From 336d338eb496a748b9255b9371f426f41a41d146 Mon Sep 17 00:00:00 2001 From: Hunter Kvalevog Date: Fri, 2 Oct 2026 22:02:14 -0500 Subject: restructure repo --- README | 9 +- win-resize-mt/build.bat | 3 - win-resize-mt/build.sh | 3 - win-resize-mt/win-resize-mt.c | 276 ------------------------------------------ win-resize-st/build.bat | 3 - win-resize-st/build.sh | 3 - win-resize-st/win-resize-st.c | 227 ---------------------------------- win/resize-mt/build.bat | 3 + win/resize-mt/build.sh | 3 + win/resize-mt/win-resize-mt.c | 276 ++++++++++++++++++++++++++++++++++++++++++ win/resize-st/build.bat | 3 + win/resize-st/build.sh | 3 + win/resize-st/win-resize-st.c | 227 ++++++++++++++++++++++++++++++++++ 13 files changed, 519 insertions(+), 520 deletions(-) delete mode 100644 win-resize-mt/build.bat delete mode 100644 win-resize-mt/build.sh delete mode 100644 win-resize-mt/win-resize-mt.c delete mode 100644 win-resize-st/build.bat delete mode 100644 win-resize-st/build.sh delete mode 100644 win-resize-st/win-resize-st.c create mode 100644 win/resize-mt/build.bat create mode 100644 win/resize-mt/build.sh create mode 100644 win/resize-mt/win-resize-mt.c create mode 100644 win/resize-st/build.bat create mode 100644 win/resize-st/build.sh create mode 100644 win/resize-st/win-resize-st.c diff --git a/README b/README index b4115a7..5d9b923 100644 --- a/README +++ b/README @@ -6,13 +6,12 @@ correct, performant, safe, or (generally speaking) good. Home page https://git.kvog.sh/demos Mirror https://github.com/kvog-git/demos -aarch64 AArch64 assembly experiments +aarch64/ AArch64 assembly experiments ffmpeg-shadertoy Basic offline Shadertoy renderer gl-asylum Procedurally generated 3D OpenGL scene huffman Huffman encoding demo qoidec Quite OK Image Format decoder -rust-asm Rust -> assembly interop demo -rustlings rustlings +rust/ Rust programming language experiments vk-cube Spinning cube Vulkan demo -win-resize-mt Drawing while resizing on Windows (multi threaded) -win-resize-st Drawing while resizing on Windows (single threaded) +win/resize-mt Drawing while resizing (multi threaded) +win/resize-st Drawing while resizing (single threaded) diff --git a/win-resize-mt/build.bat b/win-resize-mt/build.bat deleted file mode 100644 index 45cfb7f..0000000 --- a/win-resize-mt/build.bat +++ /dev/null @@ -1,3 +0,0 @@ -@echo off -if not exist bin mkdir bin -cl /W4 /Od /Zi /Fe:bin\win-resize-mt.exe win-resize-mt.c /Fd:bin\ /Fo:bin\ diff --git a/win-resize-mt/build.sh b/win-resize-mt/build.sh deleted file mode 100644 index c7c4423..0000000 --- a/win-resize-mt/build.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/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 deleted file mode 100644 index 06fb5d0..0000000 --- a/win-resize-mt/win-resize-mt.c +++ /dev/null @@ -1,276 +0,0 @@ -// ================================================================================================ -// Example of how to draw while resizing / moving a window on Windows in C. -// -// Window contents are rendered via GDI, but the behavior is the same no matter what graphics API -// you use. The original version of this demo (on old-20260427 branch) used D3D11. -// -// This demo uses a multiple threads. There's a multi threaded version called win-resize-mt 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: -// 6/2/2026: Fixed potential race condition during resize -// 5/21/2026: 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 -#include - -#include -#include -#include - -#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_SYNC (1 << 1) - -typedef struct ThreadData ThreadData; -struct ThreadData -{ - HWND wnd; - HANDLE init_sig; - HANDLE sync_sig; - volatile LONG bits; - volatile LONG size; -}; - -// -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; - } - - const LONG size = InterlockedOr(&td->size, 0); - if (size > 0) { - GfxResize(LOWORD(size), HIWORD(size)); - InterlockedExchange(&td->size, 0); - } - - 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, lparam); - - // 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(); - } -} - diff --git a/win-resize-st/build.bat b/win-resize-st/build.bat deleted file mode 100644 index 8568ca2..0000000 --- a/win-resize-st/build.bat +++ /dev/null @@ -1,3 +0,0 @@ -@echo off -if not exist bin mkdir bin -cl /W4 /Od /Zi /Fe:bin\win-resize-st.exe win-resize-st.c /Fd:bin\ /Fo:bin\ diff --git a/win-resize-st/build.sh b/win-resize-st/build.sh deleted file mode 100644 index d644837..0000000 --- a/win-resize-st/build.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash -mkdir -p bin -cc -o bin/win-resize-st -Wall -Wextra -Wpedantic -O0 -g ./win-resize-st.c -ldwmapi -lgdi32 diff --git a/win-resize-st/win-resize-st.c b/win-resize-st/win-resize-st.c deleted file mode 100644 index fe6cf97..0000000 --- a/win-resize-st/win-resize-st.c +++ /dev/null @@ -1,227 +0,0 @@ -// ================================================================================================ -// Example of how to draw while resizing / moving a window on Windows in C. -// -// Window contents are rendered via GDI, but the behavior is the same no matter what graphics API -// you use. The original version of this demo (on old-20260427 branch) used D3D11. -// -// This demo uses a single thread. There's a multi threaded version called win-resize-mt if that's -// more your style. -// -// Build (MSVC): -// > cl /W4 /Od /Zi win-resize-st.c /Fe:win-resize-st.exe -// Build (GCC/clang): -// $ cc -o win-resize-st.exe -Wall -Wextra -Wpedantic -O0 -g win-resize-st.c -ldwmapi -lgdi32 -// -// Changelog: -// 5/15/2026: 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 -#include - -#include -#include -#include - -#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); - -// -static LRESULT CALLBACK WndProc(HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam) -{ - switch (msg) - { - case WM_DESTROY: - PostQuitMessage(0); - return 0; - case WM_SIZE: - GfxResize(LOWORD(lparam), HIWORD(lparam)); - GfxDraw(); - break; - case WM_ENTERSIZEMOVE: - SetTimer(wnd, 1, USER_TIMER_MINIMUM, 0); - break; - case WM_EXITSIZEMOVE: - KillTimer(wnd, 1); - break; - case WM_TIMER: - GfxDraw(); - 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-st"; - - 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"); - - // - GfxInit(wnd); - - // - ShowWindow(wnd, cmdshow); - - // Run a typical D3D-style window loop - BOOL quit = FALSE; - while (!quit) - { - // Pump the message loop - MSG msg; - while (PeekMessageW(&msg, 0, 0, 0, PM_REMOVE)) - { - quit |= (msg.message == WM_QUIT); - TranslateMessage(&msg); - DispatchMessageW(&msg); - } - - // Update the screen - GfxDraw(); - } - - 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-st (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(); - } -} - diff --git a/win/resize-mt/build.bat b/win/resize-mt/build.bat new file mode 100644 index 0000000..45cfb7f --- /dev/null +++ b/win/resize-mt/build.bat @@ -0,0 +1,3 @@ +@echo off +if not exist bin mkdir bin +cl /W4 /Od /Zi /Fe:bin\win-resize-mt.exe win-resize-mt.c /Fd:bin\ /Fo:bin\ 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..06fb5d0 --- /dev/null +++ b/win/resize-mt/win-resize-mt.c @@ -0,0 +1,276 @@ +// ================================================================================================ +// Example of how to draw while resizing / moving a window on Windows in C. +// +// Window contents are rendered via GDI, but the behavior is the same no matter what graphics API +// you use. The original version of this demo (on old-20260427 branch) used D3D11. +// +// This demo uses a multiple threads. There's a multi threaded version called win-resize-mt 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: +// 6/2/2026: Fixed potential race condition during resize +// 5/21/2026: 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 +#include + +#include +#include +#include + +#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_SYNC (1 << 1) + +typedef struct ThreadData ThreadData; +struct ThreadData +{ + HWND wnd; + HANDLE init_sig; + HANDLE sync_sig; + volatile LONG bits; + volatile LONG size; +}; + +// +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; + } + + const LONG size = InterlockedOr(&td->size, 0); + if (size > 0) { + GfxResize(LOWORD(size), HIWORD(size)); + InterlockedExchange(&td->size, 0); + } + + 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, lparam); + + // 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(); + } +} + diff --git a/win/resize-st/build.bat b/win/resize-st/build.bat new file mode 100644 index 0000000..8568ca2 --- /dev/null +++ b/win/resize-st/build.bat @@ -0,0 +1,3 @@ +@echo off +if not exist bin mkdir bin +cl /W4 /Od /Zi /Fe:bin\win-resize-st.exe win-resize-st.c /Fd:bin\ /Fo:bin\ diff --git a/win/resize-st/build.sh b/win/resize-st/build.sh new file mode 100644 index 0000000..d644837 --- /dev/null +++ b/win/resize-st/build.sh @@ -0,0 +1,3 @@ +#!/bin/bash +mkdir -p bin +cc -o bin/win-resize-st -Wall -Wextra -Wpedantic -O0 -g ./win-resize-st.c -ldwmapi -lgdi32 diff --git a/win/resize-st/win-resize-st.c b/win/resize-st/win-resize-st.c new file mode 100644 index 0000000..fe6cf97 --- /dev/null +++ b/win/resize-st/win-resize-st.c @@ -0,0 +1,227 @@ +// ================================================================================================ +// Example of how to draw while resizing / moving a window on Windows in C. +// +// Window contents are rendered via GDI, but the behavior is the same no matter what graphics API +// you use. The original version of this demo (on old-20260427 branch) used D3D11. +// +// This demo uses a single thread. There's a multi threaded version called win-resize-mt if that's +// more your style. +// +// Build (MSVC): +// > cl /W4 /Od /Zi win-resize-st.c /Fe:win-resize-st.exe +// Build (GCC/clang): +// $ cc -o win-resize-st.exe -Wall -Wextra -Wpedantic -O0 -g win-resize-st.c -ldwmapi -lgdi32 +// +// Changelog: +// 5/15/2026: 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 +#include + +#include +#include +#include + +#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); + +// +static LRESULT CALLBACK WndProc(HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + switch (msg) + { + case WM_DESTROY: + PostQuitMessage(0); + return 0; + case WM_SIZE: + GfxResize(LOWORD(lparam), HIWORD(lparam)); + GfxDraw(); + break; + case WM_ENTERSIZEMOVE: + SetTimer(wnd, 1, USER_TIMER_MINIMUM, 0); + break; + case WM_EXITSIZEMOVE: + KillTimer(wnd, 1); + break; + case WM_TIMER: + GfxDraw(); + 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-st"; + + 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"); + + // + GfxInit(wnd); + + // + ShowWindow(wnd, cmdshow); + + // Run a typical D3D-style window loop + BOOL quit = FALSE; + while (!quit) + { + // Pump the message loop + MSG msg; + while (PeekMessageW(&msg, 0, 0, 0, PM_REMOVE)) + { + quit |= (msg.message == WM_QUIT); + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + + // Update the screen + GfxDraw(); + } + + 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-st (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(); + } +} + -- cgit v1.2.3