95 lines
2.0 KiB
PHP
95 lines
2.0 KiB
PHP
|
<?php declare(strict_types=1);
|
||
|
|
||
|
namespace Aviat\Kilo;
|
||
|
|
||
|
use FFI;
|
||
|
use FFI\CData;
|
||
|
|
||
|
use Aviat\Kilo\Enum\C;
|
||
|
|
||
|
/**
|
||
|
* An implicit singleton wrapper around terminal settings to simplify enabling/disabling raw mode
|
||
|
*/
|
||
|
class Termios {
|
||
|
private CData $originalTermios;
|
||
|
|
||
|
private function __construct()
|
||
|
{
|
||
|
$ffi = get_ffi();
|
||
|
$termios = $ffi->new('struct termios');
|
||
|
$res = $ffi->tcgetattr(C::STDIN_FILENO, FFI::addr($termios));
|
||
|
if ($res === -1)
|
||
|
{
|
||
|
die('Failed to get terminal settings');
|
||
|
}
|
||
|
|
||
|
$this->originalTermios = $termios;
|
||
|
|
||
|
// Make sure to restore normal mode on exit/die/crash
|
||
|
register_shutdown_function([$this, '__destruct']);
|
||
|
}
|
||
|
|
||
|
public function __destruct()
|
||
|
{
|
||
|
self::disableRawMode();
|
||
|
}
|
||
|
|
||
|
public static function enableRawMode(): void
|
||
|
{
|
||
|
static $run = FALSE;
|
||
|
|
||
|
// Don't run this more than once!
|
||
|
if ($run === TRUE)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
$run = TRUE;
|
||
|
|
||
|
$instance = self::getInstance();
|
||
|
|
||
|
// So, the only thing that seems to really matter here is that c_oflag is 0...
|
||
|
$termios = $instance->originalTermios;
|
||
|
$termios->c_iflag = 0; //$termios->c_iflag & ~(C::BRKINT | C::ICRNL | C::INPCK | C::ISTRIP | C::IXON);
|
||
|
$termios->c_oflag = 0; // $termios->c_oflag && ~(C::OPOST);
|
||
|
$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;
|
||
|
}
|
||
|
}
|