Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 4
CRAP
45.16% covered (danger)
45.16%
14 / 31
Termios
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 4
18.55
45.16% covered (danger)
45.16%
14 / 31
 __construct
0.00% covered (danger)
0.00%
0 / 1
2.02
83.33% covered (warning)
83.33%
5 / 6
 enableRawMode
0.00% covered (danger)
0.00%
0 / 1
5.67
33.33% covered (danger)
33.33%
5 / 15
 disableRawMode
0.00% covered (danger)
0.00%
0 / 1
1.58
16.67% covered (danger)
16.67%
1 / 6
 getInstance
0.00% covered (danger)
0.00%
0 / 1
2.06
75.00% covered (warning)
75.00%
3 / 4
<?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)
        {
            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_iflag = 0;
        $termios->c_oflag = 0;
        $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;
    }
}