From ad5248a3e1bafc1b7dec4c8af82d56ed324ef314 Mon Sep 17 00:00:00 2001 From: Vasco Date: Mon, 24 Aug 2026 03:53:16 +0100 Subject: Slimed out podium. Updated math. Rend kinda works but still bugged. --- Peak/src/p_win32.c | 391 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 332 insertions(+), 59 deletions(-) (limited to 'Peak/src/p_win32.c') diff --git a/Peak/src/p_win32.c b/Peak/src/p_win32.c index c1a289b..cf99b6c 100644 --- a/Peak/src/p_win32.c +++ b/Peak/src/p_win32.c @@ -1,40 +1,65 @@ +/* + * Win32 window, input, StretchDIBits present, and waveOut audio. + * user32.dll, gdi32.dll, and winmm.dll are loaded at runtime. + * + * * 0.4.0 - @vasco - win32 + * * 0.5.0 - @vasco - audio start/stop, s16le pull callback + */ + #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif +/* windows.h must precede mmsystem.h */ #include +#include #include #include #include +#ifdef PEAK_VULKAN +#define VK_USE_PLATFORM_WIN32_KHR +#include +#endif #define PEAK_WIN32_USER32 "user32.dll" #define PEAK_WIN32_GDI32 "gdi32.dll" +#define PEAK_WIN32_WINMM "winmm.dll" #define PEAK_WIN32_CLASS "PeakWindow" #define PEAK_WIN32_PROP "Peak" +#define PEAK_AUDIO_FRAMES 256 +#define PEAK_AUDIO_BUFFERS 2 #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)) + 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)) + X(StretchDIBits, int, WINAPI, (HDC, int, int, int, int, int, int, int, int, const void *, const BITMAPINFO *, UINT, DWORD)) + +#define PEAK_WINMM_API(X) \ + X(waveOutOpen, MMRESULT, WINAPI, (LPHWAVEOUT, UINT, LPCWAVEFORMATEX, DWORD_PTR, DWORD_PTR, DWORD)) \ + X(waveOutClose, MMRESULT, WINAPI, (HWAVEOUT)) \ + X(waveOutPrepareHeader, MMRESULT, WINAPI, (HWAVEOUT, LPWAVEHDR, UINT)) \ + X(waveOutUnprepareHeader, MMRESULT, WINAPI, (HWAVEOUT, LPWAVEHDR, UINT)) \ + X(waveOutWrite, MMRESULT, WINAPI, (HWAVEOUT, LPWAVEHDR, UINT)) \ + X(waveOutReset, MMRESULT, WINAPI, (HWAVEOUT)) typedef struct { #define X(name, ret, conv, args) ret (conv *name) args; @@ -48,6 +73,25 @@ typedef struct { #undef X } PeakGdi32Api; +typedef struct { +#define X(name, ret, conv, args) ret (conv *name) args; + PEAK_WINMM_API(X) +#undef X +} PeakWinmmApi; + +typedef struct { + volatile int run; + HANDLE thread; + HANDLE event; + HWAVEOUT out; + WAVEHDR hdr[PEAK_AUDIO_BUFFERS]; + int16_t *pcm[PEAK_AUDIO_BUFFERS]; + uint32_t channels; + uint32_t bytes; + void (*fill)(int16_t *out, size_t frames, void *userdata); + void *userdata; +} PeakAudio; + typedef struct { HMODULE user32; HMODULE gdi32; @@ -67,9 +111,28 @@ struct peak_window_internal_t { struct peak_win32_win *w; }; +static int peak_internal_user32_load(HMODULE handle); +static int peak_internal_gdi32_load(HMODULE handle); +static PeakKeyCode peak_internal_win32_key_map(WPARAM vk); +static PeakKeyMod peak_internal_win32_mod_map(void); +static int peak_internal_win32_buffer(struct peak_win32_win *w, uint32_t width, uint32_t height); +static LRESULT CALLBACK peak_internal_win32_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); +static int peak_platform_init(void); +static void peak_platform_quit(void); +static PeakWindowInternal peak_platform_window_open(const char *name, uint32_t width, uint32_t height, uint32_t flags); +static void peak_platform_window_close(PeakWindowInternal *intern); +static uint32_t *peak_platform_window_buffer(PeakWindowInternal *intern, size_t *width, size_t *height); +static void peak_platform_window_present(PeakWindowInternal *intern); +static bool peak_platform_epoll(PeakWindowInternal *intern, PeakEvent *ev); +static int peak_internal_winmm_load(void); +static void peak_internal_win32_audio_fill(int i); +static DWORD WINAPI peak_internal_win32_audio_thread(LPVOID arg); + static PeakWin32 peak_win32; static PeakUser32Api peak_user32; static PeakGdi32Api peak_gdi32; +static PeakWinmmApi peak_winmm; +static PeakAudio peak_audio; static int peak_internal_user32_load(HMODULE handle) @@ -103,34 +166,45 @@ 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; + 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; } } 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; + 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; } static int -peak_win32_buffer(struct peak_win32_win *w, uint32_t width, uint32_t height) +peak_internal_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) + if (!(buffer = calloc((size_t)width * height, sizeof(*buffer)))) return 0; free(w->buffer); w->buffer = buffer; @@ -144,9 +218,10 @@ peak_internal_win32_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { struct peak_win32_win *w; PeakEvent ev; + CREATESTRUCTA *cs; if (msg == WM_NCCREATE) { - CREATESTRUCTA *cs = (CREATESTRUCTA *)lparam; + cs = (CREATESTRUCTA *)lparam; peak_user32.SetPropA(hwnd, PEAK_WIN32_PROP, cs->lpCreateParams); } @@ -156,45 +231,46 @@ peak_internal_win32_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) switch (msg) { case WM_CLOSE: - memset(&ev, 0, sizeof ev); + 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)) + if (!peak_internal_win32_buffer(w, width, height)) return 0; - memset(&ev, 0, sizeof ev); + 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_KEYDOWN: /* FALLTHROUGH */ + case WM_KEYUP: /* FALLTHROUGH */ + case WM_SYSKEYDOWN: /* FALLTHROUGH */ case WM_SYSKEYUP: - memset(&ev, 0, sizeof ev); + 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_MOUSEMOVE: /* FALLTHROUGH */ + case WM_LBUTTONDOWN: /* FALLTHROUGH */ + case WM_LBUTTONUP: /* FALLTHROUGH */ + case WM_RBUTTONDOWN: /* FALLTHROUGH */ + case WM_RBUTTONUP: /* FALLTHROUGH */ + case WM_MBUTTONDOWN: /* FALLTHROUGH */ case WM_MBUTTONUP: - memset(&ev, 0, sizeof ev); + memset(&ev, 0, sizeof(ev)); ev.type = PEAK_EVENT_POINTER; ev.pointer.x = (float)(short)LOWORD(lparam); ev.pointer.y = (float)(short)HIWORD(lparam); @@ -215,6 +291,7 @@ peak_internal_win32_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) return 0; case WM_PAINT: { PAINTSTRUCT ps; + peak_user32.BeginPaint(hwnd, &ps); peak_user32.EndPaint(hwnd, &ps); return 0; @@ -255,8 +332,8 @@ peak_platform_init(void) } } if (!peak_win32.class_reg) { - memset(&wc, 0, sizeof wc); - wc.cbSize = sizeof wc; + memset(&wc, 0, sizeof(wc)); + wc.cbSize = sizeof(wc); wc.style = CS_OWNDC; wc.lpfnWndProc = peak_internal_win32_wndproc; wc.hInstance = GetModuleHandleA(NULL); @@ -293,10 +370,9 @@ peak_platform_window_open(const char *name, uint32_t width, uint32_t height, uin if (!peak_user32.CreateWindowExA && !peak_platform_init()) return intern; - w = calloc(1, sizeof *w); - if (!w) + if (!(w = calloc(1, sizeof(*w)))) return intern; - if (!peak_win32_buffer(w, width, height)) { + if (!peak_internal_win32_buffer(w, width, height)) { free(w); return intern; } @@ -335,6 +411,7 @@ static void peak_platform_window_close(PeakWindowInternal *intern) { struct peak_win32_win *w; + if (!intern || !intern->w) return; w = intern->w; @@ -352,7 +429,9 @@ peak_platform_window_close(PeakWindowInternal *intern) static uint32_t * peak_platform_window_buffer(PeakWindowInternal *intern, size_t *width, size_t *height) { - struct peak_win32_win *w = intern ? intern->w : NULL; + struct peak_win32_win *w; + + w = intern ? intern->w : NULL; if (!w) { *width = 0; *height = 0; @@ -366,13 +445,14 @@ peak_platform_window_buffer(PeakWindowInternal *intern, size_t *width, size_t *h static void peak_platform_window_present(PeakWindowInternal *intern) { - struct peak_win32_win *w = intern ? intern->w : NULL; + struct peak_win32_win *w; BITMAPINFO bmi; + w = intern ? intern->w : NULL; if (!w || !w->hwnd || !w->hdc || !w->buffer) return; - memset(&bmi, 0, sizeof bmi); + memset(&bmi, 0, sizeof(bmi)); bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmi.bmiHeader.biWidth = (LONG)w->width; bmi.bmiHeader.biHeight = -(LONG)w->height; @@ -389,8 +469,9 @@ static bool peak_platform_epoll(PeakWindowInternal *intern, PeakEvent *ev) { MSG msg; - struct peak_win32_win *w = intern ? intern->w : NULL; + struct peak_win32_win *w; + w = intern ? intern->w : NULL; if (!w || !w->hwnd) return 0; @@ -402,3 +483,195 @@ peak_platform_epoll(PeakWindowInternal *intern, PeakEvent *ev) } return peak_q_pop(&w->q, ev); } + +static int +peak_internal_winmm_load(void) +{ + HMODULE handle; + + if (peak_winmm.waveOutOpen) + return 1; + if (!(handle = LoadLibraryA(PEAK_WIN32_WINMM))) { + fputs("Failed to load winmm.dll. What system are you fucking using and abusing?", stderr); + return 0; + } +#define X(name, ret, conv, args) peak_winmm.name = (ret (conv *) args)(void *)GetProcAddress(handle, #name); + PEAK_WINMM_API(X) +#undef X +#define X(name, ret, conv, args) || !peak_winmm.name + if (0 PEAK_WINMM_API(X)) { + fputs("Failed to load winmm symbols", stderr); + return 0; + } +#undef X + return 1; +} + +static void +peak_internal_win32_audio_fill(int i) +{ + memset(peak_audio.pcm[i], 0, peak_audio.bytes); + if (peak_audio.fill) + peak_audio.fill(peak_audio.pcm[i], PEAK_AUDIO_FRAMES, peak_audio.userdata); + peak_winmm.waveOutWrite(peak_audio.out, &peak_audio.hdr[i], sizeof(WAVEHDR)); +} + +static DWORD WINAPI +peak_internal_win32_audio_thread(LPVOID arg) +{ + int i, did; + + (void)arg; + for (;;) { + did = 0; + if (!peak_audio.run) + break; + for (i = 0; i < PEAK_AUDIO_BUFFERS; i++) { + if (peak_audio.hdr[i].dwFlags & WHDR_DONE) { + peak_internal_win32_audio_fill(i); + did = 1; + } + } + if (did) + continue; + WaitForSingleObject(peak_audio.event, INFINITE); + ResetEvent(peak_audio.event); + } + return 0; +} + +static int +peak_platform_audio_start(uint32_t channels, uint32_t rate, void (*fill)(int16_t *out, size_t frames, void *userdata), void *userdata) +{ + WAVEFORMATEX fmt; + int i; + + if (channels > 32) + return 0; + if (!peak_internal_winmm_load()) + return 0; + + memset(&fmt, 0, sizeof(fmt)); + fmt.wFormatTag = WAVE_FORMAT_PCM; + fmt.nChannels = (WORD)channels; + fmt.nSamplesPerSec = rate; + fmt.wBitsPerSample = 16; + fmt.nBlockAlign = (WORD)(channels * 2); + fmt.nAvgBytesPerSec = rate * fmt.nBlockAlign; + + peak_audio.event = CreateEventA(NULL, TRUE, FALSE, NULL); + if (!peak_audio.event) + return 0; + if (peak_winmm.waveOutOpen(&peak_audio.out, WAVE_MAPPER, &fmt, (DWORD_PTR)peak_audio.event, 0, CALLBACK_EVENT) != MMSYSERR_NOERROR) { + CloseHandle(peak_audio.event); + peak_audio.event = NULL; + fputs("Failed to open waveOut device", stderr); + return 0; + } + + peak_audio.channels = channels; + peak_audio.bytes = (uint32_t)channels * PEAK_AUDIO_FRAMES * sizeof(int16_t); + peak_audio.fill = fill; + peak_audio.userdata = userdata; + peak_audio.run = 1; + + for (i = 0; i < PEAK_AUDIO_BUFFERS; i++) { + if (!(peak_audio.pcm[i] = calloc(1, peak_audio.bytes))) + goto fail; + memset(&peak_audio.hdr[i], 0, sizeof(peak_audio.hdr[i])); + peak_audio.hdr[i].lpData = (LPSTR)peak_audio.pcm[i]; + peak_audio.hdr[i].dwBufferLength = peak_audio.bytes; + if (peak_winmm.waveOutPrepareHeader(peak_audio.out, &peak_audio.hdr[i], sizeof(WAVEHDR)) != MMSYSERR_NOERROR) + goto fail; + peak_audio.hdr[i].dwFlags |= WHDR_DONE; + } + + for (i = 0; i < PEAK_AUDIO_BUFFERS; i++) + peak_internal_win32_audio_fill(i); + + peak_audio.thread = CreateThread(NULL, 0, peak_internal_win32_audio_thread, NULL, 0, NULL); + if (!peak_audio.thread) + goto fail; + return 1; + +fail: + peak_platform_audio_stop(); + return 0; +} + +static uint64_t +peak_platform_get_time(void) +{ + LARGE_INTEGER count, freq; + QueryPerformanceCounter(&count); + QueryPerformanceFrequency(&freq); + return (uint64_t)((count.QuadPart * 1000000000ull) / freq.QuadPart); +} + +static void +peak_platform_sleep_ns(int64_t ns) +{ + if (ns <= 0) return; + Sleep((DWORD)(ns / 1000000)); +} + +static const char ** +peak_platform_vulkan_get_extensions(uint32_t *count) +{ + static const char *exts[] = { + "VK_KHR_surface", + "VK_KHR_win32_surface", + }; + if (count) *count = 2; + return exts; +} + +static int +peak_platform_vulkan_create_surface(PeakWindowInternal *intern, void *instance, const void *allocator, void *out_surface) +{ +#ifdef PEAK_VULKAN + VkWin32SurfaceCreateInfoKHR ci; + if (!intern || !intern->w || !intern->w->hwnd) return 0; + memset(&ci, 0, sizeof ci); + ci.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; + ci.hwnd = intern->w->hwnd; + ci.hinstance = GetModuleHandleA(NULL); + return vkCreateWin32SurfaceKHR((VkInstance)instance, &ci, (const VkAllocationCallbacks *)allocator, (VkSurfaceKHR *)out_surface) == VK_SUCCESS; +#else + (void)intern; (void)instance; (void)allocator; (void)out_surface; + return 0; +#endif +} + +static void +peak_platform_audio_stop(void) +{ + int i; + + peak_audio.run = 0; + if (peak_audio.event) + SetEvent(peak_audio.event); + if (peak_audio.thread) { + WaitForSingleObject(peak_audio.thread, INFINITE); + CloseHandle(peak_audio.thread); + peak_audio.thread = NULL; + } + if (peak_audio.out) { + peak_winmm.waveOutReset(peak_audio.out); + for (i = 0; i < PEAK_AUDIO_BUFFERS; i++) { + if (peak_audio.hdr[i].dwFlags & WHDR_PREPARED) + peak_winmm.waveOutUnprepareHeader(peak_audio.out, &peak_audio.hdr[i], sizeof(WAVEHDR)); + free(peak_audio.pcm[i]); + peak_audio.pcm[i] = NULL; + memset(&peak_audio.hdr[i], 0, sizeof(peak_audio.hdr[i])); + } + peak_winmm.waveOutClose(peak_audio.out); + peak_audio.out = NULL; + } + if (peak_audio.event) { + CloseHandle(peak_audio.event); + peak_audio.event = NULL; + } + peak_audio.fill = NULL; + peak_audio.userdata = NULL; +} -- cgit v1.3.1