diff options
| author | 2026-04-13 16:24:21 -0400 | |
|---|---|---|
| committer | 2026-04-13 16:39:12 -0400 | |
| commit | 0d027c466c199f9857b0872ed76ebab2ed501e43 (patch) | |
| tree | 17a88480e7dac721902d21611e2d2834f2344c27 /src/window_win32.c | |
| parent | cbad92b78cb9901314dd3304d61bcee3b56312eb (diff) | |
| download | stela-0d027c466c199f9857b0872ed76ebab2ed501e43.tar.gz stela-0d027c466c199f9857b0872ed76ebab2ed501e43.tar.bz2 stela-0d027c466c199f9857b0872ed76ebab2ed501e43.zip | |
Lots of win32 window tweaks, partially unfinished
Signed-off-by: Andrew Opalach <andrew@akon.city>
Diffstat (limited to 'src/window_win32.c')
| -rw-r--r-- | src/window_win32.c | 226 |
1 files changed, 157 insertions, 69 deletions
diff --git a/src/window_win32.c b/src/window_win32.c index 46835e2..d97f51b 100644 --- a/src/window_win32.c +++ b/src/window_win32.c @@ -5,6 +5,12 @@ #include "common.h" #include "keys.h" +// All extra Windows headers come after <nnwt/windows.h>. +#include <windowsx.h> +#ifndef NAUNET_WIN32_COMPAT_MODE +#include <dwmapi.h> +#endif + #if defined STELA_API_VULKAN #include "vk_common.h" #elif defined STELA_API_OPENGL @@ -100,12 +106,14 @@ static LRESULT CALLBACK ServiceWndProc(HWND Window, UINT Message, WPARAM WParam, Baby->lpParam); break; } - case DESTROY_DANGEROUS_WINDOW: - if (!DestroyWindow((HWND)WParam)) { + case DESTROY_DANGEROUS_WINDOW: { + struct stl_window_win32 *w32 = (struct stl_window_win32 *)WParam; + if (!w32->Closed && !DestroyWindow(w32->Window)) { log_error("Failed to destroy window (0x%lx).", GetLastError()); } UnregisterClassW(DangerousClass, Global.ModuleHandle); break; + } case EXIT_PROCESS: PostQuitMessage(WParam); break; @@ -370,7 +378,7 @@ void stl_global_close(void) static void *GetAnyGLFuncAddress(const char *Name) { void *p = (void *)wglGetProcAddress(Name); - if (p == 0 || (p == (void *)0x1) || (p == (void *)0x2) || (p == (void *)0x3) || (p == (void *)-1)) { + if (p == 0 || p == (void *)0x1 || p == (void *)0x2 || p == (void *)0x3 || p == (void *)-1) { HMODULE GLModule = LoadLibraryA("opengl32.dll"); p = (void *)GetProcAddress(GLModule, Name); } @@ -669,6 +677,10 @@ static LRESULT CALLBACK DisplayWndProc(HWND Window, UINT Message, WPARAM WParam, // Special case with Window as WParam. PostThreadMessageW(MainThreadID, Message, (WPARAM)Window, LParam); break; + /* + case WM_ERASEBKGND: + return TRUE; + */ case WM_MOUSEACTIVATE: { LRESULT HitTest = LOWORD(LParam); UINT MouseButton = HIWORD(LParam); @@ -677,17 +689,19 @@ static LRESULT CALLBACK DisplayWndProc(HWND Window, UINT Message, WPARAM WParam, } break; } - case WM_LBUTTONDOWN: { + case WM_LBUTTONDOWN: // https://learn.microsoft.com/en-us/windows/win32/learnwin32/mouse-movement - SetCapture(w32->Window); + w32->DragCapture = TRUE; + if (!GetCapture()) { + SetCapture(w32->Window); + } goto intercept; - } - case WM_LBUTTONUP: { + case WM_LBUTTONUP: + w32->DragCapture = FALSE; ReleaseCapture(); goto intercept; - } case WM_RBUTTONDOWN: - case WM_NCRBUTTONDOWN: { + case WM_NCRBUTTONDOWN: #ifdef STELA_MOUSE2_DRAG if (!Global.IsWine) { POINT Pos; @@ -699,7 +713,10 @@ static LRESULT CALLBACK DisplayWndProc(HWND Window, UINT Message, WPARAM WParam, w32->LastMousePos.x = Pos.x; w32->LastMousePos.y = Pos.y; w32->WindowMoving = TRUE; - SetCapture(w32->Window); + // DragCapture is a higher priority. + if (!w32->DragCapture) { + SetCapture(w32->Window); + } return Result; } else { /* @@ -716,49 +733,51 @@ static LRESULT CALLBACK DisplayWndProc(HWND Window, UINT Message, WPARAM WParam, #else goto intercept; #endif - } case WM_RBUTTONUP: - case WM_NCRBUTTONUP: { + case WM_NCRBUTTONUP: #ifdef STELA_MOUSE2_DRAG if (!Global.IsWine) { w32->WindowMoving = FALSE; - ReleaseCapture(); + if (!w32->DragCapture) { + ReleaseCapture(); + } return Result; } break; #else goto intercept; #endif - } case WM_MBUTTONDOWN: case WM_MBUTTONUP: goto intercept; case WM_MOUSELEAVE: goto intercept; case WM_MOUSEMOVE: - case WM_NCMOUSEMOVE: { + case WM_NCMOUSEMOVE: if ((WParam & MK_RBUTTON) && w32->WindowMoving) { - POINT Current; - GetCursorPos(&Current); + POINT Pos; + Pos.x = GET_X_LPARAM(LParam); + Pos.y = GET_Y_LPARAM(LParam); + if (Message == WM_MOUSEMOVE) { + ClientToScreen(w32->Window, &Pos); + } RECT Window; GetWindowRect(w32->Window, &Window); SetWindowPos(w32->Window, NULL, - Window.left + (Current.x - w32->LastMousePos.x), - Window.top + (Current.y - w32->LastMousePos.y), + Window.left + (Pos.x - w32->LastMousePos.x), + Window.top + (Pos.y - w32->LastMousePos.y), 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER); - w32->LastMousePos.x = Current.x; - w32->LastMousePos.y = Current.y; + w32->LastMousePos.x = Pos.x; + w32->LastMousePos.y = Pos.y; return Result; } goto intercept; - } case WM_MOUSEWHEEL: goto intercept; - case WM_ACTIVATE: { - // Do not use Wine behavior as a reference, it's too broken. + case WM_ACTIVATE: // Don't use Wine behavior as a reference, it's too broken. switch (LOWORD(WParam)) { case WA_ACTIVE: - case WA_CLICKACTIVE: { + case WA_CLICKACTIVE: LParam = STELA_MOD_LCONTROL * ASYNC_KEY_IS_DOWN(VK_LCONTROL) | STELA_MOD_RCONTROL * ASYNC_KEY_IS_DOWN(VK_RCONTROL) | STELA_MOD_LSHIFT * ASYNC_KEY_IS_DOWN(VK_LSHIFT) | @@ -766,13 +785,10 @@ static LRESULT CALLBACK DisplayWndProc(HWND Window, UINT Message, WPARAM WParam, STELA_MOD_LALT * ASYNC_KEY_IS_DOWN(VK_LMENU) | STELA_MOD_RALT * ASYNC_KEY_IS_DOWN(VK_RMENU); break; - } - case WA_INACTIVE: { + case WA_INACTIVE: break; } - } goto intercept; - } case WM_KEYDOWN: case WM_SYSKEYDOWN: case WM_KEYUP: @@ -780,8 +796,8 @@ static LRESULT CALLBACK DisplayWndProc(HWND Window, UINT Message, WPARAM WParam, // https://learn.microsoft.com/en-us/windows/win32/inputdev/about-keyboard-input WORD Flags = HIWORD(LParam); bool PreviouslyDown = (Flags & KF_REPEAT) == KF_REPEAT; - if (((Message == WM_KEYDOWN || Message == WM_SYSKEYDOWN) && PreviouslyDown) || - ((Message == WM_KEYUP || Message == WM_SYSKEYUP) && !PreviouslyDown)) { + if (((Message == WM_KEYDOWN || Message == WM_SYSKEYDOWN) && PreviouslyDown) || + ((Message == WM_KEYUP || Message == WM_SYSKEYUP) && !PreviouslyDown)) { break; // Repeat. } WORD Scancode = LOBYTE(Flags); @@ -835,20 +851,20 @@ static bool CreateWGLContext(struct stl_window_win32 *w32) PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER | PFD_DEPTH_DONTCARE, #endif PFD_TYPE_RGBA, - 32, // Colordepth of the framebuffer. + 32, // Colordepth of the framebuffer. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, #ifdef STELA_INCLUDE_DEPTH_BUFFER - 24, // Bits for the depthbuffer. - 8, // Bits for the stencilbuffer. + 24, // Bits for the depthbuffer. + 8, // Bits for the stencilbuffer. #else 0, 0, #endif - 0, // Number of "Aux" buffers. + 0, // Number of "Aux" buffers. PFD_MAIN_PLANE, 0, 0, 0, 0 @@ -924,38 +940,46 @@ static bool CreateWGLContext(struct stl_window_win32 *w32) } #endif -static bool window_win32_create_window(struct stl_window *window, u32 width, u32 height, - const char *monitor, const char *name, u32 flags) +static bool window_win32_create_window(struct stl_window *window, u32 width, u32 height, u32 flags, + const char *monitor, const char *name, const char *app_id) { struct stl_window_win32 *w32 = (struct stl_window_win32 *)window; (void)monitor; + (void)app_id; WNDCLASSEXW WindowClass = { 0 }; WindowClass.cbSize = sizeof(WindowClass); WindowClass.lpfnWndProc = &DisplayWndProc; WindowClass.hInstance = Global.ModuleHandle; - WindowClass.hIcon = LoadIconA(NULL, IDI_APPLICATION); + HANDLE Icon = LoadImageW(Global.ModuleHandle, + L"CMV_ICON", IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED); + if (!Icon) Icon = LoadIconA(NULL, IDI_APPLICATION); + WindowClass.hIcon = Icon; WindowClass.hCursor = LoadCursorA(NULL, IDC_ARROW); WindowClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); WindowClass.lpszClassName = DangerousClass; // https://learn.microsoft.com/en-us/windows/win32/winmsg/window-class-styles - WindowClass.style = CS_OWNDC; + // On CS_HREDRAW | CS_VREDRAW: The idea is as long as we are equally as notified of + // window resizes, let us decide when to redraw. + WindowClass.style = 0; RegisterClassExW(&WindowClass); // Add in window decorations so width and height reference the client area. - RECT Over = { 0, 0, width, height }; - AdjustWindowRect(&Over, WS_OVERLAPPEDWINDOW, FALSE); + RECT Overlap = { 0, 0, width, height }; + AdjustWindowRect(&Overlap, WS_OVERLAPPEDWINDOW, FALSE); struct the_baby Baby = { 0 }; - Baby.dwExStyle = 0; Baby.lpClassName = WindowClass.lpszClassName; al_wstr_from_cstr(&w32->WindowName, (char *)name); Baby.lpWindowName = w32->WindowName.data; + // https://learn.microsoft.com/en-us/windows/win32/winmsg/window-styles + // https://learn.microsoft.com/en-us/windows/win32/winmsg/extended-window-styles Baby.dwStyle = WS_OVERLAPPEDWINDOW | WS_VISIBLE; + Baby.dwExStyle = 0; Baby.X = CW_USEDEFAULT; Baby.Y = CW_USEDEFAULT; - Baby.nWidth = Over.right - Over.left; - Baby.nHeight = Over.bottom - Over.top; + Baby.nWidth = Overlap.right - Overlap.left; + Baby.nHeight = Overlap.bottom - Overlap.top; Baby.hInstance = WindowClass.hInstance; Baby.lpParam = w32; w32->Window = (HWND)SendMessageW(Global.ServiceWindow, CREATE_DANGEROUS_WINDOW, (WPARAM)&Baby, 0); @@ -966,9 +990,12 @@ static bool window_win32_create_window(struct stl_window *window, u32 width, u32 stl_window_created(&w32->w, width, height, flags); + w32->Closed = FALSE; w32->Decorated = TRUE; w32->UserDecorated = TRUE; w32->Prev.Style = GetWindowLong(w32->Window, GWL_STYLE); + w32->Prev.ExStyle = GetWindowLong(w32->Window, GWL_EXSTYLE); + w32->DragCapture = TRUE; w32->WindowMoving = FALSE; w32->MouseButtonMask = 0; @@ -991,9 +1018,15 @@ static void window_win32_resize_internal(struct stl_window *window, u32 width, u static void window_win32_resize(struct stl_window *window, u32 width, u32 height) { - (void)window; - (void)width; - (void)height; + struct stl_window_win32 *w32 = (struct stl_window_win32 *)window; + if (!w32->w.fullscreen) { + ShowWindow(w32->Window, SW_SHOWNOACTIVATE); + RECT Overlap = { 0, 0, width, height }; + AdjustWindowRect(&Overlap, WS_OVERLAPPEDWINDOW, FALSE); + SetWindowPos(w32->Window, HWND_NOTOPMOST, 0, 0, + Overlap.right - Overlap.left, Overlap.bottom - Overlap.top, + SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE); + } } static struct stl_monitor_win32 *get_monitor_from_handle(HMONITOR Handle) @@ -1007,18 +1040,55 @@ static struct stl_monitor_win32 *get_monitor_from_handle(HMONITOR Handle) return NULL; } +// @TODO: +// - Separate decorations and transparent background. +// - Fix jump on toggle. static void set_decorations(struct stl_window_win32 *w32, bool enabled) { - if (!enabled && w32->Decorated) { + if (!enabled && w32->Decorated) { // Voodoo... // https://stackoverflow.com/a/2400467 LONG Style = GetWindowLong(w32->Window, GWL_STYLE); + LONG ExStyle = GetWindowLong(w32->Window, GWL_EXSTYLE); w32->Prev.Style = Style; + w32->Prev.ExStyle = ExStyle; Style &= ~(WS_CAPTION | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SYSMENU); + //ExStyle |= WS_EX_LAYERED; SetWindowLong(w32->Window, GWL_STYLE, Style); + //SetWindowLong(w32->Window, GWL_EXSTYLE, ExStyle); + //SetLayeredWindowAttributes(w32->Window, 0, 255, LWA_ALPHA); + //SetLayeredWindowAttributes(w32->Window, RGB(0, 0, 0), 0, LWA_COLORKEY); + /* + SetLayeredWindowAttributes(w32->Window, 0, 0, LWA_ALPHA); + BLENDFUNCTION blend = { 0 }; + blend.BlendOp = AC_SRC_OVER; + blend.SourceConstantAlpha = 0; + blend.AlphaFormat = AC_SRC_ALPHA; + POINT ptLocation = { 0, 0 }; + SIZE szWnd = { w32->w.width, w32->w.height }; + POINT ptSrc = { 0, 0 }; + UpdateLayeredWindow(w32->Window, NULL, &ptLocation, &szWnd, NULL, &ptSrc, 0, &blend, ULW_ALPHA); + */ + // https://github.com/mpv-player/mpv/blob/dbd7a905b6ed47dd8f0acd09a1f4cc9a08e854a6/video/out/w32_common.c#L2575C1-L2575C30 +#ifndef NAUNET_WIN32_COMPAT_MODE + DWM_BLURBEHIND dbb = { 0 }; + HRGN Region = CreateRectRgn(0, 0, -1, -1); + dbb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION; + dbb.hRgnBlur = Region; + dbb.fEnable = TRUE; + DwmEnableBlurBehindWindow(w32->Window, &dbb); + DeleteObject(Region); +#endif w32->Decorated = FALSE; } else if (enabled && !w32->Decorated) { al_assert(w32->Prev.Style != 0); SetWindowLong(w32->Window, GWL_STYLE, w32->Prev.Style); + SetWindowLong(w32->Window, GWL_EXSTYLE, w32->Prev.ExStyle); +#ifndef NAUNET_WIN32_COMPAT_MODE + DWM_BLURBEHIND dbb = { 0 }; + dbb.dwFlags = DWM_BB_ENABLE; + dbb.fEnable = FALSE; + DwmEnableBlurBehindWindow(w32->Window, &dbb); +#endif w32->Decorated = TRUE; } } @@ -1056,9 +1126,35 @@ static void window_win32_set_decorations(struct stl_window *window, bool enabled struct stl_window_win32 *w32 = (struct stl_window_win32 *)window; w32->UserDecorated = enabled; if (!w32->w.fullscreen) { + s32 cx, cy, dx = 0, dy = 0; + if (!enabled) { + RECT Prev; + GetWindowRect(w32->Window, &Prev); + w32->Prev.X = Prev.left; + w32->Prev.Y = Prev.top; + w32->Prev.Width = Prev.right - Prev.left; + w32->Prev.Height = Prev.bottom - Prev.top; + //RECT Over = { 0, 0, w32->w.width, w32->w.height }; + //AdjustWindowRect(&Over, WS_OVERLAPPEDWINDOW, FALSE); + cx = w32->w.width; + cy = w32->w.height; + // @TODO: What is 8? + dx = ((Prev.right - Prev.left) - cx) - 8; + dy = ((Prev.bottom - Prev.top) - cy) - 8; + dx = Prev.left + dx; + dy = Prev.top + dy; + } else { + RECT Over = { 0, 0, w32->w.width, w32->w.height }; + AdjustWindowRect(&Over, WS_OVERLAPPEDWINDOW, FALSE); + cx = w32->Prev.Width; + cy = w32->Prev.Height; + dx = w32->Prev.X; + dy = w32->Prev.Y; + } set_decorations(w32, enabled); - UINT Flags = SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOSENDCHANGING | SWP_NOACTIVATE; - SetWindowPos(w32->Window, 0, 0, 0, 0, 0, Flags); + //UINT Flags = SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOSENDCHANGING | SWP_NOACTIVATE; + UINT Flags = SWP_FRAMECHANGED | SWP_NOZORDER | SWP_NOSENDCHANGING | SWP_NOACTIVATE; + SetWindowPos(w32->Window, 0, dx, dy, cx, cy, Flags); } } @@ -1079,6 +1175,7 @@ static void win32_handle_message(struct stl_window_win32 *w32, MSG *Message) case WM_CREATE: break; case WM_CLOSE: + w32->Closed = TRUE; #ifdef STELA_API_OPENGL wglDeleteContext(w32->GLContext); // Makes context not current. #endif @@ -1110,27 +1207,23 @@ static void win32_handle_message(struct stl_window_win32 *w32, MSG *Message) stl_window_send_mouse_button_event(&w32->w, STELA_BUTTON_RELEASED, STELA_MOUSE1); break; } - case WM_RBUTTONDOWN: { + case WM_RBUTTONDOWN: w32->MouseButtonMask |= STELA_MOUSE2; stl_window_send_mouse_button_event(&w32->w, STELA_BUTTON_PRESSED, STELA_MOUSE2); break; - } - case WM_RBUTTONUP: { + case WM_RBUTTONUP: w32->MouseButtonMask &= ~STELA_MOUSE2; stl_window_send_mouse_button_event(&w32->w, STELA_BUTTON_RELEASED, STELA_MOUSE2); break; - } - case WM_MBUTTONDOWN: { + case WM_MBUTTONDOWN: w32->MouseButtonMask |= STELA_MOUSE3; stl_window_send_mouse_button_event(&w32->w, STELA_BUTTON_PRESSED, STELA_MOUSE3); break; - } - case WM_MBUTTONUP: { + case WM_MBUTTONUP: w32->MouseButtonMask &= ~STELA_MOUSE3; stl_window_send_mouse_button_event(&w32->w, STELA_BUTTON_RELEASED, STELA_MOUSE3); break; - } - case WM_MOUSELEAVE: { + case WM_MOUSELEAVE: /* if (w32->MouseButtonMask & STELA_MOUSE1) { w32->MouseButtonMask &= ~STELA_MOUSE1; @@ -1138,17 +1231,14 @@ static void win32_handle_message(struct stl_window_win32 *w32, MSG *Message) } */ break; - } case WM_MOUSEMOVE: case WM_NCMOUSEMOVE: { POINT Pos; Pos.x = GET_X_LPARAM(Message->lParam); Pos.y = GET_Y_LPARAM(Message->lParam); - /* - if (!(w32->MouseButtonMask & STELA_MOUSE1)) { + if (Message->message == WM_NCMOUSEMOVE) { ScreenToClient(w32->Window, &Pos); } - */ stl_window_send_pointer_pos_event(&w32->w, (f64)Pos.x, (f64)Pos.y); break; } @@ -1159,7 +1249,7 @@ static void win32_handle_message(struct stl_window_win32 *w32, MSG *Message) stl_window_send_scroll_event(&w32->w, Bits); break; } - case WM_ACTIVATE: { + case WM_ACTIVATE: switch (LOWORD(Message->wParam)) { case WA_ACTIVE: case WA_CLICKACTIVE: { @@ -1172,20 +1262,18 @@ static void win32_handle_message(struct stl_window_win32 *w32, MSG *Message) } break; } - case WA_INACTIVE: { + case WA_INACTIVE: stl_window_drop_modifiers(&w32->w); break; } - } break; - } case WM_KEYDOWN: case WM_SYSKEYDOWN: case WM_KEYUP: case WM_SYSKEYUP: stl_window_send_key_event(&w32->w, MESSAGE_TO_KEY_STATE(Message->message), Global.keycodes[Message->wParam]); break; - case WM_SIZE: + case WM_SIZE: // Gives the size of the client area. w32->w.width = LOWORD(Message->lParam); w32->w.height = HIWORD(Message->lParam); stl_window_send_resize_event(&w32->w, w32->w.width, w32->w.height); @@ -1243,7 +1331,7 @@ static bool window_win32_should_refresh(struct stl_window *window) static void window_win32_free(struct stl_window **window) { struct stl_window_win32 *w32 = (struct stl_window_win32 *)*window; - SendMessageW(Global.ServiceWindow, DESTROY_DANGEROUS_WINDOW, (WPARAM)w32->Window, 0); + SendMessageW(Global.ServiceWindow, DESTROY_DANGEROUS_WINDOW, (WPARAM)w32, 0); al_wstr_free(&w32->WindowName); al_free(w32); *window = NULL; |