Last active
October 30, 2021 07:45
-
-
Save aont/0d40aab4ddd332d4da4d8e84cf625c6e to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// EchoCon.cpp : Entry point for the EchoCon Pseudo-Console sample application. | |
// Copyright © 2018, Microsoft | |
// Modified by aont 2021/10/15 | |
// #include "stdafx.h" | |
#include <Windows.h> | |
#include <process.h> | |
#include <cstdio> | |
// Forward declarations | |
HRESULT CreatePseudoConsoleAndPipes(HPCON*, HANDLE*, HANDLE*); | |
HRESULT InitializeStartupInfoAttachedToPseudoConsole(STARTUPINFOEX*, HPCON); | |
void __cdecl PipeListener(LPVOID); | |
int main() | |
{ | |
wchar_t szCommand[]{ L"C:\\msys64\\usr\\bin\\bash.exe --login -i" }; | |
HRESULT hr{ E_UNEXPECTED }; | |
HANDLE hConsole = { GetStdHandle(STD_OUTPUT_HANDLE) }; | |
HANDLE hin = GetStdHandle(STD_INPUT_HANDLE); | |
// Enable Console VT Processing | |
DWORD consoleMode{}; | |
GetConsoleMode(hin, &consoleMode); | |
hr = SetConsoleMode(hin, consoleMode ^ ENABLE_PROCESSED_INPUT ^ ENABLE_LINE_INPUT) | |
? S_OK | |
: GetLastError(); | |
GetConsoleMode(hConsole, &consoleMode); | |
hr = SetConsoleMode(hConsole, consoleMode | ENABLE_VIRTUAL_TERMINAL_PROCESSING) | |
? S_OK | |
: GetLastError(); | |
if (S_OK == hr) | |
{ | |
HPCON hPC{ INVALID_HANDLE_VALUE }; | |
// Create the Pseudo Console and pipes to it | |
HANDLE hPipeIn{ INVALID_HANDLE_VALUE }; | |
HANDLE hPipeOut{ INVALID_HANDLE_VALUE }; | |
hr = CreatePseudoConsoleAndPipes(&hPC, &hPipeIn, &hPipeOut); | |
if (S_OK == hr) | |
{ | |
// Create & start thread to listen to the incoming pipe | |
// Note: Using CRT-safe _beginthread() rather than CreateThread() | |
HANDLE hPipeListenerThread{ reinterpret_cast<HANDLE>(_beginthread(PipeListener, 0, hPipeIn)) }; | |
// Initialize the necessary startup info struct | |
STARTUPINFOEX startupInfo{}; | |
if (S_OK == InitializeStartupInfoAttachedToPseudoConsole(&startupInfo, hPC)) | |
{ | |
// Launch ping to emit some text back via the pipe | |
PROCESS_INFORMATION piClient{}; | |
hr = CreateProcess( | |
NULL, // No module name - use Command Line | |
szCommand, // Command Line | |
NULL, // Process handle not inheritable | |
NULL, // Thread handle not inheritable | |
FALSE, // Inherit handles | |
EXTENDED_STARTUPINFO_PRESENT, // Creation flags | |
NULL, // Use parent's environment block | |
NULL, // Use parent's starting directory | |
&startupInfo.StartupInfo, // Pointer to STARTUPINFO | |
&piClient) // Pointer to PROCESS_INFORMATION | |
? S_OK | |
: GetLastError(); | |
char input_key; | |
DWORD num_events; | |
while (true) { | |
bool ret; | |
ret = ReadFile(hin, &input_key, 1, &num_events, NULL); | |
if (ret) { | |
fprintf(stderr, "input: 0x%x\n", input_key); | |
WriteFile(hPipeOut, &input_key, 1, &num_events, NULL); | |
} | |
}; | |
if (S_OK == hr) | |
{ | |
// Wait up to 10s for ping process to complete | |
WaitForSingleObject(piClient.hThread, 10 * 1000); | |
// Allow listening thread to catch-up with final output! | |
Sleep(500); | |
} | |
// --- CLOSEDOWN --- | |
// Now safe to clean-up client app's process-info & thread | |
CloseHandle(piClient.hThread); | |
CloseHandle(piClient.hProcess); | |
// Cleanup attribute list | |
DeleteProcThreadAttributeList(startupInfo.lpAttributeList); | |
free(startupInfo.lpAttributeList); | |
} | |
// Close ConPTY - this will terminate client process if running | |
ClosePseudoConsole(hPC); | |
// Clean-up the pipes | |
if (INVALID_HANDLE_VALUE != hPipeOut) CloseHandle(hPipeOut); | |
if (INVALID_HANDLE_VALUE != hPipeIn) CloseHandle(hPipeIn); | |
} | |
} | |
return S_OK == hr ? EXIT_SUCCESS : EXIT_FAILURE; | |
} | |
HRESULT CreatePseudoConsoleAndPipes(HPCON* phPC, HANDLE* phPipeIn, HANDLE* phPipeOut) | |
{ | |
HRESULT hr{ E_UNEXPECTED }; | |
HANDLE hPipePTYIn{ INVALID_HANDLE_VALUE }; | |
HANDLE hPipePTYOut{ INVALID_HANDLE_VALUE }; | |
// Create the pipes to which the ConPTY will connect | |
if (CreatePipe(&hPipePTYIn, phPipeOut, NULL, 0) && | |
CreatePipe(phPipeIn, &hPipePTYOut, NULL, 0)) | |
{ | |
// Determine required size of Pseudo Console | |
COORD consoleSize{}; | |
CONSOLE_SCREEN_BUFFER_INFO csbi{}; | |
HANDLE hConsole{ GetStdHandle(STD_OUTPUT_HANDLE) }; | |
if (GetConsoleScreenBufferInfo(hConsole, &csbi)) | |
{ | |
consoleSize.X = csbi.srWindow.Right - csbi.srWindow.Left + 1; | |
consoleSize.Y = csbi.srWindow.Bottom - csbi.srWindow.Top + 1; | |
} | |
// Create the Pseudo Console of the required size, attached to the PTY-end of the pipes | |
hr = CreatePseudoConsole(consoleSize, hPipePTYIn, hPipePTYOut, 0, phPC); | |
// Note: We can close the handles to the PTY-end of the pipes here | |
// because the handles are dup'ed into the ConHost and will be released | |
// when the ConPTY is destroyed. | |
if (INVALID_HANDLE_VALUE != hPipePTYOut) CloseHandle(hPipePTYOut); | |
if (INVALID_HANDLE_VALUE != hPipePTYIn) CloseHandle(hPipePTYIn); | |
} | |
return hr; | |
} | |
// Initializes the specified startup info struct with the required properties and | |
// updates its thread attribute list with the specified ConPTY handle | |
HRESULT InitializeStartupInfoAttachedToPseudoConsole(STARTUPINFOEX* pStartupInfo, HPCON hPC) | |
{ | |
HRESULT hr{ E_UNEXPECTED }; | |
if (pStartupInfo) | |
{ | |
SIZE_T attrListSize{}; | |
pStartupInfo->StartupInfo.cb = sizeof(STARTUPINFOEX); | |
// Get the size of the thread attribute list. | |
InitializeProcThreadAttributeList(NULL, 1, 0, &attrListSize); | |
// Allocate a thread attribute list of the correct size | |
pStartupInfo->lpAttributeList = | |
reinterpret_cast<LPPROC_THREAD_ATTRIBUTE_LIST>(malloc(attrListSize)); | |
// Initialize thread attribute list | |
if (pStartupInfo->lpAttributeList | |
&& InitializeProcThreadAttributeList(pStartupInfo->lpAttributeList, 1, 0, &attrListSize)) | |
{ | |
// Set Pseudo Console attribute | |
hr = UpdateProcThreadAttribute( | |
pStartupInfo->lpAttributeList, | |
0, | |
PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE, | |
hPC, | |
sizeof(HPCON), | |
NULL, | |
NULL) | |
? S_OK | |
: HRESULT_FROM_WIN32(GetLastError()); | |
} | |
else | |
{ | |
hr = HRESULT_FROM_WIN32(GetLastError()); | |
} | |
} | |
return hr; | |
} | |
void __cdecl PipeListener(LPVOID pipe) | |
{ | |
HANDLE hPipe{ pipe }; | |
HANDLE hConsole{ GetStdHandle(STD_OUTPUT_HANDLE) }; | |
const DWORD BUFF_SIZE{ 512 }; | |
char szBuffer[BUFF_SIZE]{}; | |
DWORD dwBytesWritten{}; | |
DWORD dwBytesRead{}; | |
BOOL fRead{ FALSE }; | |
do | |
{ | |
// Read from the pipe | |
fRead = ReadFile(hPipe, szBuffer, BUFF_SIZE, &dwBytesRead, NULL); | |
// Write received text to the Console | |
// Note: Write to the Console using WriteFile(hConsole...), not printf()/puts() to | |
// prevent partially-read VT sequences from corrupting output | |
WriteFile(hConsole, szBuffer, dwBytesRead, &dwBytesWritten, NULL); | |
} while (fRead && dwBytesRead >= 0); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// original: https://blog.goo.ne.jp/lm324/e/16629a8aadaa0de77fc05611390cf15b | |
// modified by aont 2021/10/15 | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <termios.h> | |
#include <unistd.h> | |
#include <signal.h> | |
#include <fcntl.h> | |
int getkey(void) | |
{ | |
struct termios oldt, newt; | |
int ch0,ch1,ch2,ch3,ch4; | |
int ret; | |
tcgetattr(STDIN_FILENO, &oldt); | |
newt = oldt; | |
newt.c_iflag = ~( BRKINT | ISTRIP | IXON ); | |
newt.c_lflag = ~( ICANON | IEXTEN | ECHO | ECHOE | ECHOK | ECHONL ); | |
newt.c_cc[VTIME] = 0; | |
newt.c_cc[VMIN] = 1; | |
newt.c_cc[VINTR] = 1; | |
if(tcsetattr(STDIN_FILENO, TCSANOW, &newt)==-1) { | |
fprintf(stderr,"error tcsetattr\n"); | |
exit(EXIT_FAILURE); | |
} | |
ch0 = getchar(); | |
if(ch0==0x1B) { | |
ch1 = getchar(); | |
ch2 = getchar(); | |
if(ch2==0x32) { | |
ch3 = getchar(); | |
if(ch3==0x7e) { | |
ret = (ch0<<24) | (ch1<<16) | (ch2<<8) | ch3; | |
} else { | |
ch4 = getchar(); | |
ret = (ch1<<24) | (ch2<<16) | (ch3<<8) | ch4; | |
} | |
} else if(ch2==0x31) { | |
ch3 = getchar(); | |
ch4 = getchar(); | |
ret = (ch1<<24) | (ch2<<16) | (ch3<<8) | ch4; | |
} else if((ch2==0x33)||(ch2==0x35)||(ch2==0x36)) { | |
ch3 = getchar(); | |
ret = (ch0<<24) | (ch1<<16) | (ch2<<8) | ch3; | |
} else { | |
ret = (ch0<<16) | (ch1<<8) | ch2; | |
} | |
} else if(ch0 != EOF) { | |
ret = ch0; | |
} else { | |
ret = 0; | |
} | |
if(tcsetattr(STDIN_FILENO, TCSANOW, &oldt)==-1) { | |
fprintf(stderr,"error tcsetattr\n"); | |
exit(EXIT_FAILURE); | |
} | |
return ret; | |
} | |
int main(int argc,char *argv[]) | |
{ | |
int key; | |
while (1) { | |
key = getkey(); | |
if (key!=0) { | |
printf("key code 0x%x\n",key); | |
}else{ | |
; | |
usleep(1000); | |
} | |
if(key==0x71 /* q */ ) { | |
break; | |
} | |
} | |
return 0; | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <setjmp.h> | |
#include <stdio.h> | |
#include <signal.h> | |
#include <readline/readline.h> | |
sigjmp_buf ctrlc_buf; | |
void handle_signals(int signo) { | |
if (signo == SIGINT) { | |
siglongjmp(ctrlc_buf, 1); | |
} | |
} | |
int main(int argc, char **argv) | |
{ | |
char * input; | |
if (signal(SIGINT, handle_signals) == SIG_ERR) { | |
fprintf(stderr, "installing signal handler failed\n"); | |
} | |
char buf[128]; | |
while (1) | |
{ | |
while ( sigsetjmp( ctrlc_buf, 1 ) != 0 ) { | |
fprintf(stderr, "Ctrl+C\n"); | |
} | |
input = readline("> "); | |
if (!input) | |
break; | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment