diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 31529b49..ee83655d 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -52,6 +52,7 @@ Version 1.3.0 (Bakeapple): Not yet released - Optimize network performance for `af_obj_update` packets and bot decommission logic - Sync animation state for crouched players in first person spectate view - Sync rail scanner in first person spectate +- Fix Alt+Enter crash in Direct3D 11 renderer Version 1.2.2 (Willow): Released Jan-04-2026 -------------------------------- diff --git a/game_patch/graphics/d3d11/gr_d3d11.cpp b/game_patch/graphics/d3d11/gr_d3d11.cpp index b61df78c..b7128f79 100644 --- a/game_patch/graphics/d3d11/gr_d3d11.cpp +++ b/game_patch/graphics/d3d11/gr_d3d11.cpp @@ -220,6 +220,14 @@ namespace df::gr::d3d11 dxgi_factory->CreateSwapChain(device_, &sd, &swap_chain_) ); } + + // Alpine uses DXGI's flip presentation model, which requires ResizeBuffers + // after mode transitions + // Disable DXGI's built-in Alt+Enter fullscreen toggle. DXGI's auto-handling + // calls SetFullscreenState internally without the app's knowledge, which causes + // the next Present() call to return DXGI_ERROR_INVALID_CALL and crash. We handle + // Alt+Enter ourselves via WM_SYSKEYDOWN in the message handler. + dxgi_factory->MakeWindowAssociation(hwnd, DXGI_MWA_NO_ALT_ENTER); } void Renderer::init_back_buffer() diff --git a/game_patch/graphics/d3d11/gr_d3d11_hooks.cpp b/game_patch/graphics/d3d11/gr_d3d11_hooks.cpp index d29873f7..67992fb2 100644 --- a/game_patch/graphics/d3d11/gr_d3d11_hooks.cpp +++ b/game_patch/graphics/d3d11/gr_d3d11_hooks.cpp @@ -20,8 +20,13 @@ namespace df::gr::d3d11 { static std::optional renderer; + void update_window_mode(); + void msg_handler(UINT msg, WPARAM w_param, LPARAM l_param) { + if (!renderer) { + return; + } switch (msg) { case WM_ACTIVATEAPP: if (w_param) { @@ -32,6 +37,20 @@ namespace df::gr::d3d11 xlog::trace("inactive {:x} {:x}", w_param, l_param); renderer->window_inactive(); } + break; + case WM_SYSKEYDOWN: + // Handle Alt+Enter to toggle between fullscreen and windowed mode. + // DXGI's built-in Alt+Enter handling is disabled (MakeWindowAssociation with + // DXGI_MWA_NO_ALT_ENTER) to prevent it from calling SetFullscreenState internally, + // which would put the swap chain in an inconsistent state and crash on the next Present(). + if (w_param == VK_RETURN) { + auto new_mode = (rf::gr::screen.window_mode == rf::gr::FULLSCREEN) + ? rf::gr::WINDOWED + : rf::gr::FULLSCREEN; + rf::gr::screen.window_mode = new_mode; + update_window_mode(); + } + break; } }