Repository navigation
feat: OnClose, a handler for the window's close request - #17
Merged
Merged
Conversation
Closing the window (its X button, Alt+F4, the taskbar, a launcher such as Steam asking the game to exit) ended the game at once, so a game had no chance to save progress or bank a run in progress. g.OnClose(fn) takes over the close request (ebiten SetWindowClosingHandled): when the window is asked to close, fn runs once on the game loop, between two frames, then the game ends the way Quit does (the next Update returns ebiten.Termination, Run returns and saves the COLLIDER_RECORD GIF). No frame runs after the handler. There is no veto: launchers and the OS expect a close request to end the game, and a confirm dialog on close is not what a small API needs. fn never runs on Quit (the game's own decision), headless (no window), or in a browser (a tab closes its page; ebiten reports no close request there, so it is a no-op: save as you go). OnClose(nil) restores the default. The window hooks are swappable variables so the tests fake a close request through runner.Update. The catcher example keeps a record in the making when the window closes mid-round; README cheat-sheet row and contract item 8 updated.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
g.OnClose(fn func()): runfnonce when the player closes the window (X button, Alt+F4, the taskbar's Close, a launcher such as Steam asking the game to exit), then end the game likeQuit. Until now a close request ended the game at once, with no chance to save progress or bank a run in progress.Design choices
ebiten.SetWindowClosingHandled(true)+ebiten.IsWindowBeingClosed().OnClosesets the flag (OnClose(nil)gives the close button back to the window); the runner checks for a close request at the start ofUpdate.Updatereturnsebiten.Termination,Runreturns and saves theCOLLIDER_RECORDGIF, the same path asQuit.Quit(the game's own decision), not headless (no window:StepandCOLLIDER_AGENT=mcpnever see it), and a no-op in a browser: a tab closes its page and ebiten reports no close request there, so games save as they go (localStorage writes are immediate). Autopilot andmcp-windoware real windows: the handler runs.windowBeingClosed/setWindowClosingHandledare swappable variables so tests fake the window.Proof
close_test.go: the handler runs once, itsSavelands, no frame runs after it, laterUpdates stayTermination;Quitdoes not run it;OnClose(nil)restores the default; headless never runs it.examples/catcher: closing the window mid-round keeps a new best (g.OnClosesaves it); its README explains the pattern.OnClosewriting a file, closed byWM_CLOSE(Process.CloseMainWindow): the handler ran after 135 frames, thenRunreturned and the process exited.