fix(windowed): #88 don't force-quit windowed games on Esc or 'q'
cocoa.ll win_poll hard-coded Escape (keycode 53) and 'q' as quit (W_running=0), so a shipped windowed game died the instant a player pressed Esc (pause) or typed 'q'. Remove the dev-loop quit keys for windowed builds: Escape is delivered as key 27 and 'q' is an ordinary key, consistently across the single per-frame @W_key and the #50 held-key set (ev_keyval maps Esc->27, not 'q'). A windowed game owns Esc/pause and quits via quit() or the window close button (still ends the run). The headless rt_poll keeps its own 'q'=quit for scripted golden tests, so nothing headless changes. Also stops forwarding consumed key events to -sendEvent:, which rang AppKit's system beep per key. Windowed-only behavior (not exercised by the headless suite); verified a windowed build links and assembles cleanly. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
57b8747c31
commit
bdf1a97550
2 changed files with 23 additions and 10 deletions
3
changes/windowed-quit-keys.md
Normal file
3
changes/windowed-quit-keys.md
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
bump: patch
|
||||||
|
type: fix
|
||||||
|
Windowed games no longer force-quit on Esc or 'q' (#88). The macOS platform layer (`runtime/native/cocoa.ll` `win_poll`) used to hard-code Escape (keycode 53) and the character 'q' as *quit* — storing `W_running = 0` so a shipped windowed game died the instant a player pressed Esc (a universal pause key) or typed 'q'. Those dev-loop conveniences are removed for windowed builds: **Escape is delivered to the game as key 27** and **'q' is an ordinary key**, consistently across both the single per-frame key (`@W_key`) and the `#50` held-key set (`ev_keyval` now maps Escape→27, not 'q'). A windowed game now owns Esc/pause and shuts down via `quit()` or the window close button (which still ends the run). The headless test driver (`rt_poll` in `core.ludic`) keeps its own `'q'` = quit for scripted golden runs, so nothing headless changes. Also stops forwarding consumed key events to `-sendEvent:`, which was triggering AppKit's system "funk" beep on every keystroke.
|
||||||
|
|
@ -305,7 +305,9 @@ ret:
|
||||||
}
|
}
|
||||||
|
|
||||||
; #50 — the ASCII value an NSEvent key event maps to (same mapping as win_poll's
|
; #50 — the ASCII value an NSEvent key event maps to (same mapping as win_poll's
|
||||||
; keyDown switch: arrows -> WASD, return, escape -> 'q', else the first character).
|
; keyDown switch: arrows -> WASD, return, escape -> 27, else the first character).
|
||||||
|
; #88: escape delivers key 27 (left to the game), not 'q' — so the held-key set
|
||||||
|
; stays consistent with @W_key and a shipped game owns Esc (pause).
|
||||||
define i32 @ev_keyval(ptr %ev) {
|
define i32 @ev_keyval(ptr %ev) {
|
||||||
entry:
|
entry:
|
||||||
%sel_kc = call ptr @sel_registerName(ptr @.s_keycd)
|
%sel_kc = call ptr @sel_registerName(ptr @.s_keycd)
|
||||||
|
|
@ -326,7 +328,7 @@ va: ret i32 97
|
||||||
vd: ret i32 100
|
vd: ret i32 100
|
||||||
vspace: ret i32 32
|
vspace: ret i32 32
|
||||||
vret: ret i32 10
|
vret: ret i32 10
|
||||||
vesc: ret i32 113
|
vesc: ret i32 27
|
||||||
chars:
|
chars:
|
||||||
%sel_ch = call ptr @sel_registerName(ptr @.s_chars)
|
%sel_ch = call ptr @sel_registerName(ptr @.s_chars)
|
||||||
%sel_len = call ptr @sel_registerName(ptr @.s_length)
|
%sel_len = call ptr @sel_registerName(ptr @.s_length)
|
||||||
|
|
@ -344,8 +346,9 @@ none:
|
||||||
}
|
}
|
||||||
|
|
||||||
; Drain the event queue, remembering the last key pressed. Arrow keys map onto
|
; Drain the event queue, remembering the last key pressed. Arrow keys map onto
|
||||||
; WASD and escape onto 'q', matching what the C backend did. #50: also track the
|
; WASD; escape delivers key 27 and 'q' is an ordinary key (#88 — a windowed game
|
||||||
; held-key set (keyDown/keyUp) and the mouse (buttons, position, wheel).
|
; owns Esc/quit; the headless rt_poll keeps its own 'q' = quit for scripted tests).
|
||||||
|
; #50: also track the held-key set (keyDown/keyUp) and the mouse.
|
||||||
define i32 @win_poll() {
|
define i32 @win_poll() {
|
||||||
entry:
|
entry:
|
||||||
store i32 0, ptr @W_key
|
store i32 0, ptr @W_key
|
||||||
|
|
@ -446,8 +449,9 @@ k_d: store i32 100, ptr @W_key br label %forward
|
||||||
k_space: store i32 32, ptr @W_key br label %forward
|
k_space: store i32 32, ptr @W_key br label %forward
|
||||||
k_ret: store i32 10, ptr @W_key br label %forward
|
k_ret: store i32 10, ptr @W_key br label %forward
|
||||||
k_esc:
|
k_esc:
|
||||||
store i32 113, ptr @W_key
|
; Escape is delivered as key 27 and left to the game (pause menus etc.).
|
||||||
store i32 0, ptr @W_running
|
; It used to force-quit the window, which a shipped game cannot tolerate.
|
||||||
|
store i32 27, ptr @W_key
|
||||||
br label %forward
|
br label %forward
|
||||||
fromchars:
|
fromchars:
|
||||||
%chars = call ptr (ptr, ptr) @objc_msgSend(ptr %ev, ptr %sel_ch)
|
%chars = call ptr (ptr, ptr) @objc_msgSend(ptr %ev, ptr %sel_ch)
|
||||||
|
|
@ -458,12 +462,18 @@ takechar:
|
||||||
%ch = call i16 (ptr, ptr, i64) @objc_msgSend(ptr %chars, ptr %sel_cat, i64 0)
|
%ch = call i16 (ptr, ptr, i64) @objc_msgSend(ptr %chars, ptr %sel_cat, i64 0)
|
||||||
%ch32 = zext i16 %ch to i32
|
%ch32 = zext i16 %ch to i32
|
||||||
store i32 %ch32, ptr @W_key
|
store i32 %ch32, ptr @W_key
|
||||||
%isq = icmp eq i32 %ch32, 113
|
; 'q' is an ordinary key in a windowed game (the headless test driver keeps its
|
||||||
br i1 %isq, label %quit, label %forward
|
; own 'q' = quit in rt_poll); a shipped game must not die on a typo.
|
||||||
quit:
|
|
||||||
store i32 0, ptr @W_running
|
|
||||||
br label %forward
|
br label %forward
|
||||||
forward:
|
forward:
|
||||||
|
; key events are consumed by win_poll itself; forwarding them to -sendEvent:
|
||||||
|
; hits an unhandled responder chain and AppKit plays the system "funk" beep.
|
||||||
|
; Mouse / other events are still forwarded so the window behaves normally.
|
||||||
|
%fwd_isdown = icmp eq i64 %ty, 10
|
||||||
|
%fwd_isup = icmp eq i64 %ty, 11
|
||||||
|
%fwd_iskey = or i1 %fwd_isdown, %fwd_isup
|
||||||
|
br i1 %fwd_iskey, label %pump, label %dosend
|
||||||
|
dosend:
|
||||||
%fwd = call ptr (ptr, ptr, ptr) @objc_msgSend(ptr %app, ptr %sel_send, ptr %ev)
|
%fwd = call ptr (ptr, ptr, ptr) @objc_msgSend(ptr %app, ptr %sel_send, ptr %ev)
|
||||||
br label %pump
|
br label %pump
|
||||||
finish:
|
finish:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue