#define WIN_EVENTS_QUEUE_SIZE 256 #define FLAG_WIN_QUIT 1 #define FLAG_WIN_RESIZE 2 #define FLAG_WIN_REPAINT 4 struct statestate { HINSTANCE hInstance; HWND hWnd; RECT WinRect; XINPUT_STATE Gamepads[1]; uint32_t Flags; window_event EventsQueue[WIN_EVENTS_QUEUE_SIZE]; uint64_t EventsQueueRead; uint64_t EventsQueueWrite; } *S; static window_event* WriteEvent() { window_event* ret = &S->EventsQueue[S->EventsQueueWrite]; MEM_ZERO(ret, 0); S->EventsQueueWrite = (S->EventsQueueWrite + 1) % WIN_EVENTS_QUEUE_SIZE; return ret; } static key ScanCodeToKey(uint8_t scanCode, bool extended) { if (extended) { switch (scanCode) { case 0x38: return Key_RightAlt; case 0x1D: return Key_RightCtrl; case 0x4B: return Key_ArrowLeft; case 0x4D: return Key_ArrowRight; case 0x48: return Key_ArrowUp; case 0x50: return Key_ArrowDown; case 0x47: return Key_Home; case 0x4F: return Key_End; case 0x49: return Key_PageUp; case 0x51: return Key_PageDown; case 0x52: return Key_Insert; case 0x53: return Key_Delete; default: return Key_Unknown; } } switch (scanCode) { case 0x01: return Key_Escape; case 0x29: return Key_Grave; case 0x02: return Key_1; case 0x03: return Key_2; case 0x04: return Key_3; case 0x05: return Key_4; case 0x06: return Key_5; case 0x07: return Key_6; case 0x08: return Key_7; case 0x09: return Key_8; case 0x0A: return Key_9; case 0x0B: return Key_0; case 0x0C: return Key_Minus; case 0x0D: return Key_Equal; case 0x0E: return Key_Backspace; case 0x0F: return Key_Tab; case 0x10: return Key_Q; case 0x11: return Key_W; case 0x12: return Key_E; case 0x13: return Key_R; case 0x14: return Key_T; case 0x15: return Key_Y; case 0x16: return Key_U; case 0x17: return Key_I; case 0x18: return Key_O; case 0x19: return Key_P; case 0x1A: return Key_LeftBracket; case 0x1B: return Key_RightBracket; case 0x1C: return Key_Enter; case 0x3A: return Key_CapsLock; case 0x1E: return Key_A; case 0x1F: return Key_S; case 0x20: return Key_D; case 0x21: return Key_F; case 0x22: return Key_G; case 0x23: return Key_H; case 0x24: return Key_J; case 0x25: return Key_K; case 0x26: return Key_L; case 0x27: return Key_Semicolon; case 0x28: return Key_Apostrophe; case 0x2A: return Key_LeftShift; case 0x2C: return Key_Z; case 0x2D: return Key_X; case 0x2E: return Key_C; case 0x2F: return Key_V; case 0x30: return Key_B; case 0x31: return Key_N; case 0x32: return Key_M; case 0x33: return Key_Comma; case 0x34: return Key_Period; case 0x35: return Key_Slash; case 0x36: return Key_RightShift; case 0x1D: return Key_LeftCtrl; case 0x38: return Key_LeftAlt; case 0x39: return Key_Space; case 0x3B: return Key_F1; case 0x3C: return Key_F2; case 0x3D: return Key_F3; case 0x3E: return Key_F4; case 0x3F: return Key_F5; case 0x40: return Key_F6; case 0x41: return Key_F7; case 0x42: return Key_F8; case 0x43: return Key_F9; case 0x44: return Key_F10; case 0x57: return Key_F11; case 0x58: return Key_F12; } ASSERT(false); return {}; } LRESULT MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_PAINT: { S->Flags |= FLAG_WIN_REPAINT; return 0; } case WM_CLOSE: { DestroyWindow(hWnd); return 0; } case WM_DESTROY: { S->Flags |= FLAG_WIN_QUIT; PostQuitMessage(0); return 0; } case WM_SIZE: { if (wParam != SIZE_MINIMIZED) { S->Flags |= FLAG_WIN_RESIZE; GetClientRect(S->hWnd, &S->WinRect); } return 0; } case WM_SYSKEYDOWN: case WM_KEYDOWN: { UINT scanCode = (UINT)((lParam >> 16) & 0xFF); bool extended = ((lParam >> 24) & 0x01) != 0; bool wasDown = ((lParam >> 30) & 0x01) != 0; if (!wasDown) { window_event* Event = WriteEvent(); Event->type = WindowEvent_KeyDown; Event->Key = ScanCodeToKey(scanCode, extended); } return 0; } case WM_SYSKEYUP: case WM_KEYUP: { UINT scanCode = (UINT)((lParam >> 16) & 0xFF); bool extended = ((lParam >> 24) & 0x01) != 0; window_event* Event = WriteEvent(); Event->type = WindowEvent_KeyUp; Event->Key = ScanCodeToKey(scanCode, extended); return 0; } case WM_MOUSEMOVE: { window_event* Event = WriteEvent(); Event->type = WindowEvent_MouseMove; Event->Mouse.Button = MouseButton_None; Event->Mouse.x = (short)LOWORD(lParam); Event->Mouse.y = (short)HIWORD(lParam); return 0; } case WM_LBUTTONDOWN: { window_event* Event = WriteEvent(); Event->type = WindowEvent_MouseDown; Event->Mouse.Button = MouseButton_Left; Event->Mouse.x = (short)LOWORD(lParam); Event->Mouse.y = (short)HIWORD(lParam); return 0; } case WM_LBUTTONUP: { window_event* Event = WriteEvent(); Event->type = WindowEvent_MouseUp; Event->Mouse.Button = MouseButton_Left; Event->Mouse.x = (short)LOWORD(lParam); Event->Mouse.y = (short)HIWORD(lParam); return 0; } case WM_RBUTTONDOWN: { window_event* Event = WriteEvent(); Event->type = WindowEvent_MouseDown; Event->Mouse.Button = MouseButton_Right; Event->Mouse.x = (short)LOWORD(lParam); Event->Mouse.y = (short)HIWORD(lParam); return 0; } case WM_RBUTTONUP: { window_event* Event = WriteEvent(); Event->type = WindowEvent_MouseUp; Event->Mouse.Button = MouseButton_Right; Event->Mouse.x = (short)LOWORD(lParam); Event->Mouse.y = (short)HIWORD(lParam); return 0; } case WM_MBUTTONDOWN: { window_event* Event = WriteEvent(); Event->type = WindowEvent_MouseDown; Event->Mouse.Button = MouseButton_Middle; Event->Mouse.x = (short)LOWORD(lParam); Event->Mouse.y = (short)HIWORD(lParam); return 0; } case WM_MBUTTONUP: { window_event* Event = WriteEvent(); Event->type = WindowEvent_MouseUp; Event->Mouse.Button = MouseButton_Middle; Event->Mouse.x = (short)LOWORD(lParam); Event->Mouse.y = (short)HIWORD(lParam); return 0; } default: return DefWindowProc(hWnd, uMsg, wParam, lParam); } } static void WinOpen() { wchar_t name[] = L"nyla"; WNDCLASSW windowClass = { .lpfnWndProc = MainWndProc, .hInstance = S->hInstance, .lpszClassName = name, }; RegisterClassW(&windowClass); S->hWnd = CreateWindowExW(0, name, name, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, nullptr, nullptr, S->hInstance, nullptr); ShowWindow(S->hWnd, SW_SHOW); UpdateWindow(S->hWnd); GetClientRect(S->hWnd, &S->WinRect); } static void WinGetSize(uint32_t* width, uint32_t* height) { *width = S->WinRect.right - S->WinRect.left; *height = S->WinRect.bottom - S->WinRect.top; } static bool WinPollEvent(window_event* outEvent) { MEM_ZERO(outEvent, 0); for (;;) { if (S->EventsQueueWrite != S->EventsQueueRead) break; if (S->Flags) break; MSG msg {}; if (!PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) break; if (msg.message == WM_QUIT) { S->Flags |= FLAG_WIN_QUIT; break; } TranslateMessage(&msg); DispatchMessage(&msg); } if (S->Flags & FLAG_WIN_QUIT) { outEvent->type = WindowEvent_Quit; return true; } if (S->Flags & FLAG_WIN_RESIZE) { outEvent->type = WindowEvent_Resize; S->Flags &= ~FLAG_WIN_RESIZE; return true; } if (S->Flags & FLAG_WIN_REPAINT) { PAINTSTRUCT ps { }; BeginPaint(S->hWnd, &ps); EndPaint(S->hWnd, &ps); outEvent->type = WindowEvent_Repaint; S->Flags &= ~FLAG_WIN_REPAINT; return true; } if (S->EventsQueueWrite != S->EventsQueueRead) { *outEvent = S->EventsQueue[S->EventsQueueRead]; S->EventsQueueRead = (S->EventsQueueRead + 1) % WIN_EVENTS_QUEUE_SIZE; return true; } return false; } static void Init0() { SYSTEM_INFO SysInfo; GetNativeSystemInfo(&SysInfo); ASSERT(SysInfo.dwPageSize == MEMPAGE_SIZE); } static void Init1() { S = PUSH_ALLOC(&PermanentAlloc, statestate, 1); #ifndef NDEBUG ASSERT(AllocConsole()); HANDLE hConOut = CreateFileA("CONOUT$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr); HANDLE hConIn = CreateFileA("CONIN$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr); SetStdHandle(STD_INPUT_HANDLE, hConIn); SetStdHandle(STD_OUTPUT_HANDLE, hConOut); SetStdHandle(STD_ERROR_HANDLE, hConOut); FILE* dummy; freopen_s(&dummy, "CONOUT$", "w", stdout); freopen_s(&dummy, "CONIN$", "r", stdin); freopen_s(&dummy, "CONOUT$", "w", stderr); #endif } static void TearDown() { #ifndef NDEBUG #if 0 HANDLE hConIn = CreateFileA("CONIN$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr); for (;;) { INPUT_RECORD ir; DWORD read; if (ReadConsoleInputA(hConIn, &ir, 1, &read) && read > 0) { if (ir.EventType == KEY_EVENT && ir.Event.KeyEvent.bKeyDown) break; } } #endif PostMessage(GetConsoleWindow(), WM_CLOSE, 0, 0); #endif }