protonscr

[General issue] NtUserFlashWindowEx (semi-stub) deadlocks games that flash a minimized window – HELLDIVERS™ 2 hangs on black screen when alt-tabbed during mission load

protonclosed appid 553850
ValveSoftware/Proton#10126 · opened 2026-09-06 by IVBACK · updated 2026-09-06 · 1 comments · github · game page · search this game
IIVBACK 2026-09-06 github

Summary

Wine's NtUserFlashWindowEx is a semi-stub (since 2015). When the target window is minimized it calls NtUserRedrawWindow(hwnd, 0, 0, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW | RDW_FRAME). RDW_UPDATENOW makes win32u send the paint messages synchronously to the thread that owns the window and block until they are processed. Windows does not repaint synchronously from FlashWindowEx.

Games that call FlashWindowEx from 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 calls FlashWindowEx when 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/wine proton_10.0 and proton_11.0, and in proton-cachyos, so every Proton build is affected.

Environment

  • Game: HELLDIVERS™ 2 (553850), display mode exclusive fullscreen (fullscreen = true, borderless_fullscreen = false), launch options MANGOHUD=1 DISABLE_LAYER_MESA_ANTI_LAG=1 %command%
  • Proton: proton-cachyos 11.0-20260703 (wine-11.0 based, Steam Linux Runtime 3.0). Same NtUserFlashWindowEx code in Proton 10.0 / 11.0 branches and upstream master (see below).
  • Distro/kernel: CachyOS, linux-cachyos 7.2.2
  • Desktop: KDE Plasma 6 Wayland (game runs under Xwayland)
  • GPU: AMD Radeon RX 7900 XT, Mesa 26.2.2 (RADV)
  • CPU: AMD Ryzen 7 9800X3D

Symptoms / reproduction

  1. Start HELLDIVERS 2 in exclusive fullscreen.
  2. Launch a mission ("dive"). While the mission is loading, alt-tab / click another window so the game window gets minimized.
  3. When the mission finishes loading the game calls FlashWindowEx to 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:

#0  ioctl () from libc.so.6
#1  linux_wait_objs () from ntdll.so
#2  NtWaitForMultipleObjects () from ntdll.so
#3  wait_message () from win32u.so
#4  wait_message_reply () from win32u.so
#5  send_inter_thread_message () from win32u.so
#6  send_message () from win32u.so
#7  NtUserRedrawWindow () from win32u.so
#8  NtUserFlashWindowEx () from win32u.so
#9  __wine_syscall_dispatcher () from ntdll.so

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:

#0  select () from libc.so.6
#1  NtDelayExecution () from ntdll.so
#2  __wine_syscall_dispatcher () from ntdll.so
#3  call_window_proc () from win32u.so
#4  peek_message () from win32u.so
#5  NtUserPeekMessage () from win32u.so

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.

NtUserRedrawWindow is only reached through the is_iconic() branch, which confirms the window was minimized when FlashWindowEx was called (dlls/win32u/window.c, unchanged since the 2015 semi-stub commit f22760d210, moved to win32u in 2022):

    FIXME( "%p - semi-stub\n", info );

    if (is_iconic( info->hwnd ))
    {
        NtUserRedrawWindow( info->hwnd, 0, 0, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW | RDW_FRAME );
        ...
        user_driver->pFlashWindowEx( info );
        return TRUE;
    }
    else
    {
        ...
        if (!info->dwFlags || info->dwFlags & FLASHW_CAPTION)
            send_notify_message( hwnd, WM_NCACTIVATE, wparam, 0, 0 );   /* async in Proton */
        ...
    }

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 whole NtUserRedrawWindow call) in the iconic branch so the redraw is only queued and the owning thread paints on its next message-loop iteration, like Windows:

--- a/dlls/win32u/window.c
+++ b/dlls/win32u/window.c
@@
     if (is_iconic( info->hwnd ))
     {
-        NtUserRedrawWindow( info->hwnd, 0, 0, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW | RDW_FRAME );
+        NtUserRedrawWindow( info->hwnd, 0, 0, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME );

In the shipped win32u.so this is the immediate of mov $0x505,%ecx right before the call NtUserRedrawWindow in NtUserFlashWindowEx (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,+win if useful.

Workaround for users

Use borderless fullscreen (borderless_fullscreen = true in user_settings.config or the in-game Display Mode setting): the window is then never minimized on focus loss, so the is_iconic branch is never taken. Or simply don't alt-tab while a mission is loading.

Kkisak-valve maintainer 2026-09-06 github

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 versions

Launch options

Launch lines

Upstream links