new('struct termios'); $res = $ffi->tcgetattr(C::STDIN_FILENO, FFI::addr($termios)); if ($res === -1) { die('Failed to get terminal settings'); } $this->originalTermios = $termios; } public static function enableRawMode(): void { static $run = FALSE; // Don't run this more than once! if ($run === TRUE) { return; } $run = TRUE; $instance = self::getInstance(); // Make sure to restore normal mode on exit/die/crash register_shutdown_function([static::class, 'disableRawMode']); // So, the only thing that seems to really matter here is that c_oflag is 0... $termios = clone $instance->originalTermios; // $termios->c_iflag &= ~(C::BRKINT | C::ICRNL | C::INPCK | C::ISTRIP | C::IXON); // $termios->c_oflag &= ~(C::OPOST); $termios->c_iflag = 0; $termios->c_oflag = 0; $termios->c_cflag |= (C::CS8); $termios->c_lflag = $termios->c_lflag & ~( C::ECHO | C::ICANON | C::IEXTEN | C::ISIG ); $termios->c_cc[C::VMIN] = 0; $termios->c_cc[C::VTIME] = 1; // Turn on raw mode $res = get_ffi()->tcsetattr(C::STDIN_FILENO, C::TCSAFLUSH, FFI::addr($termios)); if ($res === -1) { die('Failed to set raw mode'); } } public static function disableRawMode(): void { $instance = self::getInstance(); write_stdout("\x1b[2J"); // Clear the screen write_stdout("\x1b[H"); // Reposition cursor to top-left echo "\n"; $res = get_ffi()->tcsetattr(C::STDIN_FILENO, C::TCSAFLUSH, FFI::addr($instance->originalTermios)); if ($res === -1) { die('Failed to restore terminal settings'); } } private static function getInstance(): self { static $instance; if ($instance === NULL) { $instance = new self(); } return $instance; } }