Skip to content

Instantly share code, notes, and snippets.

@rossy
Last active December 19, 2015 11:09
Show Gist options
  • Select an option

  • Save rossy/5946000 to your computer and use it in GitHub Desktop.

Select an option

Save rossy/5946000 to your computer and use it in GitHub Desktop.
// 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