new('struct termios'); if ($termios === NULL) { throw new TermiosException('Failed to create termios struct'); } $termiosAddr = FFI::addr($termios); $res = $ffi->tcgetattr(C::STDIN_FILENO, $termiosAddr); if ($res === -1) { throw new TermiosException('Failed to get existing terminal settings'); } $this->originalTermios = $termios; } /** * Put the current terminal into raw input mode * * Returns TRUE if successful. Will return NULL if run more than once, as * raw mode is pretty binary...there's no point in reapplying raw mode! * * @return bool|null */ public static function enableRawMode(): ?bool { static $run = FALSE; // Don't run this more than once! if ($run === TRUE) { return NULL; } $run = TRUE; $instance = self::getInstance(); // Make sure to restore normal mode on exit/die/crash register_shutdown_function([static::class, 'disableRawMode']); $termios = clone $instance->originalTermios; $termios->c_iflag &= ~(C::BRKINT | C::ICRNL | C::INPCK | C::ISTRIP | C::IXON); $termios->c_oflag &= ~(C::OPOST); $termios->c_cflag |= (C::CS8); $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)); return $res !== -1; } /** * Restores terminal settings that were changed when going into raw mode. * * Returns TRUE if settings are applied successfully. If raw mode was not * enabled, this will output a line of escape codes and a new line. * * @return bool */ public static function disableRawMode(): bool { $instance = self::getInstance(); // Cleanup write_stdout(ANSI::CLEAR_SCREEN); write_stdout(ANSI::RESET_CURSOR); write_stdout("\n"); // New line, please $res = get_ffi()->tcsetattr(C::STDIN_FILENO, C::TCSAFLUSH, FFI::addr($instance->originalTermios)); return $res !== -1; } private static function getInstance(): self { static $instance; if ($instance === NULL) { $instance = new self(); } return $instance; } }