Repository navigation
shell: a process app may refuse a close and ask first (#179) - #187
Merged
Merged
Conversation
A process client's close sent CloseRequested and Kill back to back, so closing a process Terminal's tile (the macOS default hosting) still ended its running jobs without asking. Stacked on #186, which did the same for in-process modules. - process_close.rs: a pure close state machine per process client. An app that says WmRequest::AsksBeforeClose (makepad feat/wm-close-refused) is asked with CloseRequested alone; its tile and process stay. Its answer is its own WmRequest::Close (yes: closed at once, without asking back), CloseRefused (it is asking the person: no deadline, the tile comes to the front) or an exit. - Fallback for an app that does not answer: a second close of the same tile while the first is unanswered ends it at once (a repeat within 0.5 s is the same click); left alone it is ended after 5 s. A refusal is never a licence to hang: every close asks again, a live app re-answers at once, a frozen one falls to the fallback. So an app that is visibly asking is never killed, and a hung one is always closable. - An app that never declared closes exactly as before (CloseRequested + Kill, the tile leaves at once), so apps without the new words, and a Terminal from an older makepad, lose nothing. - Shell quit (menu, Cmd+Q, the window's close, /quit) asks declared apps the same way and waits while any module or app is deciding; it goes when the last says yes. A second quit forces unanswered apps. A termination signal or a crash still asks nobody. - The words are read off the wire by their exact text, so this works with the current makepad pin; on_wm_request gains a catch-all arm so a repin to the new variants compiles. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Sep 30, 2026
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.
Closes the last gap in #179. Stacked on #186, which covers in-process modules; the makepad side is OctoSense-org/makepad#68.
The gap: on macOS the Terminal runs as a separate process by default. Closing its tile made
request_closesendCloseRequestedandKillback to back, so running jobs ended without asking.The close state machine (
crates/shell/src/process_close.rs, pure and unit-tested)Each process client is in one of three states: Idle, Awaiting (asked, no answer yet) or Asking (it refused and is asking the person).
AsksBeforeCloseCloseRequested+Kill, the tile leaves at once, as todayCloseRequestedalone, keep the tile and bring it to the front, go to AwaitingCloseRefusedwhile AwaitingWmRequest::CloseANSWER_TIMEOUT)Shell quit (menu Quit, Cmd+Q, the window's close button,
/quit) asks every declared app the same way, together with #186's modules. It waits while any module or app is deciding and goes once the last one says yes (process_close::take_quit_ready). A second quit forces apps that have not answered. A close of one app drops a waiting quit, as in #186. A termination signal or a crash of the shell asks nobody.Fallback policy, and why
AsksBeforeClose) and answers every ask (CloseRefusedorClose).CloseRefusedhas no deadline. Every new close asks again: a live Terminal re-answers at once, a frozen one does not and falls to the timeout or to a second close. So a hung app is always closable, and an app visibly showing its question never is.on_wm_requestgains an#[allow(unreachable_patterns)] _ => {}arm, so the repin to makepad#68 compiles unchanged.Found on the way: under the hosted runtime
cx.quit()does nothing, so a Terminal that says yes cannot end itself. makepad#68 makes it sendWmRequest::Close. With the current pin, a confirmed close falls to the 5 s timeout; this was seen end to end before that fix.Tests
cd phone && cargo test --locked --features mobile-apps -p octosense-shell: 607 passed. 12 are new inprocess_close: acks and exits, refuses and stays, never acks and times out, second close forces, a repeat within 0.5 s waits, an asking app is re-asked, shell quit waits for every app, quit waits on modules and apps together, a no holds the quit and a close abandons it, a hung app under quit, an undeclared app keeps the legacy path, wire parsing.cargo check --locked -p octosense: clean.python3 tools/setup.py --check --cargo: passes.End to end
A hidden release desktop with temporary
OCTOSENSE_HOME,OCTOSENSE_APP_DATAandOCTOS_APP_CORE_DIR,MAKEPAD_REMOTE, and a process Terminal (the default hosting). makepad#68 was applied to.sources/makepadfor the run and restored afterwards. Driven by remote input only; every step passed.sleep 300, close the tile: the Terminal's bar asks Closing the terminal ends "sleep". Close anyway? Esc, then 7 s (past the timeout): the tile, the Terminal andsleepare all still there./quit: the log saysquit waits: … apps [1] are answeringand the prompt shows. After 7 s the shell, the Terminal andsleepare still running. Esc.sleepare gone in about 0.5 s. The shell keeps running, because the close dropped the waiting quit.closed again before it answered; ending it).did not answer its close in 5s: the 5 s timeout plus the 1 s reaper tick)./quitwith a job-free Terminal: the shell exits in 0.07 s and no Terminal is left.Screenshots are local to the test session (grabs of the hidden window):
01-sleep-running,02-close-prompt,03-after-esc,04-quit-prompt,05-close-again-prompt,06-closed,07-stuck-first-close,08-stuck-forced.Not verified: an undeclared process app (none ships on the macOS desktop besides the Terminal). Its path is the unchanged legacy code, covered by the unit test.
🤖 Generated with Claude Code