Skip to content

feat(input): Enhance window management capabilities#2608

Draft
githubawn wants to merge 6 commits into
TheSuperHackers:mainfrom
githubawn:feat/fullscreen-toggle
Draft

feat(input): Enhance window management capabilities#2608
githubawn wants to merge 6 commits into
TheSuperHackers:mainfrom
githubawn:feat/fullscreen-toggle

Conversation

@githubawn

@githubawn githubawn commented Apr 16, 2026

Copy link
Copy Markdown

Alt+Enter Borderless Fullscreen & Live Resize Reflow Integration

Integrates Alt+Enter fullscreen / windowed toggling with live resizing during games.

Summary of Changes

  • Alt+Enter Toggle: Toggles borderless fullscreen matching native monitor bounds, persisting the setting to options.ini.
  • Startup Priority: Command-line parameters (-win/-nowin) override persistent preferences.
  • **Deferred Preferences Saving: Relocated preference-writing logic from the OS-level hook into performLiveResize(). Saving only occurs upon a successful D3D reset, ensuring options.ini is updated with both the window state and the new resolution.
  • ** Unblocked In-Game Options Menu: Removed the block in OptionsMenu.cpp that disabled the resolution dropdown during a match, allowing in-game resolution adjustments.

This change was generated with AI assistance. All generated code has been reviewed, tested, and verified for correctness.

todo, replicate to generals

@greptile-apps

greptile-apps Bot commented Apr 16, 2026

Copy link
Copy Markdown

Greptile Summary

This PR wires up Alt+Enter / F11 borderless-fullscreen toggling and live window-resize reflow for Zero Hour, including a g_windowLayoutRegistry that records each window's original creation geometry so reflowWindowTree can rescale it on demand, a deferred-resize mechanism that waits until the shell animation is done or gameplay is active, and a revamped recreateControlBar that preserves hide/science-panel state across reflows. It also seeds the windowed preference from options.ini before command-line parsing so -win/-nowin properly takes priority, and re-enables the in-game resolution dropdown.

  • WinMain.cpp carries the bulk of the change: toggleFullscreen, performLiveResize, checkAndApplyDeferredResize, and the InternalResizeGuard RAII helper; g_resizePending is cleared before the D3D reset attempt, so a failed reset leaves the window and D3D surface out of sync with no automatic retry.
  • GameWindowManagerScript.cpp introduces the g_windowLayoutRegistry global and the TempParsedRect side-channel between parseScreenRect and createWindow; both the zero-divisor guard and the key-repeat filter (lParam bit 30) from earlier review rounds are now in place.
  • dx8wrapper.cpp removes the long-standing TODO by routing Set_Device_Resolution through Set_Render_Device, enabling windowed-mode and bit-depth changes in a single call.

Confidence Score: 4/5

Safe to merge with low risk; the one real defect only manifests on a transient D3D reset failure and requires a second manual resize to recover.

The deferred-resize path clears g_resizePending before calling performLiveResize. If the D3D device reset inside fails, the Win32 window is already at the new size but the D3D surface stays at the old one, and nothing re-queues the attempt. Recovery requires the user to manually trigger another resize event. All three issues flagged in the previous review rounds (key-repeat, insert no-op, division by zero) are addressed in this revision.

GeneralsMD/Code/Main/WinMain.cpp — specifically the checkAndApplyDeferredResize / performLiveResize pair around the g_resizePending flag lifetime.

Important Files Changed

