Last active
December 19, 2015 11:09
-
-
Save rossy/5946000 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
| // i686-w64-mingw32-gcc -std=c99 -Wall -municode waittest.c -o waittest.exe | |
| #include <windows.h> | |
| #include <windowsx.h> | |
| #include <stdbool.h> | |
| #include <stdlib.h> | |
| #include <stdio.h> | |
| struct pump { | |
| char* buf; | |
| DWORD size; | |
| HANDLE in; | |
| HANDLE has_data; | |
| HANDLE read_finished; | |
| }; | |
| DWORD CALLBACK pump_thread(LPVOID lpThreadParameter) | |
| { | |
| struct pump* p = (struct pump*)lpThreadParameter; | |
| for (;;) { | |
| if (!ReadFile(p->in, p->buf, 4096, &p->size, NULL)) break; | |
| // Signal to the main thread that new data is available | |
| SetEvent(p->has_data); | |
| // Wait for it to read | |
| WaitForSingleObject(p->read_finished, INFINITE); | |
| ResetEvent(p->read_finished); | |
| } | |
| /* These should probably be closed in a thread safe way | |
| CloseHandle(p->has_data); | |
| CloseHandle(p->read_finished); | |
| HeapFree(GetProcessHeap(), 0, p->buf); | |
| HeapFree(GetProcessHeap(), 0, p); | |
| */ | |
| return 0; | |
| } | |
| struct pump* pump_create(HANDLE pipe) | |
| { | |
| struct pump* p = HeapAlloc(GetProcessHeap(), 0, sizeof(struct pump)); | |
| p->in = pipe; | |
| p->has_data = CreateEventW(NULL, FALSE, FALSE, NULL); | |
| p->read_finished = CreateEventW(NULL, FALSE, FALSE, NULL); | |
| p->buf = HeapAlloc(GetProcessHeap(), 0, 4096); | |
| CloseHandle(CreateThread(NULL, 0, pump_thread, (void*)p, 0, NULL)); | |
| return p; | |
| } | |
| bool is_console(HANDLE h) | |
| { | |
| // isatty for Windows | |
| DWORD mode; | |
| return GetConsoleMode(h, &mode); | |
| } | |
| void process_console(HANDLE console) | |
| { | |
| DWORD events; | |
| GetNumberOfConsoleInputEvents(console, &events); | |
| INPUT_RECORD* records = calloc(sizeof(INPUT_RECORD), events); | |
| ReadConsoleInputW(console, records, events, &events); | |
| for (unsigned i = 0; i < events; i ++) | |
| switch (records[i].EventType) { | |
| case KEY_EVENT: | |
| if (records[i].Event.KeyEvent.bKeyDown) | |
| printf("keydown: %u \n", | |
| records[i].Event.KeyEvent.wVirtualKeyCode); | |
| else | |
| printf("keyup: %u \n", | |
| records[i].Event.KeyEvent.wVirtualKeyCode); | |
| break; | |
| case WINDOW_BUFFER_SIZE_EVENT: | |
| printf("window: %ux%u \n", | |
| records[i].Event.WindowBufferSizeEvent.dwSize.X, | |
| records[i].Event.WindowBufferSizeEvent.dwSize.Y); | |
| break; | |
| } | |
| } | |
| void process_pipe(struct pump* p) | |
| { | |
| ResetEvent(p->has_data); | |
| printf("pipe: "); | |
| fwrite(p->buf, p->size, 1, stdout); | |
| // Signal to the pump thread that it can write to p->buf again | |
| SetEvent(p->read_finished); | |
| } | |
| bool process_messages(int* ret) | |
| { | |
| MSG message; | |
| // It's important to read all available messages before returning. | |
| // MsgWaitForMultipleObjects is only signalled once, even if multiple | |
| // messages are recieved at the same time. | |
| // See: http://blogs.msdn.com/b/oldnewthing/archive/2005/02/17/375307.aspx | |
| while (PeekMessage(&message, NULL, 0, 0, PM_REMOVE)) { | |
| if (message.message == WM_QUIT) { | |
| *ret = message.wParam; | |
| return false; | |
| } | |
| TranslateMessage(&message); | |
| DispatchMessageW(&message); | |
| } | |
| return true; | |
| } | |
| void on_timeout() | |
| { | |
| static long timeouts = 0; | |
| printf("timeout: %lu \r", timeouts++); | |
| } | |
| LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) | |
| { | |
| switch (uMsg) { | |
| case WM_CLOSE: | |
| DestroyWindow(hwnd); | |
| return 0; | |
| case WM_DESTROY: | |
| PostQuitMessage(0); | |
| return 0; | |
| case WM_LBUTTONDOWN: | |
| printf("mouse: %ux%u \n", | |
| GET_X_LPARAM(lParam), | |
| GET_Y_LPARAM(lParam)); | |
| return 0; | |
| } | |
| return DefWindowProcW(hwnd, uMsg, wParam, lParam); | |
| } | |
| int CALLBACK wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, | |
| LPWSTR lpCmdLine, int nCmdShow) | |
| { | |
| ATOM class = RegisterClassExW(&(WNDCLASSEXW) { | |
| .cbSize = sizeof(WNDCLASSEXW), | |
| .lpfnWndProc = WindowProc, | |
| .hInstance = hInstance, | |
| .hCursor = LoadCursor(NULL, IDC_ARROW), | |
| .hbrBackground = (HBRUSH)(COLOR_WINDOW + 1), | |
| .lpszClassName = L"waittest", | |
| }); | |
| HWND window = CreateWindowExW( | |
| WS_EX_APPWINDOW, | |
| (LPWSTR)MAKEINTATOM(class), | |
| L"wait test", | |
| WS_CAPTION | WS_POPUPWINDOW, | |
| 200, 200, 400, 300, | |
| NULL, NULL, hInstance, NULL); | |
| ShowWindow(window, SW_SHOWDEFAULT); | |
| UpdateWindow(window); | |
| HANDLE wstdin = GetStdHandle(STD_INPUT_HANDLE); | |
| bool console = is_console(wstdin); | |
| struct pump* p = NULL; | |
| if (console) { | |
| DWORD old; | |
| // Set console to raw mode | |
| GetConsoleMode(wstdin, &old); | |
| SetConsoleMode(wstdin, | |
| (old & ~(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT)) | | |
| ENABLE_WINDOW_INPUT); | |
| } else { | |
| // MsgWaitForMultipleObjects won't wait on a pipe. Create a thread to | |
| // watch stdin that will signal the main thread when there is data. | |
| p = pump_create(wstdin); | |
| // wstdin is now an event object | |
| wstdin = p->has_data; | |
| } | |
| HANDLE* h = &(HANDLE){ wstdin }; | |
| for (;;) | |
| switch (MsgWaitForMultipleObjectsEx(1, h, 100, QS_ALLINPUT, 0)) { | |
| case WAIT_OBJECT_0: | |
| if (console) | |
| process_console(wstdin); | |
| else | |
| process_pipe(p); | |
| break; | |
| case WAIT_OBJECT_0 + 1: { | |
| int ret; | |
| if (!process_messages(&ret)) | |
| return ret; | |
| break; | |
| } | |
| case WAIT_TIMEOUT: | |
| // Only fires if there has been no other events in the last | |
| // 100ms | |
| on_timeout(); | |
| break; | |
| case WAIT_FAILED: | |
| puts(";_;"); | |
| return 255; | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment