Skip to content

feature(gamelogic): Implement ALT + F4 and Window X button to close game#2336

Draft
githubawn wants to merge 7 commits intoTheSuperHackers:mainfrom
githubawn:fix/graceful-exit-logic
Draft

feature(gamelogic): Implement ALT + F4 and Window X button to close game#2336
githubawn wants to merge 7 commits intoTheSuperHackers:mainfrom
githubawn:fix/graceful-exit-logic

Conversation

@githubawn
Copy link

@githubawn githubawn commented Feb 21, 2026

Fixes #1356

Split up in 2 commits.

  1. Previously, the quit logic was duplicated and inconsistent across several places (the quit menu, the "quit to desktop" button, and the Alt+F4 handler). This commit centralizes all of that into a single GameLogic::quit(Bool toDesktop) method. It properly handles edge cases like stopping an active recording, sending a self-destruct message in multiplayer (using a new m_quitToDesktopAfterMatch flag), unpausing/unfreezing the game before exiting. All the old scattered quit code is replaced with a simple call to TheGameLogic->quit(true/false).

  2. This follow-up extends the graceful exit to two more situations where the game previously couldn't be closed cleanly:
    During the loading screen: The load progress update now checks if a quit has been requested and throws a QuitGameException, which is caught in startNewGame() to cleanly abort the loading process mid-way.
    During movies: The inline ESC-key checking that was copy-pasted in multiple video playback loops is refactored into a new GameClient::isMovieAbortRequested() method. This method now also checks for window close / Alt+F4 events (not just ESC), so closing the window during a movie no longer gets stuck. The MSG_META_DEMO_INSTANT_QUIT message is also registered as a system message so it propagates correctly even during loading.

@greptile-apps
Copy link

greptile-apps bot commented Feb 21, 2026

Greptile Summary

Implemented comprehensive graceful exit handling for Alt+F4 and window close events across all game states (menu, loading, movies, gameplay). Centralized previously duplicated quit logic into a single GameLogic::quit(Bool toDesktop) method that properly handles edge cases including stopping active recordings, sending multiplayer self-destruct messages, and unpausing/unfreezing the game.

Key Changes:

  • Centralized quit logic eliminates code duplication across quit menu, Alt+F4 handler, and instant quit message
  • Added exception-based abort (QuitGameException) for clean exit during loading screens
  • Refactored inline ESC-key checks in movie loops into GameClient::isMovieAbortRequested() method that also detects window close/Alt+F4
  • Moved MSG_META_DEMO_INSTANT_QUIT out of debug-only section and registered as system message for proper propagation during loading
  • Added m_quitToDesktopAfterMatch flag for deferred quit in multiplayer (sends self-destruct, then exits after match cleanup)
  • Consistent implementation across both Generals and GeneralsMD codebases

Code Quality:

  • Well-structured refactoring that reduces duplication and improves maintainability
  • Proper cleanup sequence: stop recording → unpause → unfreeze → exit/clear data
  • Exception handling is used appropriately for control flow during loading abort
  • All edge cases appear to be handled correctly

Confidence Score: 5/5

  • This PR is safe to merge with high confidence
  • The implementation demonstrates excellent software engineering with proper refactoring that eliminates code duplication while adding important functionality. The quit logic is properly centralized, all edge cases are handled (recording cleanup, multiplayer self-destruct, pause/freeze state), and the implementation is consistent across both Generals and GeneralsMD codebases. The use of exceptions for loading abort is appropriate, and the deferred quit pattern for multiplayer is well-designed. No logical errors, security issues, or bugs were identified.
  • No files require special attention

Important Files Changed