Filename Overview
GeneralsMD/Code/Main/WinMain.cpp Core of this PR: adds Alt+Enter / F11 fullscreen toggle, live-resize pipeline, and performLiveResize; has a real defect where g_resizePending is cleared before the D3D reset, and a no-op SetWindowLong(WS_EX_TOPMOST) call.
GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GameWindowManager.cpp Adds reflowWindowTree/reflowAllWindows and erases destroyed windows from g_windowLayoutRegistry; the earlier-flagged division-by-zero is guarded with createResX > 0 checks.
GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp Rewrites recreateControlBar to look up by ControlBarParent, preserve hide/science-visible state, re-apply player scheme, and guard against a missing window; logic looks sound.
GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GameWindowManagerScript.cpp Defines g_windowLayoutRegistry and populates it via g_tempParsedRect whenever a window is created by script; safe because parsing is single-threaded.
GeneralsMD/Code/GameEngine/Source/Common/GlobalData.cpp Adds command-line priority logic for m_windowed in parseGameDataDefinition; correctly saves the pre-parse value and restores it when -win/-nowin was specified.
GeneralsMD/Code/GameEngineDevice/Source/Win32Device/Common/Win32GameEngine.cpp Hooks checkAndApplyDeferredResize() into the game engine update loop via a function-body extern declaration; works but creates an opaque cross-file dependency.
Core/Libraries/Source/WWVegas/WW3D2/dx8wrapper.cpp Refactors Set_Device_Resolution to forward all parameters through Set_Render_Device, removing the TODO about unsupported windowed/bit-depth changes; straightforward delegation.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant User
    participant WndProc
    participant toggleFullscreen
    participant checkAndApplyDeferredResize
    participant performLiveResize
    participant TheDisplay
    participant reflowAllWindows
    participant InGameUI

    User->>WndProc: Alt+Enter / F11 / SC_MAXIMIZE
    WndProc->>toggleFullscreen: call
    toggleFullscreen->>toggleFullscreen: flip m_windowed
    toggleFullscreen->>WndProc: SetWindowPos (new size/style)
    WndProc->>WndProc: "WM_SIZE → g_resizePending = true"

    loop Every engine update
        WndProc->>checkAndApplyDeferredResize: checkAndApplyDeferredResize()
        checkAndApplyDeferredResize->>checkAndApplyDeferredResize: isResizeSafe()?
        alt safe
            checkAndApplyDeferredResize->>checkAndApplyDeferredResize: "g_resizePending = false"
            checkAndApplyDeferredResize->>performLiveResize: call
            performLiveResize->>TheDisplay: setDisplayMode(w, h, bpp, windowed)
            alt D3D reset succeeds
                TheDisplay-->>performLiveResize: true
                performLiveResize->>reflowAllWindows: reflowAllWindows(w, h)
                performLiveResize->>InGameUI: recreateControlBar()
                performLiveResize->>performLiveResize: optionPref.write()
            else D3D reset fails
                TheDisplay-->>performLiveResize: false
                Note over performLiveResize: window/D3D out of sync, no retry
            end
        else not safe
            Note over checkAndApplyDeferredResize: g_resizePending stays true
        end
    end
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant User
    participant WndProc
    participant toggleFullscreen
    participant checkAndApplyDeferredResize
    participant performLiveResize
    participant TheDisplay
    participant reflowAllWindows
    participant InGameUI

    User->>WndProc: Alt+Enter / F11 / SC_MAXIMIZE
    WndProc->>toggleFullscreen: call
    toggleFullscreen->>toggleFullscreen: flip m_windowed
    toggleFullscreen->>WndProc: SetWindowPos (new size/style)
    WndProc->>WndProc: "WM_SIZE → g_resizePending = true"

    loop Every engine update
        WndProc->>checkAndApplyDeferredResize: checkAndApplyDeferredResize()
        checkAndApplyDeferredResize->>checkAndApplyDeferredResize: isResizeSafe()?
        alt safe
            checkAndApplyDeferredResize->>checkAndApplyDeferredResize: "g_resizePending = false"
            checkAndApplyDeferredResize->>performLiveResize: call
            performLiveResize->>TheDisplay: setDisplayMode(w, h, bpp, windowed)
            alt D3D reset succeeds
                TheDisplay-->>performLiveResize: true
                performLiveResize->>reflowAllWindows: reflowAllWindows(w, h)
                performLiveResize->>InGameUI: recreateControlBar()
                performLiveResize->>performLiveResize: optionPref.write()
            else D3D reset fails
                TheDisplay-->>performLiveResize: false
                Note over performLiveResize: window/D3D out of sync, no retry
            end
        else not safe
            Note over checkAndApplyDeferredResize: g_resizePending stays true
        end
    end
Loading
Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
GeneralsMD/Code/Main/WinMain.cpp:411-418
**Deferred resize flag cleared before the attempt leaves resize unretried on D3D failure**

