170 lines
3.2 KiB
PHP
170 lines
3.2 KiB
PHP
<?php declare(strict_types=1);
|
|
|
|
namespace Aviat\Kilo;
|
|
|
|
use Aviat\Kilo\Enum\RawKeyCode;
|
|
use Aviat\Kilo\Enum\KeyType;
|
|
use Aviat\Kilo\Type\TerminalSize;
|
|
|
|
class Terminal {
|
|
/**
|
|
* Get the size of the current terminal window
|
|
*
|
|
* @codeCoverageIgnore
|
|
* @return TerminalSize
|
|
*/
|
|
public static function size(): TerminalSize
|
|
{
|
|
return new TerminalSize(...self::getWindowSize());
|
|
}
|
|
|
|
/**
|
|
* Get the size of the current terminal window
|
|
*
|
|
* @codeCoverageIgnore
|
|
* @return array
|
|
*/
|
|
public static function getWindowSize(): array
|
|
{
|
|
$ffiSize = Termios::getWindowSize();
|
|
if ($ffiSize !== NULL)
|
|
{
|
|
return $ffiSize;
|
|
}
|
|
|
|
// Try using tput
|
|
if (self::has_tput())
|
|
{
|
|
$rows = (int)trim((string)shell_exec('tput lines'));
|
|
$cols = (int)trim((string)shell_exec('tput cols'));
|
|
|
|
if ($rows > 0 && $cols > 0)
|
|
{
|
|
return [$rows, $cols];
|
|
}
|
|
}
|
|
|
|
// Worst-case, return an arbitrary 'standard' size
|
|
return [25, 80];
|
|
}
|
|
|
|
/**
|
|
* Clear the screen and reset the cursor position
|
|
*/
|
|
public static function clear(): void
|
|
{
|
|
self::write(ANSI::CLEAR_SCREEN . ANSI::RESET_CURSOR);
|
|
}
|
|
|
|
/**
|
|
* Pull input from the stdin stream.
|
|
*
|
|
* @codeCoverageIgnore
|
|
* @param int $len
|
|
* @return string
|
|
*/
|
|
public static function read(int $len = 128): string
|
|
{
|
|
$handle = fopen('php://stdin', 'rb');
|
|
if ($handle === false)
|
|
{
|
|
return '';
|
|
}
|
|
|
|
$input = fread($handle, $len);
|
|
fclose($handle);
|
|
|
|
return (is_string($input)) ? $input : '';
|
|
}
|
|
|
|
/**
|
|
* Get the last key input from the terminal and convert to a
|
|
* more useful format
|
|
*
|
|
* @return string
|
|
*/
|
|
public static function readKey(): string
|
|
{
|
|
$c = Terminal::read();
|
|
|
|
return match($c)
|
|
{
|
|
// Unambiguous mappings
|
|
RawKeyCode::ARROW_DOWN => KeyType::ARROW_DOWN,
|
|
RawKeyCode::ARROW_LEFT => KeyType::ARROW_LEFT,
|
|
RawKeyCode::ARROW_RIGHT => KeyType::ARROW_RIGHT,
|
|
RawKeyCode::ARROW_UP => KeyType::ARROW_UP,
|
|
RawKeyCode::DELETE => KeyType::DELETE,
|
|
RawKeyCode::ENTER => KeyType::ENTER,
|
|
RawKeyCode::PAGE_DOWN => KeyType::PAGE_DOWN,
|
|
RawKeyCode::PAGE_UP => KeyType::PAGE_UP,
|
|
|
|
// Backspace
|
|
RawKeyCode::CTRL('h'), RawKeyCode::BACKSPACE => KeyType::BACKSPACE,
|
|
|
|
// Escape
|
|
RawKeyCode::CTRL('l'), RawKeyCode::ESCAPE => KeyType::ESCAPE,
|
|
|
|
// Home Key
|
|
"\eOH", "\e[7~", "\e[1~", ANSI::RESET_CURSOR => KeyType::HOME,
|
|
|
|
// End Key
|
|
"\eOF", "\e[4~", "\e[8~", "\e[F" => KeyType::END,
|
|
|
|
default => $c,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Ring the terminal bell
|
|
*/
|
|
public static function ding(): void
|
|
{
|
|
self::write(RawKeyCode::BELL);
|
|
}
|
|
|
|
/**
|
|
* Write to the stdout stream
|
|
*
|
|
* @codeCoverageIgnore
|
|
* @param string $str
|
|
* @param int|NULL $len
|
|
* @return int|false
|
|
*/
|
|
public static function write(string $str, int $len = NULL): int|false
|
|
{
|
|
$handle = fopen('php://stdout', 'ab');
|
|
if ($handle === false)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
$res = (is_int($len))
|
|
? fwrite($handle, $str, $len)
|
|
: fwrite($handle, $str);
|
|
|
|
fflush($handle);
|
|
|
|
fclose($handle);
|
|
|
|
return $res;
|
|
}
|
|
|
|
/**
|
|
* See if tput exists for fallback terminal size detection
|
|
*
|
|
* @return bool
|
|
* @codeCoverageIgnore
|
|
*/
|
|
private static function has_tput(): bool
|
|
{
|
|
$cmd = shell_exec('type tput');
|
|
if ( ! is_string($cmd))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
return str_contains($cmd, ' is ');
|
|
}
|
|
}
|