php-kilo/src/Termios.php

92 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;
}
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;
}
}