`g_resizePending` is set to `false` before `performLiveResize` is called. If `TheDisplay->setDisplayMode(...)` inside `performLiveResize` returns `false` (e.g., the D3D device is lost or a reset fails), the window has already been resized by `SetWindowPos` but the D3D surface stays at the previous dimensions. Because `g_resizePending` is now `false`, nothing re-triggers the resize — the mismatch persists until the user manually initiates another resize or toggle.

Consider clearing the flag only on a successful apply: have `performLiveResize` return a `bool` and conditionally reset `g_resizePending`.

Reviews (6): Last reviewed commit: "Fix windowed preference saving and preve..." | Re-trigger Greptile

Comment thread Core/GameEngine/Source/Common/OptionPreferences.cpp Outdated
Comment thread GeneralsMD/Code/Main/WinMain.cpp Outdated
@stephanmeesters

Copy link
Copy Markdown

Haven't read too closely but when the resolution is set to native display resolution, will alt+enter here switch between borderless and fullscreen? How is this typically implemented in games?

@githubawn

Copy link
Copy Markdown
Author

Currently it switches between exclusive and windowed that covers the entire screen. Have a small regression there as the titel bars uncenters the borderless window.

In modern games they don't let you set the fullscreen resolution anymore, the resolution setting only applies to windowed. The fullscreen resolution is always the desktop resolution.

Comment thread GeneralsMD/Code/Main/WinMain.cpp Outdated
@githubawn githubawn marked this pull request as draft April 16, 2026 17:52
@xezon xezon changed the title feat(input)/Add Alt+Enter support for fullscreen toggling feat(input): Add Alt+Enter support for fullscreen toggling Apr 18, 2026
@githubawn githubawn changed the title feat(input): Add Alt+Enter support for fullscreen toggling feat(input): Enhance window management capabilities Jun 27, 2026
@githubawn githubawn force-pushed the feat/fullscreen-toggle branch 2 times, most recently from f4fca7f to 4d29ea6 Compare June 27, 2026 18:58
@githubawn githubawn marked this pull request as ready for review June 27, 2026 18:58
@githubawn githubawn marked this pull request as draft June 27, 2026 18:59
@githubawn githubawn force-pushed the feat/fullscreen-toggle branch from 4d29ea6 to 76bd972 Compare June 27, 2026 19:05
Comment thread GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GameWindowManager.cpp Outdated
@githubawn

Copy link
Copy Markdown
Author

@greptile review

Comment on lines +411 to +418
void checkAndApplyDeferredResize()
{
if (g_resizePending && isResizeSafe())
{
g_resizePending = false;
performLiveResize(ApplicationHWnd);
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Deferred resize flag cleared before the attempt leaves resize unretried on D3D failure

g_resizePending is set to false before performLiveResize is called. If TheDisplay->setDisplayMode(...) inside performLiveResize returns false (e.g., the D3D device is lost or a reset fails), the window has already been resized by SetWindowPos but the D3D surface stays at the previous dimensions. Because g_resizePending is now false, nothing re-triggers the resize — the mismatch persists until the user manually initiates another resize or toggle.

Consider clearing the flag only on a successful apply: have performLiveResize return a bool and conditionally reset g_resizePending.

Prompt To Fix With AI
This is a comment left during a code review.
Path: GeneralsMD/Code/Main/WinMain.cpp
Line: 411-418

Comment:
**Deferred resize flag cleared before the attempt leaves resize unretried on D3D failure**

`g_resizePending` is set to `false` before `performLiveResize` is called. If `TheDisplay->setDisplayMode(...)` inside `performLiveResize` returns `false` (e.g., the D3D device is lost or a reset fails), the window has already been resized by `SetWindowPos` but the D3D surface stays at the previous dimensions. Because `g_resizePending` is now `false`, nothing re-triggers the resize — the mismatch persists until the user manually initiates another resize or toggle.

Consider clearing the flag only on a successful apply: have `performLiveResize` return a `bool` and conditionally reset `g_resizePending`.

How can I resolve this? If you propose a fix, please make it concise.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants