diff options
Diffstat (limited to 'win-resize-mt')
| -rw-r--r-- | win-resize-mt/build.sh | 3 | ||||
| -rw-r--r-- | win-resize-mt/win-resize-mt.c | 277 |
2 files changed, 280 insertions, 0 deletions
diff --git a/win-resize-mt/build.sh b/win-resize-mt/build.sh new file mode 100644 index 0000000..c7c4423 --- /dev/null +++ b/win-resize-mt/build.sh @@ -0,0 +1,3 @@ +#!/bin/bash +mkdir -p bin +cc -o bin/win-resize-mt -Wall -Wextra -Wpedantic -O0 -g ./win-resize-mt.c -ldwmapi -lgdi32 diff --git a/win-resize-mt/win-resize-mt.c b/win-resize-mt/win-resize-mt.c new file mode 100644 index 0000000..2f9dd4e --- /dev/null +++ b/win-resize-mt/win-resize-mt.c @@ -0,0 +1,277 @@ +// ================================================================================================ +// Example of how to draw while resizing / moving a window on Windows in C. +// +// This demo uses a dedicated render thread with basic synchronization with the event loop. There +// is a single-threaded version called win-resize/ if that's more your style. +// +// Build (MSVC): +// > cl /W4 /Od /Zi win-resize-mt.c /Fe:win-resize-mt.exe +// Build (GCC/clang): +// $ cc -o win-resize-mt.exe -Wall -Wextra -Wpedantic -O0 -g win-resize-mt.c -ldwmapi -lgdi32 +// +// Changelog: +// XX/XX/XXXX: Initial release +// +// License: +// Copyright (c) 2026 Hunter Kvalevog +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE. +// ================================================================================================ + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <dwmapi.h> + +#include <assert.h> +#include <math.h> +#include <stdio.h> + +#ifdef _MSC_VER +# pragma comment(lib, "dwmapi.lib") +# pragma comment(lib, "gdi32.lib") +# pragma comment(lib, "user32.lib") +#endif + +#define UNUSED(Var) ((void)(Var)) + +static void GfxInit(HWND wnd); +static void GfxResize(UINT vpw, UINT vph); +static void GfxDraw(void); + +#define THREAD_BIT_QUIT (1 << 0) +#define THREAD_BIT_SIZE (1 << 1) +#define THREAD_BIT_SYNC (1 << 2) + +typedef struct ThreadData ThreadData; +struct ThreadData +{ + HWND wnd; + HANDLE init_sig; + HANDLE sync_sig; + volatile LONG bits; + volatile LONG size_w; + volatile LONG size_h; +}; + +// +static DWORD WINAPI RenderThread(LPVOID opaque) +{ + ThreadData *td = opaque; + GfxInit(td->wnd); + GfxDraw(); + + SetEvent(td->init_sig); + + while (TRUE) { + const LONG bits = InterlockedOr(&td->bits, 0); + + if (bits & THREAD_BIT_QUIT) { + break; + } + + if (bits & THREAD_BIT_SIZE) { + const LONG size_w = InterlockedOr(&td->size_w, 0); + const LONG size_h = InterlockedOr(&td->size_h, 0); + GfxResize(size_w, size_h); + InterlockedAnd(&td->bits, ~THREAD_BIT_SIZE); + } + + GfxDraw(); + + if (bits & THREAD_BIT_SYNC) { + SetEvent(td->sync_sig); + InterlockedAnd(&td->bits, ~THREAD_BIT_SYNC); + } + } + + return 0; +} + +// +static LRESULT CALLBACK WndProc(HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + ThreadData *td = (ThreadData *)GetWindowLongPtrW(wnd, GWLP_USERDATA); + switch (msg) + { + case WM_DESTROY: + PostQuitMessage(0); + return 0; + case WM_SIZE: + // Resize + InterlockedExchange(&td->size_w, LOWORD(lparam)); + InterlockedExchange(&td->size_h, HIWORD(lparam)); + InterlockedOr(&td->bits, THREAD_BIT_SIZE); + + // Sync + InterlockedOr(&td->bits, THREAD_BIT_SYNC); + WaitForSingleObject(td->sync_sig, INFINITE); + ResetEvent(td->sync_sig); + break; + } + return DefWindowProcW(wnd, msg, wparam, lparam); +} + +// +int WinMain(HINSTANCE instance, HINSTANCE previnstance, LPSTR cmdline, int cmdshow) +{ + UNUSED(previnstance); UNUSED(cmdline); + + // Register window class + WNDCLASSEXW wc = { 0 }; + wc.cbSize = sizeof(wc); + wc.hCursor = LoadCursor(0, IDC_ARROW); + wc.hInstance = instance; + wc.lpfnWndProc = WndProc; + wc.lpszClassName = L"win-resize-mt"; + + ATOM atom = RegisterClassExW(&wc); + assert(atom && "Failed to register window class"); + + // Create window + HWND wnd = CreateWindowExW(WS_EX_APPWINDOW, wc.lpszClassName, wc.lpszClassName, + WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, + CW_USEDEFAULT, 0, 0, wc.hInstance, 0); + assert(wnd && "Failed to create window"); + + // + ThreadData td = { 0 }; + td.wnd = wnd; + td.init_sig = CreateEvent(0, TRUE, FALSE, 0); + td.sync_sig = CreateEvent(0, TRUE, FALSE, 0); + + // + HANDLE th = CreateThread(0, 0, RenderThread, &td, 0, 0); + assert(th && "Failed to create render thread"); + + // + SetWindowLongPtrW(wnd, GWLP_USERDATA, (LONG_PTR)&td); + WaitForSingleObject(td.init_sig, INFINITE); + + // + ShowWindow(wnd, cmdshow); + + // + MSG msg; + while (GetMessageW(&msg, 0, 0, 0) > 0) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + // + InterlockedOr(&td.bits, THREAD_BIT_QUIT); + WaitForSingleObject(th, INFINITE); + + return 0; +} + +// ================================================================================================ + +static UINT g_vpw = 0; +static UINT g_vph = 0; +static HDC g_hdc = 0; +static HDC g_hdc_back = 0; +static HBITMAP g_bmp = 0; +static HBRUSH g_bgbr = 0; +static HBRUSH g_fgbr = 0; +static HPEN g_pen = 0; + +static void GfxInit(HWND wnd) +{ + g_hdc = GetDC(wnd); + g_hdc_back = CreateCompatibleDC(g_hdc); + g_bgbr = CreateSolidBrush(RGB(0x00, 0x20, 0x20)); + g_fgbr = CreateSolidBrush(RGB(0xE0, 0xE0, 0xE0)); + g_pen = CreatePen(PS_SOLID, 2, RGB(0xFF, 0x10, 0x10)); + + RECT rc; + GetClientRect(wnd, &rc); + + GfxResize(rc.right, rc.bottom); +} + +static void GfxResize(UINT vpw, UINT vph) +{ + if (g_bmp) + { + DeleteObject(g_bmp); + g_bmp = 0; + } + + if (vpw > 0 && vph > 0) + { + g_bmp = CreateCompatibleBitmap(g_hdc, vpw, vph); + SelectObject(g_hdc_back, g_bmp); + } + + g_vpw = vpw; + g_vph = vph; +} + +static void GfxDraw(void) +{ + static UINT frame = 0; + ++frame; + + if (g_bmp && g_vpw > 0 && g_vph > 0) + { + HDC hdc = g_hdc_back; + + // Clear + { + RECT rc = { 0, 0, g_vpw, g_vph }; + FillRect(hdc, &rc, g_bgbr); + } + + // Draw X + { + SelectObject(hdc, g_pen); + MoveToEx(hdc, 0, 0, 0); + LineTo(hdc, g_vpw, g_vph); + MoveToEx(hdc, 0, g_vph, 0); + LineTo(hdc, g_vpw, 0); + } + + // Draw rotating triangle + { + int cx = g_vpw / 2; + int cy = g_vph / 2; + int r = (g_vpw < g_vph ? g_vpw : g_vph) / 3; + + POINT verts[3]; + for (int i = 0; i < 3; ++i) + { + float ang = ((float)frame / 100.0f) + (float)i * (2.0f * 3.14159265f / 3.0f); + verts[i].x = cx + (int)(cosf(ang) * r); + verts[i].y = cy + (int)(sinf(ang) * r); + } + + SelectObject(hdc, g_pen); + SelectObject(hdc, g_fgbr); + Polygon(hdc, verts, ARRAYSIZE(verts)); + } + + // Draw text + { + WCHAR txtbuf[256]; + _snwprintf_s(txtbuf, ARRAYSIZE(txtbuf), _TRUNCATE, L"win-resize-mt (frame %u)", frame); + + SIZE sz; + GetTextExtentPoint32W(hdc, txtbuf, (int)wcslen(txtbuf), &sz); + + SetBkMode(hdc, TRANSPARENT); + SetTextColor(hdc, RGB(0xE0, 0xE0, 0xE0)); + TextOutW(hdc, (g_vpw - sz.cx) / 2, 19, txtbuf, (int)wcslen(txtbuf)); + } + + // Present + BitBlt(g_hdc, 0, 0, g_vpw, g_vph, g_hdc_back, 0, 0, SRCCOPY); + + // Wait for dwm to display a frame + DwmFlush(); + } +} + |