Summary
On macOS, SDL_QUIT is turned into an immediate Sys_Quit with no confirmation, which makes it easy to quit a game mid-session by accident. Retail Half-Life (and the engine's own menu) shows a quit-confirmation dialog, but the SDL_QUIT path bypasses it.
Environment
- OS: macOS (Apple Silicon / arm64)
- Engine: Xash3D FWGS 0.21, build 4127, commit
009855c, apple-arm64 (continuous build)
- Game:
valve (Half-Life 1), local listen server
What we observed
While playing on a local listen server, the app exited cleanly in the middle of the session. With -log enabled, engine.log recorded:
Note: Issuing host shutdown due to reason "caught SDL_QUIT"
...
Stopped with reason "caught SDL_QUIT" at ...
This is a clean shutdown, not a crash — there is no Crash: signal ... line from the POSIX crash handler and no macOS crash report (.ips) is generated. So the engine received a normal SDL_QUIT and exited immediately.
Source
In engine/platform/sdl2/host_sdl2.c the quit event is handled unconditionally:
case SDL_QUIT:
Sys_Quit( "caught SDL_QUIT" );
break;
SDL_WINDOWEVENT_CLOSE is not handled in the SDL_WINDOWEVENT switch. Meanwhile the in-game UI already has a quit confirmation (GameUI_QuitConfirmationTitle / GameUI_QuitConfirmationText), but it is only reached via the menu — an OS-level SDL_QUIT skips it.
On macOS the default SDL2 Cocoa app menu exposes ⌘Q → SDL_QUIT (and ⌘W on the window), so an accidental modifier press during gameplay can terminate the match instantly with no prompt. On a Mac keyboard the Command key sits right next to the keys players use for movement/crouch, which makes this easy to trigger unintentionally.
Question / suggestion
Would you be open to gating SDL_QUIT behind the existing quit-confirmation when a game/map is active (i.e. Host_IsLocal() / an active client), while keeping immediate quit at the menu or via an "already confirmed" path? That would match retail Half-Life behavior and avoid accidental mid-game exits, without changing dedicated-server or menu behavior.
Happy to test any patch on macOS arm64. If this is intended behavior (or better solved by running fullscreen), feel free to close — just wanted to document it since engine.log clearly shows it's a clean quit rather than the crash it looks like in the moment.
Summary
On macOS,
SDL_QUITis turned into an immediateSys_Quitwith no confirmation, which makes it easy to quit a game mid-session by accident. Retail Half-Life (and the engine's own menu) shows a quit-confirmation dialog, but theSDL_QUITpath bypasses it.Environment
009855c,apple-arm64(continuous build)valve(Half-Life 1), local listen serverWhat we observed
While playing on a local listen server, the app exited cleanly in the middle of the session. With
-logenabled,engine.logrecorded:This is a clean shutdown, not a crash — there is no
Crash: signal ...line from the POSIX crash handler and no macOS crash report (.ips) is generated. So the engine received a normalSDL_QUITand exited immediately.Source
In
engine/platform/sdl2/host_sdl2.cthe quit event is handled unconditionally:SDL_WINDOWEVENT_CLOSEis not handled in theSDL_WINDOWEVENTswitch. Meanwhile the in-game UI already has a quit confirmation (GameUI_QuitConfirmationTitle/GameUI_QuitConfirmationText), but it is only reached via the menu — an OS-levelSDL_QUITskips it.On macOS the default SDL2 Cocoa app menu exposes ⌘Q →
SDL_QUIT(and ⌘W on the window), so an accidental modifier press during gameplay can terminate the match instantly with no prompt. On a Mac keyboard the Command key sits right next to the keys players use for movement/crouch, which makes this easy to trigger unintentionally.Question / suggestion
Would you be open to gating
SDL_QUITbehind the existing quit-confirmation when a game/map is active (i.e.Host_IsLocal()/ an active client), while keeping immediate quit at the menu or via an "already confirmed" path? That would match retail Half-Life behavior and avoid accidental mid-game exits, without changing dedicated-server or menu behavior.Happy to test any patch on macOS arm64. If this is intended behavior (or better solved by running fullscreen), feel free to close — just wanted to document it since
engine.logclearly shows it's a clean quit rather than the crash it looks like in the moment.