Skip to content

Commit ae4ebab

Browse files
committed
untested Ctrl+C & D fixes
1 parent 0169811 commit ae4ebab

5 files changed

Lines changed: 30 additions & 41 deletions

File tree

gambaterm/blessed_keyboard_input.py

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ class KeyboardState:
2020

2121
pressed: set[DomCode] = field(default_factory=set)
2222
cpr_received: bool = False
23+
keystrokes: list = field(default_factory=list)
2324

2425

2526
# Blessed synthesizes key_name as "KEY_{char}" for A-Z and 0-9 on release
@@ -72,25 +73,12 @@ def blessed_key_pressed_context(
7273

7374
def get_pressed() -> KeyboardState:
7475
state.cpr_received = False
76+
state.keystrokes.clear()
7577
while True:
7678
key = term.inkey(timeout=0)
7779
if not key:
7880
break
79-
# Ctrl+C
80-
ctrl = key.modifiers_bits & 4
81-
if (
82-
str(key) == "\x03"
83-
or key.key_name == "KEY_CTRL_C"
84-
or (ctrl and key.key_value == "c")
85-
):
86-
raise KeyboardInterrupt
87-
# Ctrl+D
88-
if (
89-
str(key) == "\x04"
90-
or key.key_name == "KEY_CTRL_D"
91-
or (ctrl and key.key_value == "d")
92-
):
93-
raise OSError
81+
state.keystrokes.append(key)
9482
# Cursor position response
9583
if _CPR_RE.match(str(key)):
9684
state.cpr_received = True

gambaterm/keyboard_input.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ def __call__(self) -> set[Console.Input]:
9595
result = self._get_pressed()
9696
if isinstance(result, KeyboardState):
9797
self.cpr_state.cpr_received = result.cpr_received
98+
self.cpr_state.keystrokes = result.keystrokes
9899
new_pressed = set(result.pressed)
99100
else:
100101
self.cpr_state.cpr_received = False

gambaterm/pynput_keyboard_input.py

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
from __future__ import annotations
22

33
import sys
4-
import threading
54
from contextlib import contextmanager
65
from typing import Callable, Iterator, TYPE_CHECKING
76

@@ -38,9 +37,6 @@ def get_value_from_pynput_key_code(key: pynput.keyboard.KeyCode) -> DomCode | No
3837
def pynput_key_pressed_context() -> Iterator[Callable[[], set[DomCode]]]:
3938
import pynput.keyboard
4039

41-
ctrl_c_event = threading.Event()
42-
ctrl_d_event = threading.Event()
43-
4440
def on_press(key: pynput.keyboard.Key | pynput.keyboard.KeyCode | None) -> None:
4541
value: DomCode | None
4642
if isinstance(key, pynput.keyboard.KeyCode):
@@ -51,13 +47,6 @@ def on_press(key: pynput.keyboard.Key | pynput.keyboard.KeyCode | None) -> None:
5147
return
5248
if value is not None:
5349
pressed.add(value)
54-
# Check for Ctrl+C / Ctrl+D
55-
ctrl_held = DomCode.CONTROL_LEFT in pressed or DomCode.CONTROL_RIGHT in pressed
56-
if ctrl_held:
57-
if DomCode.US_C in pressed:
58-
ctrl_c_event.set()
59-
elif DomCode.US_D in pressed:
60-
ctrl_d_event.set()
6150

6251
def on_release(key: pynput.keyboard.Key | pynput.keyboard.KeyCode | None) -> None:
6352
value: DomCode | None
@@ -70,18 +59,11 @@ def on_release(key: pynput.keyboard.Key | pynput.keyboard.KeyCode | None) -> Non
7059
if value is not None:
7160
pressed.discard(value)
7261

73-
def get_pressed() -> set[DomCode]:
74-
if ctrl_c_event.is_set():
75-
raise KeyboardInterrupt
76-
if ctrl_d_event.is_set():
77-
raise OSError
78-
return pressed
79-
8062
pressed: set[DomCode] = set()
8163
listener = pynput.keyboard.Listener(on_press=on_press, on_release=on_release)
8264
try:
8365
listener.start()
84-
yield get_pressed
66+
yield lambda: pressed
8567
finally:
8668
pressed.clear()
8769
listener.stop()

gambaterm/run.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,31 @@ def run(
116116
if audio_out:
117117
audio_out.send(audio[:samples, :])
118118

119+
# Read keys for ctrl-c/ctrl-d detection.
120+
# We check for raw \x03 and \x04 bytes rather than physical key codes
121+
# so that detection is keyboard-layout agnostic: e.g. a Bépo user
122+
# presses Ctrl+C (physically Ctrl+H on US layout) and the terminal
123+
# translates it to \x03 (ETX) before putting it on stdin.
124+
# For blessed backend: keystrokes were collected during get_input()
125+
if isinstance(get_input, GameInputGetter) and get_input.cpr_state.keystrokes:
126+
for key in get_input.cpr_state.keystrokes:
127+
if str(key) == "\x03":
128+
raise KeyboardInterrupt
129+
if str(key) == "\x04":
130+
raise OSError
131+
# For X11/pynput backends: game input comes from X11 events / OS key
132+
# hooks (not stdin), so we read stdin here solely for ctrl-c/ctrl-d.
133+
# This replaces prompt-toolkit's read_keys().
134+
else:
135+
while True:
136+
key = term.inkey(timeout=0)
137+
if not key:
138+
break
139+
if str(key) == "\x03":
140+
raise KeyboardInterrupt
141+
if str(key) == "\x04":
142+
raise OSError
143+
119144
# Check for CPR response (set by keyboard handler during get_input)
120145
if use_cpr_sync and cpr_state is not None and cpr_state.cpr_received:
121146
screen_ready = True

gambaterm/x11_keyboard_input.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -93,13 +93,6 @@ def get_pressed() -> set[DomCode]:
9393
if is_key_released:
9494
pressed.discard(key)
9595

96-
# Check for Ctrl+C / Ctrl+D
97-
if DomCode.CONTROL_LEFT in pressed or DomCode.CONTROL_RIGHT in pressed:
98-
if DomCode.US_C in pressed:
99-
raise KeyboardInterrupt
100-
if DomCode.US_D in pressed:
101-
raise OSError
102-
10396
# Return the currently pressed keys
10497
return pressed
10598

0 commit comments

Comments
 (0)