diff options
Diffstat (limited to 'Peak/src/p_win32.c')
| -rw-r--r-- | Peak/src/p_win32.c | 399 |
1 files changed, 391 insertions, 8 deletions
diff --git a/Peak/src/p_win32.c b/Peak/src/p_win32.c index 16fab7f..c1a289b 100644 --- a/Peak/src/p_win32.c +++ b/Peak/src/p_win32.c @@ -1,21 +1,404 @@ +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif + #include <windows.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define PEAK_WIN32_USER32 "user32.dll" +#define PEAK_WIN32_GDI32 "gdi32.dll" +#define PEAK_WIN32_CLASS "PeakWindow" +#define PEAK_WIN32_PROP "Peak" + +#define PEAK_USER32_API(X) \ + X(RegisterClassExA, ATOM, WINAPI, (const WNDCLASSEXA *)) \ + X(UnregisterClassA, BOOL, WINAPI, (LPCSTR, HINSTANCE)) \ + X(CreateWindowExA, HWND, WINAPI, (DWORD, LPCSTR, LPCSTR, DWORD, int, int, int, int, HWND, HMENU, HINSTANCE, LPVOID)) \ + X(DestroyWindow, BOOL, WINAPI, (HWND)) \ + X(ShowWindow, BOOL, WINAPI, (HWND, int)) \ + X(GetDC, HDC, WINAPI, (HWND)) \ + X(ReleaseDC, int, WINAPI, (HWND, HDC)) \ + X(PeekMessageA, BOOL, WINAPI, (LPMSG, HWND, UINT, UINT, UINT)) \ + X(TranslateMessage, BOOL, WINAPI, (const MSG *)) \ + X(DispatchMessageA, LRESULT, WINAPI, (const MSG *)) \ + X(DefWindowProcA, LRESULT, WINAPI, (HWND, UINT, WPARAM, LPARAM)) \ + X(SetPropA, BOOL, WINAPI, (HWND, LPCSTR, HANDLE)) \ + X(GetPropA, HANDLE, WINAPI, (HWND, LPCSTR)) \ + X(RemovePropA, HANDLE, WINAPI, (HWND, LPCSTR)) \ + X(BeginPaint, HDC, WINAPI, (HWND, LPPAINTSTRUCT)) \ + X(EndPaint, BOOL, WINAPI, (HWND, const PAINTSTRUCT *)) \ + X(AdjustWindowRectEx, BOOL, WINAPI, (LPRECT, DWORD, BOOL, DWORD)) \ + X(LoadCursorA, HCURSOR, WINAPI, (HINSTANCE, LPCSTR)) \ + X(GetKeyState, SHORT, WINAPI, (int)) + +#define PEAK_GDI32_API(X) \ + X(StretchDIBits, int, WINAPI, (HDC, int, int, int, int, int, int, int, int, const void *, const BITMAPINFO *, UINT, DWORD)) + +typedef struct { +#define X(name, ret, conv, args) ret (conv *name) args; + PEAK_USER32_API(X) +#undef X +} PeakUser32Api; + +typedef struct { +#define X(name, ret, conv, args) ret (conv *name) args; + PEAK_GDI32_API(X) +#undef X +} PeakGdi32Api; + +typedef struct { + HMODULE user32; + HMODULE gdi32; + int class_reg; +} PeakWin32; + +struct peak_win32_win { + HWND hwnd; + HDC hdc; + uint32_t *buffer; + uint32_t width; + uint32_t height; + PeakQ q; +}; + +struct peak_window_internal_t { + struct peak_win32_win *w; +}; + +static PeakWin32 peak_win32; +static PeakUser32Api peak_user32; +static PeakGdi32Api peak_gdi32; + +static int +peak_internal_user32_load(HMODULE handle) +{ +#define X(name, ret, conv, args) peak_user32.name = (ret (conv *) args)(void *)GetProcAddress(handle, #name); + PEAK_USER32_API(X) +#undef X +#define X(name, ret, conv, args) || !peak_user32.name + if (0 PEAK_USER32_API(X)) + return 0; +#undef X + return 1; +} -void -peak_platform_window_open() +static int +peak_internal_gdi32_load(HMODULE handle) { +#define X(name, ret, conv, args) peak_gdi32.name = (ret (conv *) args)(void *)GetProcAddress(handle, #name); + PEAK_GDI32_API(X) +#undef X +#define X(name, ret, conv, args) || !peak_gdi32.name + if (0 PEAK_GDI32_API(X)) + return 0; +#undef X + return 1; } -void -peak_platform_window_close() +static PeakKeyCode +peak_internal_win32_key_map(WPARAM vk) { + if (vk >= 'A' && vk <= 'Z') + return (PeakKeyCode)(PEAK_KEY_A + (int)(vk - 'A')); + switch (vk) { + case VK_UP: return PEAK_KEY_UP; + case VK_DOWN: return PEAK_KEY_DOWN; + case VK_LEFT: return PEAK_KEY_LEFT; + case VK_RIGHT: return PEAK_KEY_RIGHT; + case VK_SPACE: return PEAK_KEY_SPACE; + case VK_ESCAPE: return PEAK_KEY_ESCAPE; + case VK_RETURN: return PEAK_KEY_ENTER; + default: return PEAK_KEY_UNKNOWN; + } } -uint32_t* -peak_platform_window_buffer(size_t *width, size_t *height) +static PeakKeyMod +peak_internal_win32_mod_map(void) { + if (peak_user32.GetKeyState(VK_CONTROL) & 0x8000) return PEAK_KEYMOD_CTRL; + if (peak_user32.GetKeyState(VK_MENU) & 0x8000) return PEAK_KEYMOD_ALT; + if (peak_user32.GetKeyState(VK_SHIFT) & 0x8000) return PEAK_KEYMOD_SHIFT; + if (peak_user32.GetKeyState(VK_CAPITAL) & 1) return PEAK_KEYMOD_CAPS; + return (PeakKeyMod)0; } -bool -peak_platform_epoll(PeakEvent *ev) +static int +peak_win32_buffer(struct peak_win32_win *w, uint32_t width, uint32_t height) { + uint32_t *buffer; + + buffer = calloc((size_t)width * height, sizeof *buffer); + if (!buffer) + return 0; + free(w->buffer); + w->buffer = buffer; + w->width = width; + w->height = height; + return 1; +} + +static LRESULT CALLBACK +peak_internal_win32_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + struct peak_win32_win *w; + PeakEvent ev; + + if (msg == WM_NCCREATE) { + CREATESTRUCTA *cs = (CREATESTRUCTA *)lparam; + peak_user32.SetPropA(hwnd, PEAK_WIN32_PROP, cs->lpCreateParams); + } + + w = (struct peak_win32_win *)peak_user32.GetPropA(hwnd, PEAK_WIN32_PROP); + if (!w) + return peak_user32.DefWindowProcA(hwnd, msg, wparam, lparam); + + switch (msg) { + case WM_CLOSE: + memset(&ev, 0, sizeof ev); + ev.type = PEAK_EVENT_WINDOW_CLOSE; + peak_q_push(&w->q, ev); + return 0; + case WM_SIZE: { + uint32_t width, height; + if (wparam == SIZE_MINIMIZED) + return 0; + width = (uint32_t)LOWORD(lparam); + height = (uint32_t)HIWORD(lparam); + if (!width || !height || (width == w->width && height == w->height)) + return 0; + if (!peak_win32_buffer(w, width, height)) + return 0; + memset(&ev, 0, sizeof ev); + ev.type = PEAK_EVENT_WINDOW_RESIZE; + ev.resize.width = w->width; + ev.resize.height = w->height; + peak_q_push(&w->q, ev); + return 0; + } + case WM_KEYDOWN: + case WM_KEYUP: + case WM_SYSKEYDOWN: + case WM_SYSKEYUP: + memset(&ev, 0, sizeof ev); + ev.type = (msg == WM_KEYDOWN || msg == WM_SYSKEYDOWN) ? PEAK_EVENT_KEY_DOWN : PEAK_EVENT_KEY_UP; + ev.key.key = peak_internal_win32_key_map(wparam); + ev.key.mod = peak_internal_win32_mod_map(); + peak_q_push(&w->q, ev); + return 0; + case WM_MOUSEMOVE: + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + case WM_RBUTTONDOWN: + case WM_RBUTTONUP: + case WM_MBUTTONDOWN: + case WM_MBUTTONUP: + memset(&ev, 0, sizeof ev); + ev.type = PEAK_EVENT_POINTER; + ev.pointer.x = (float)(short)LOWORD(lparam); + ev.pointer.y = (float)(short)HIWORD(lparam); + if (msg == WM_MOUSEMOVE) { + ev.pointer.state = PEAK_POINTER_MOVED; + ev.pointer.type = (wparam & MK_RBUTTON) ? PEAK_POINTER_RIGHT : + (wparam & MK_MBUTTON) ? PEAK_POINTER_MIDDLE : PEAK_POINTER_LEFT; + } else if (msg == WM_LBUTTONDOWN || msg == WM_RBUTTONDOWN || msg == WM_MBUTTONDOWN) { + ev.pointer.state = PEAK_POINTER_PRESSED; + ev.pointer.type = (msg == WM_RBUTTONDOWN) ? PEAK_POINTER_RIGHT : + (msg == WM_MBUTTONDOWN) ? PEAK_POINTER_MIDDLE : PEAK_POINTER_LEFT; + } else { + ev.pointer.state = PEAK_POINTER_RELEASED; + ev.pointer.type = (msg == WM_RBUTTONUP) ? PEAK_POINTER_RIGHT : + (msg == WM_MBUTTONUP) ? PEAK_POINTER_MIDDLE : PEAK_POINTER_LEFT; + } + peak_q_push(&w->q, ev); + return 0; + case WM_PAINT: { + PAINTSTRUCT ps; + peak_user32.BeginPaint(hwnd, &ps); + peak_user32.EndPaint(hwnd, &ps); + return 0; + } + case WM_ERASEBKGND: + return 1; + case WM_DESTROY: + peak_user32.RemovePropA(hwnd, PEAK_WIN32_PROP); + return 0; + default: + return peak_user32.DefWindowProcA(hwnd, msg, wparam, lparam); + } +} + +static int +peak_platform_init(void) +{ + WNDCLASSEXA wc; + + if (!peak_user32.CreateWindowExA) { + if (!(peak_win32.user32 = LoadLibraryA(PEAK_WIN32_USER32))) { + fputs("Failed to load user32.dll. What system are you fucking using and abusing?", stderr); + return 0; + } + if (!peak_internal_user32_load(peak_win32.user32)) { + fputs("Failed to load user32 symbols", stderr); + return 0; + } + } + if (!peak_gdi32.StretchDIBits) { + if (!(peak_win32.gdi32 = LoadLibraryA(PEAK_WIN32_GDI32))) { + fputs("Failed to load gdi32.dll. What system are you fucking using and abusing?", stderr); + return 0; + } + if (!peak_internal_gdi32_load(peak_win32.gdi32)) { + fputs("Failed to load gdi32 symbols", stderr); + return 0; + } + } + if (!peak_win32.class_reg) { + memset(&wc, 0, sizeof wc); + wc.cbSize = sizeof wc; + wc.style = CS_OWNDC; + wc.lpfnWndProc = peak_internal_win32_wndproc; + wc.hInstance = GetModuleHandleA(NULL); + wc.hCursor = peak_user32.LoadCursorA(NULL, IDC_ARROW); + wc.lpszClassName = PEAK_WIN32_CLASS; + if (!peak_user32.RegisterClassExA(&wc) && GetLastError() != ERROR_CLASS_ALREADY_EXISTS) { + fputs("Failed to register window class", stderr); + return 0; + } + peak_win32.class_reg = 1; + } + return 1; +} + +static void +peak_platform_quit(void) +{ + if (!peak_win32.class_reg) + return; + peak_user32.UnregisterClassA(PEAK_WIN32_CLASS, GetModuleHandleA(NULL)); + peak_win32.class_reg = 0; +} + +static PeakWindowInternal +peak_platform_window_open(const char *name, uint32_t width, uint32_t height, uint32_t flags) +{ + PeakWindowInternal intern = {0}; + struct peak_win32_win *w; + DWORD style, ex; + RECT r; + HINSTANCE inst; + + (void)flags; + if (!peak_user32.CreateWindowExA && !peak_platform_init()) + return intern; + + w = calloc(1, sizeof *w); + if (!w) + return intern; + if (!peak_win32_buffer(w, width, height)) { + free(w); + return intern; + } + + ex = 0; + style = WS_OVERLAPPEDWINDOW; + r.left = 0; + r.top = 0; + r.right = (LONG)width; + r.bottom = (LONG)height; + peak_user32.AdjustWindowRectEx(&r, style, FALSE, ex); + inst = GetModuleHandleA(NULL); + w->hwnd = peak_user32.CreateWindowExA(ex, PEAK_WIN32_CLASS, name, style, + CW_USEDEFAULT, CW_USEDEFAULT, r.right - r.left, r.bottom - r.top, + NULL, NULL, inst, w); + if (!w->hwnd) { + free(w->buffer); + free(w); + return intern; + } + + w->hdc = peak_user32.GetDC(w->hwnd); + if (!w->hdc) { + peak_user32.DestroyWindow(w->hwnd); + free(w->buffer); + free(w); + return intern; + } + + peak_user32.ShowWindow(w->hwnd, SW_SHOWNORMAL); + intern.w = w; + return intern; +} + +static void +peak_platform_window_close(PeakWindowInternal *intern) +{ + struct peak_win32_win *w; + if (!intern || !intern->w) + return; + w = intern->w; + if (w->hdc && w->hwnd) + peak_user32.ReleaseDC(w->hwnd, w->hdc); + if (w->hwnd) { + peak_user32.RemovePropA(w->hwnd, PEAK_WIN32_PROP); + peak_user32.DestroyWindow(w->hwnd); + } + free(w->buffer); + free(w); + intern->w = NULL; +} + +static uint32_t * +peak_platform_window_buffer(PeakWindowInternal *intern, size_t *width, size_t *height) +{ + struct peak_win32_win *w = intern ? intern->w : NULL; + if (!w) { + *width = 0; + *height = 0; + return NULL; + } + *width = w->width; + *height = w->height; + return w->buffer; +} + +static void +peak_platform_window_present(PeakWindowInternal *intern) +{ + struct peak_win32_win *w = intern ? intern->w : NULL; + BITMAPINFO bmi; + + if (!w || !w->hwnd || !w->hdc || !w->buffer) + return; + + memset(&bmi, 0, sizeof bmi); + bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bmi.bmiHeader.biWidth = (LONG)w->width; + bmi.bmiHeader.biHeight = -(LONG)w->height; + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = 32; + bmi.bmiHeader.biCompression = BI_RGB; + peak_gdi32.StretchDIBits(w->hdc, + 0, 0, (int)w->width, (int)w->height, + 0, 0, (int)w->width, (int)w->height, + w->buffer, &bmi, DIB_RGB_COLORS, SRCCOPY); +} + +static bool +peak_platform_epoll(PeakWindowInternal *intern, PeakEvent *ev) +{ + MSG msg; + struct peak_win32_win *w = intern ? intern->w : NULL; + + if (!w || !w->hwnd) + return 0; + + while (peak_user32.PeekMessageA(&msg, w->hwnd, 0, 0, PM_REMOVE)) { + peak_user32.TranslateMessage(&msg); + peak_user32.DispatchMessageA(&msg); + if (peak_q_pop(&w->q, ev)) + return 1; + } + return peak_q_pop(&w->q, ev); } |
