From 2a9cdc0e736111d27510b8d5b8c26c2ca9b8e727 Mon Sep 17 00:00:00 2001 From: Lea Anthony Date: Sat, 28 Feb 2026 16:08:34 +1100 Subject: [PATCH] fix(windows): use WM_NCHITTEST instead of removing WS_EX_LAYERED in fullscreen Removing WS_EX_LAYERED dynamically breaks the layered window rendering because unfullscreen() cannot properly re-initialize the compositing pipeline when re-adding the flag via SetWindowLong (the window was originally created with WS_EX_LAYERED via CreateWindowEx which auto- initializes, but dynamically adding it requires explicit calls to SetLayeredWindowAttributes or UpdateLayeredWindow). Instead, keep WS_EX_LAYERED set and handle the alpha-based hit-testing issue in WndProc: return HTCLIENT for WM_NCHITTEST when fullscreen is active, forcing all mouse events to be captured regardless of pixel alpha values. WS_EX_TRANSPARENT is still removed in fullscreen() to prevent explicit click-through. Co-Authored-By: Claude Opus 4.6 --- v3/pkg/application/webview_window_windows.go | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/v3/pkg/application/webview_window_windows.go b/v3/pkg/application/webview_window_windows.go index 1b39cbae2..763d5cb61 100644 --- a/v3/pkg/application/webview_window_windows.go +++ b/v3/pkg/application/webview_window_windows.go @@ -843,13 +843,15 @@ func (w *windowsWebviewWindow) fullscreen() { w32.GWL_STYLE, w.previousWindowStyle & ^uint32(w32.WS_OVERLAPPEDWINDOW) | (w32.WS_POPUP|w32.WS_VISIBLE), ) - // Remove WS_EX_TRANSPARENT and WS_EX_LAYERED to ensure mouse events are captured in fullscreen mode. - // Both flags must be removed to fix click-through issues when Frameless + BackgroundTypeTransparent are used. + // Remove WS_EX_DLGMODALFRAME and WS_EX_TRANSPARENT to prevent mouse click-through in fullscreen. + // WS_EX_LAYERED is intentionally kept: removing it dynamically breaks the layered window rendering + // and cannot be properly restored in unfullscreen() without re-initializing the compositing pipeline. + // Alpha-based hit-testing (from WS_EX_LAYERED) is handled separately via WM_NCHITTEST in WndProc. // See: https://github.com/wailsapp/wails/issues/4408 w32.SetWindowLong( w.hwnd, w32.GWL_EXSTYLE, - w.previousWindowExStyle & ^uint32(w32.WS_EX_DLGMODALFRAME|w32.WS_EX_TRANSPARENT|w32.WS_EX_LAYERED), + w.previousWindowExStyle & ^uint32(w32.WS_EX_DLGMODALFRAME|w32.WS_EX_TRANSPARENT), ) w.isCurrentlyFullscreen = true w32.SetWindowPos(w.hwnd, w32.HWND_TOP, @@ -1384,6 +1386,14 @@ func (w *windowsWebviewWindow) WndProc(msg uint32, wparam, lparam uintptr) uintp return code } + // Force mouse event capture in fullscreen mode for transparent/layered windows. + // WS_EX_LAYERED windows use alpha-based hit testing, so transparent pixels (alpha=0) would + // normally pass mouse events through. Returning HTCLIENT overrides this behavior. + // See: https://github.com/wailsapp/wails/issues/4408 + if msg == w32.WM_NCHITTEST && w.isCurrentlyFullscreen { + return w32.HTCLIENT + } + switch msg { case w32.WM_ACTIVATE: if int(wparam&0xffff) == w32.WA_INACTIVE {