Hello @IVBACK, we're using one issue report per unofficially supported game title, so I've gone ahead and transferred this issue report to https://github.com/ValveSoftware/Proton/issues/7486#issuecomment-5561751423.
proton 10.0x1 2026-09proton 11.0x1 2026-09MANGOHUD=1x1 2026-09
Summary
Wine's
NtUserFlashWindowExis a semi-stub (since 2015). When the target window is minimized it callsNtUserRedrawWindow(hwnd, 0, 0, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW | RDW_FRAME).RDW_UPDATENOWmakes win32u send the paint messages synchronously to the thread that owns the window and block until they are processed. Windows does not repaint synchronously fromFlashWindowEx.Games that call
FlashWindowExfrom their main/game thread while the window is owned by a separate window/input thread can deadlock on this. HELLDIVERS™ 2 (553850) does exactly that: if the game window is minimized (exclusive fullscreen + focus lost, e.g. alt-tab while a mission is loading), the game callsFlashWindowExwhen the mission is ready, and hangs forever on a black screen. It has to be killed. The Steam forums are full of "don't alt-tab while in the hellpod" advice for Linux; this is the root cause.The code is identical in upstream wine master,
ValveSoftware/wineproton_10.0andproton_11.0, and in proton-cachyos, so every Proton build is affected.Environment
fullscreen = true,borderless_fullscreen = false), launch optionsMANGOHUD=1 DISABLE_LAYER_MESA_ANTI_LAG=1 %command%NtUserFlashWindowExcode in Proton 10.0 / 11.0 branches and upstream master (see below).Symptoms / reproduction
FlashWindowExto notify the player. The game freezes: black screen, no audio, GPU idle (~3 % busy), no disk I/O, no kernel/amdgpu errors. KWin eventually offers to kill the unresponsive window. It never recovers.Reproduced 3 times today. It does not happen when the window is not minimized at that moment, which is why it only bites "sometimes".
Analysis
Stack traces of the hung process (
eu-stack/gdb -p, proton-cachyos wine-11.0):Game main thread – blocked inside
FlashWindowEx, waiting for the window-owning thread to answer the synchronous paint message:Window-owning thread ("Window & Input") – inside the game's window procedure, sleeping in a wait loop (the game waits for its render/main thread there), so it never picks up the inter-thread message:
Render thread – busy-polling in game code (100 % of one core, no syscalls), waiting for the main thread.
main → waits for window thread (synchronous RedrawWindow), window thread → waits for renderer/main (game's own wait loop), renderer → waits for main. Classic three-way deadlock, and the only Wine-introduced edge is the synchronous repaint in
NtUserFlashWindowEx.NtUserRedrawWindowis only reached through theis_iconic()branch, which confirms the window was minimized whenFlashWindowExwas called (dlls/win32u/window.c, unchanged since the 2015 semi-stub commit f22760d210, moved to win32u in 2022):Note the non-iconic branch already uses the asynchronous
send_notify_message, so only the minimized case can deadlock.Proposed fix
Drop
RDW_UPDATENOW(or the wholeNtUserRedrawWindowcall) in the iconic branch so the redraw is only queued and the owning thread paints on its next message-loop iteration, like Windows:In the shipped
win32u.sothis is the immediate ofmov $0x505,%ecxright before thecall NtUserRedrawWindowinNtUserFlashWindowEx(0x505 → 0x405). I have not tested the patched build in-game yet; happy to do so and report back, or to provide a Proton log with+message,+winif useful.Workaround for users
Use borderless fullscreen (
borderless_fullscreen = trueinuser_settings.configor the in-game Display Mode setting): the window is then never minimized on focus loss, so theis_iconicbranch is never taken. Or simply don't alt-tab while a mission is loading.