1111 * CLI keyboard input handler for PHPBoy.
1212 *
1313 * Maps keyboard keys to Game Boy buttons:
14- * - Arrow keys → D-pad (Up, Down, Left, Right)
14+ * - Arrow keys / WASD → D-pad (Up, Down, Left, Right)
1515 * - Z → A button
1616 * - X → B button
1717 * - Enter → Start
18- * - Right Shift → Select
18+ * - Space → Select
19+ * - Ctrl+S → Save state (via callback)
20+ * - Ctrl+C → Exit emulation
1921 *
2022 * Note: Non-blocking keyboard input in PHP CLI is limited.
2123 * This implementation uses stream_select for non-blocking reads
@@ -29,6 +31,9 @@ final class CliInput implements InputInterface
2931 /** Control character for Ctrl+C (ASCII 3) */
3032 private const CTRL_C = "\x03" ;
3133
34+ /** Control character for Ctrl+S (ASCII 19) */
35+ private const CTRL_S = "\x13" ;
36+
3237 /** @var array<string, Button> Keyboard key to button mapping */
3338 private const KEY_MAP = [
3439 // Arrow keys (ANSI escape sequences)
@@ -73,12 +78,25 @@ final class CliInput implements InputInterface
7378 /** @var bool Whether terminal mode has been set */
7479 private bool $ terminalModeSet = false ;
7580
81+ /** @var callable|null Callback to invoke when Ctrl+S is pressed */
82+ private $ onSaveCallback = null ;
83+
7684 public function __construct ()
7785 {
7886 $ this ->stdin = STDIN ;
7987 $ this ->setupTerminal ();
8088 }
8189
90+ /**
91+ * Set callback to invoke when Ctrl+S is pressed.
92+ *
93+ * @param callable $callback Function to call when user presses Ctrl+S for save
94+ */
95+ public function onSave (callable $ callback ): void
96+ {
97+ $ this ->onSaveCallback = $ callback ;
98+ }
99+
82100 public function __destruct ()
83101 {
84102 $ this ->restoreTerminal ();
@@ -201,6 +219,14 @@ private function parseInput(string $input): void
201219 exit (0 );
202220 }
203221
222+ // Check for Ctrl+S (in raw mode, this comes through as ASCII 19)
223+ if (str_contains ($ input , self ::CTRL_S )) {
224+ if ($ this ->onSaveCallback !== null ) {
225+ ($ this ->onSaveCallback )();
226+ }
227+ // Don't return here - continue processing other input in the buffer
228+ }
229+
204230 // Check for arrow key escape sequences (3 characters)
205231 if (strlen ($ input ) >= 3 && $ input [0 ] === "\033" && $ input [1 ] === '[ ' ) {
206232 $ sequence = substr ($ input , 0 , 3 );
0 commit comments