Filename Overview
Generals/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp Implemented centralized quit(Bool toDesktop) method with proper handling for multiplayer self-destruct, recording cleanup, and game state management. Added exception-based abort for loading screen.
GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp Identical implementation to Generals version - centralized quit logic with proper cleanup and exception-based loading abort.
Generals/Code/GameEngine/Source/GameClient/GameClient.cpp Added isMovieAbortRequested() helper consolidating ESC, window close, and Alt+F4 detection. Used in movie playback loops to enable graceful exit.
GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp Identical implementation to Generals version - movie abort helper with ESC and window close detection.
Core/GameEngine/Source/GameClient/GUI/LoadScreen.cpp Refactored movie loops to use isMovieAbortRequested() and added quit checks during loading progress updates. Enables graceful exit during loading screens.
Generals/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp Moved MSG_META_DEMO_INSTANT_QUIT out of debug-only section, registered it as system message, and replaced duplicated quit code with quit(TRUE) call.
GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp Removed ALLOW_ALT_F4 ifdef guard, registered MSG_META_DEMO_INSTANT_QUIT as system message, replaced duplicated quit code with centralized call.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[User Action: Alt+F4 / Window X / Quit Menu] --> B{Check Current State}
    B -->|In Movie| C[isMovieAbortRequested checks]
    B -->|In Loading| D[LoadScreen.update checks]
    B -->|In Menu| E[QuitMenu callback]
    
    C --> F[GameClient::isMovieAbortRequested]
    D --> G[GameLogic::updateLoadProgress]
    E --> H[GameLogic::quit]
    
    F -->|ESC / Window Close / Alt+F4| H
    G -->|Quitting detected| I[Throw QuitGameException]
    I --> J[Caught in startNewGame]
    
    H --> K{toDesktop parameter}
    K -->|TRUE| L{isInGame?}
    K -->|FALSE| M[exitGame]
    
    L -->|Yes| N{isMultiplayer?}
    L -->|No| O[Set quitting flag]
    
    N -->|Yes| P[Send self-destruct message]
    N -->|No| Q[clearGameData immediately]
    
    P --> R[Set m_quitToDesktopAfterMatch]
    R --> S[exitGame - defer quit]
    S --> T[clearGameData detects flag]
    T --> O
    
    Q --> O
    M --> U[setClientQuiet]
    O --> U
    U --> V[Game exits gracefully]
Loading

Last reviewed commit: 6b28aed

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

8 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

@xezon
Copy link

xezon commented Feb 21, 2026

Needs rebase.

My initial thought on this, without looking at code, is that it will make it easier for players to disconnect from a Multiplayer match - by accident or intentional - so this feature must not work during a Multiplayer Match.

@githubawn
Copy link
Author

Will rebase soon.

The multiplayer behavior was designed based on your comment from #1356 (comment).
Rather than blocking Alt+F4 entirely, it intercepts the OS close command to ensure a MSG_SELF_DESTRUCT (Surrender) message is sent over the network before the game quits, so other players aren't left hanging. Happy to adjust if your thinking has changed since then!

It also adds safe abort logic so Alt+F4 works consistently everywhere addressing helmutbuhler's #1356 (comment) concern about the button only working in some situations.

@Caball009
Copy link

Caball009 commented Feb 21, 2026

Thank you for implementing this. I did a few quick tests and the current implementation appears to work well.

@githubawn githubawn force-pushed the fix/graceful-exit-logic branch from 843b370 to 4fda496 Compare February 22, 2026 00:47
@githubawn githubawn force-pushed the fix/graceful-exit-logic branch from 2d896d5 to 036ed84 Compare February 22, 2026 23:35
Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

16 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Copy link

@xezon xezon left a comment

Choose a reason for hiding this comment

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

It would have been easier to review if this change just contained Core+GeneralsMD edits for starters.

void GameLogic::startNewGame( Bool saveGame )
{
try
{
Copy link

Choose a reason for hiding this comment

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

Perhaps do

void GameLogic::startNewGame( Bool saveGame )
{
  try
  {
    tryStartNewGame(saveGame);
  }
  catch ..
  {
  }
}

{
if (isInMultiplayerGame() && !isInSkirmishGame() && TheGameInfo && !TheGameInfo->isSandbox())
{
GameMessage *msg = TheMessageStream->appendMessage(GameMessage::MSG_SELF_DESTRUCT);
Copy link

Choose a reason for hiding this comment

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

Any chance that this can cause player frustration when they accidentally press the X button during a match, for example when they do not use the Mouse Cursor Capture feature?

Copy link
Author

@githubawn githubawn Feb 25, 2026

Choose a reason for hiding this comment

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

Good point, I didn't know about players with disabled mouse capture. I'm considering something like adding a double click + notification.

Choose a reason for hiding this comment

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

if (!TheInGameUI->isQuitMenuVisible())
{
  ToggleQuitMenu();
}
else
{
  // append self destruct msg
}

Would that be enough?

Copy link

Choose a reason for hiding this comment

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

So the user would need to click the X twice then?

Copy link

@Caball009 Caball009 Feb 28, 2026

Choose a reason for hiding this comment

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

Yes, unless they already had the menu open, not sure if that's still somewhat undesirable. I think it's easier to implement that than to check for a repeated WM_CLOSE message if that was the idea so far.

@githubawn githubawn marked this pull request as draft February 26, 2026 23:25
@githubawn githubawn force-pushed the fix/graceful-exit-logic branch from 3a3c926 to 6f2b0b8 Compare February 27, 2026 02:30
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.

Implement ALT + F4 and Window X button to close game

4 participants