Initial commit
This commit is contained in:
		
							
								
								
									
										245
									
								
								src/Renderer/GdRenderer.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										245
									
								
								src/Renderer/GdRenderer.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,245 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace NoccyLabs\TermBuf\Renderer;
 | 
			
		||||
 | 
			
		||||
use NoccyLabs\TermBuf\TerminalBuffer;
 | 
			
		||||
 | 
			
		||||
class GdRenderer
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    private $font = __DIR__."/../../firacode.ttf";
 | 
			
		||||
    private $boldFont = __DIR__."/../../firacode-bold.ttf";
 | 
			
		||||
 | 
			
		||||
    private $gd;
 | 
			
		||||
 | 
			
		||||
    private $palette = [
 | 
			
		||||
        0x000000,
 | 
			
		||||
        0xCC0000,
 | 
			
		||||
        0x00CC00,
 | 
			
		||||
        0xCCCC00,
 | 
			
		||||
        0x0000CC,
 | 
			
		||||
        0xCC00CC,
 | 
			
		||||
        0x00CCCC,
 | 
			
		||||
        0xCCCCCC,
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    private $annotations = [];
 | 
			
		||||
 | 
			
		||||
    private $annotationFontSize = 2;
 | 
			
		||||
 | 
			
		||||
    private $keyOverlay = [];
 | 
			
		||||
 | 
			
		||||
    private $cellWidth;
 | 
			
		||||
 | 
			
		||||
    private $cellHeight;
 | 
			
		||||
 | 
			
		||||
    public function render(TerminalBuffer $buffer)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        $lines = $buffer->getLines();
 | 
			
		||||
        $cols = $buffer->getColumns();
 | 
			
		||||
 | 
			
		||||
        $cw = 8;
 | 
			
		||||
        $ch = 17;
 | 
			
		||||
 | 
			
		||||
        $this->cellWidth = $cw;
 | 
			
		||||
        $this->cellHeight = $ch;
 | 
			
		||||
 | 
			
		||||
        $this->gd = imagecreatetruecolor($cols * $cw, $lines * $ch);
 | 
			
		||||
 | 
			
		||||
        for ($line = 0; $line < $lines; $line++) {
 | 
			
		||||
            for ($column = 0; $column < $cols; $column++) {
 | 
			
		||||
                $this->renderChar($line*$ch, $column*$cw, $cw, $ch, $buffer->bufferGetRaw($line, $column));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [$cursorLine,$cursorColumn] = $buffer->getCursorPosition();
 | 
			
		||||
        $cursorX = $cursorColumn * $cw;
 | 
			
		||||
        $cursorY = $cursorLine * $ch;
 | 
			
		||||
 | 
			
		||||
        imagerectangle($this->gd, $cursorX, $cursorY, $cursorX + $cw, $cursorY + $ch - 1, 0xFFFFFF);
 | 
			
		||||
 | 
			
		||||
        $this->drawAnnotations();
 | 
			
		||||
        $this->drawKeyOverlay();
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function renderChar(int $y, int $x, int $w, int $h, array $raw)
 | 
			
		||||
    {
 | 
			
		||||
        [ $char, $attrs ] = $raw;
 | 
			
		||||
 | 
			
		||||
        $bold = false;
 | 
			
		||||
        $underline = false;
 | 
			
		||||
        $color = 0xFFFFFF;
 | 
			
		||||
        $bgcolor = 0x000000;
 | 
			
		||||
        
 | 
			
		||||
        //echo "\rattr: " . json_encode($attrs); usleep(100000);
 | 
			
		||||
        foreach ($attrs as $attr) {
 | 
			
		||||
            $attr = intval($attr);
 | 
			
		||||
            if ($attr === 0) {
 | 
			
		||||
                $color = 0xFFFFFF;
 | 
			
		||||
                $bgcolor = 0x000000;
 | 
			
		||||
                $bold = false;
 | 
			
		||||
                $underline = false;
 | 
			
		||||
            } elseif ($attr == 1) {
 | 
			
		||||
                $bold = true;
 | 
			
		||||
            } elseif ($attr == 4) {
 | 
			
		||||
                $underline = true;
 | 
			
		||||
            } elseif ($attr == 22) {
 | 
			
		||||
                $bold = false;
 | 
			
		||||
            } elseif ($attr == 24) {
 | 
			
		||||
                $underline = false;
 | 
			
		||||
            } elseif ($attr >= 30 && $attr <= 39) {
 | 
			
		||||
                $color = $this->palette[$attr - 30];
 | 
			
		||||
            } elseif ($attr >= 40 && $attr <= 49) {
 | 
			
		||||
                $bgcolor = $this->palette[$attr - 40];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        //$attr = join(";", $attrs)??"0";
 | 
			
		||||
        //printf("\e[%d;%dH\e[%sm%s", $line + 1, $column + 1, $attr, $char);
 | 
			
		||||
        imagefilledrectangle($this->gd, $x, $y, $x + $w, $y + $h, $bgcolor);
 | 
			
		||||
        imagettftext($this->gd, 10, 0, $x, $y + 12, $color, $bold?$this->boldFont:$this->font, $char);
 | 
			
		||||
        if ($bold) {
 | 
			
		||||
            //imagettftext($this->gd, 10, 0, $x + 1, $y + 10, $color, $this->font, $char);
 | 
			
		||||
        }
 | 
			
		||||
        if ($underline) {
 | 
			
		||||
            imageline($this->gd, $x, $y + $h - 2, $x + $w, $y + $h - 2, $color);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function writePng(string $filename)
 | 
			
		||||
    {
 | 
			
		||||
        imagepng($this->gd, $filename);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function drawAnnotations()
 | 
			
		||||
    {
 | 
			
		||||
        $fw = imagefontwidth($this->annotationFontSize);
 | 
			
		||||
        $fh = imagefontheight($this->annotationFontSize);
 | 
			
		||||
        $bg = 0xFFEEDD;
 | 
			
		||||
        $bgs = 0x808080;
 | 
			
		||||
        $bgf = 0xFF8800;
 | 
			
		||||
 | 
			
		||||
        foreach ($this->annotations as $annotation) {
 | 
			
		||||
            [$line, $column, $text, $width] = $annotation;
 | 
			
		||||
            $x = $this->cellWidth * $column;
 | 
			
		||||
            $y = $this->cellHeight * ($line + 1) + 5;
 | 
			
		||||
            $lines = explode("\\n", $text);
 | 
			
		||||
            $lc = count($lines);
 | 
			
		||||
            $lw = max(array_map("strlen", $lines));
 | 
			
		||||
            $w = 4 + ($fw * $lw);
 | 
			
		||||
            $h = 2 + ($fh * $lc);
 | 
			
		||||
            $aw = floor($this->cellWidth / 2);
 | 
			
		||||
            for ($n = 0; $n < $aw; $n++) {
 | 
			
		||||
                imageline($this->gd, $x + $n, $y - $n, $x + (2 * $aw) - $n, $y - $n, $bg);
 | 
			
		||||
            }
 | 
			
		||||
            imagerectangle($this->gd, $x+1, $y+1, $x + $w + 1, $y + $h + 1, $bgs);
 | 
			
		||||
            imagefilledrectangle($this->gd, $x, $y, $x + $w, $y + $h, $bg);
 | 
			
		||||
            $lp = 0;
 | 
			
		||||
            foreach ($lines as $line) {
 | 
			
		||||
                imagestring($this->gd, $this->annotationFontSize, $x + 2, $y + 1 + ($lp++ * $fh), $line, 0x0);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if ($width > 0) {
 | 
			
		||||
                imageline($this->gd, $x - 1, $y - 4, $x - 1, $y - 7, $bgf);
 | 
			
		||||
                imageline($this->gd, $x + ($width * $this->cellWidth) + 1, $y - 4, $x + ($width * $this->cellWidth) + 1, $y - 7, $bgf);
 | 
			
		||||
                imageline($this->gd, $x - 1, $y - 4, $x + ($width * $this->cellWidth) + 1, $y - 4, $bgf);
 | 
			
		||||
                imageline($this->gd, $x - 1, $y - 5, $x + ($width * $this->cellWidth) + 1, $y - 5, $bgf);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function addAnnotation(string $id, int $line, int $column, string $text)
 | 
			
		||||
    {
 | 
			
		||||
        $this->annotations[$id] = [ $line, $column, $text, 0 ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function updateAnnotation(string $id, array $props)
 | 
			
		||||
    {
 | 
			
		||||
        if (!array_key_exists($id, $this->annotations)) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        foreach ($props as $k=>$v) {
 | 
			
		||||
            switch ($k) {
 | 
			
		||||
                case 'length':
 | 
			
		||||
                    $this->annotations[$id][3] = $v;
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function removeAnnotation(string $id)
 | 
			
		||||
    {
 | 
			
		||||
        unset ($this->annotations[$id]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function clearAnnotations()
 | 
			
		||||
    {
 | 
			
		||||
        $this->annotations = [];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function drawKeyOverlay()
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        $x = 0;
 | 
			
		||||
        $d = 5;
 | 
			
		||||
        $y = imagesy($this->gd) - 32;
 | 
			
		||||
        $h = 24;
 | 
			
		||||
        $w = 32;
 | 
			
		||||
        $ww = 48;
 | 
			
		||||
 | 
			
		||||
        foreach ($this->keyOverlay as $key) {
 | 
			
		||||
            $x += $d;
 | 
			
		||||
            $char = $this->translateKeyToGlyph($key);
 | 
			
		||||
            if (mb_strlen($char) > 2) {
 | 
			
		||||
                $this->drawRoundedRectangle($x, $y, $ww, $h, 3, 0xCCCCCC);
 | 
			
		||||
                imagettftext($this->gd, 8, 0, $x + 5, $y + 16, 0x000000, $this->boldFont, $char); 
 | 
			
		||||
                $x += $ww;
 | 
			
		||||
            } else {
 | 
			
		||||
                $this->drawRoundedRectangle($x, $y, $w, $h, 3, 0xCCCCCC);
 | 
			
		||||
                imagettftext($this->gd, 10, 0, $x + ((mb_strlen($char)==1)?12:8), $y + 16, 0x000000, $this->boldFont, $char);
 | 
			
		||||
                $x += $w;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setKeyOverlay(array $keys)
 | 
			
		||||
    {
 | 
			
		||||
        $this->keyOverlay = $keys;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function drawRoundedRectangle(int $left, int $top, int $width, int $height, int $radius, $color)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        $right = $left + $width;
 | 
			
		||||
        $bottom = $top + $height;
 | 
			
		||||
        imagefilledrectangle($this->gd, $left + $radius, $top, $right - $radius, $bottom, $color);
 | 
			
		||||
        imagefilledrectangle($this->gd, $left, $top + $radius, $right, $bottom - $radius, $color);
 | 
			
		||||
 | 
			
		||||
        imagefilledellipse($this->gd, $left + $radius, $top + $radius, $radius * 2, $radius * 2, $color);
 | 
			
		||||
        imagefilledellipse($this->gd, $left + $radius, $bottom - $radius, $radius * 2, $radius * 2, $color);
 | 
			
		||||
        imagefilledellipse($this->gd, $right - $radius, $top + $radius, $radius * 2, $radius * 2, $color);
 | 
			
		||||
        imagefilledellipse($this->gd, $right - $radius, $bottom - $radius, $radius * 2, $radius * 2, $color);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function translateKeyToGlyph(string $key)
 | 
			
		||||
    {
 | 
			
		||||
        switch ($key) {
 | 
			
		||||
            case "up":
 | 
			
		||||
                return "↑";
 | 
			
		||||
            case "down":
 | 
			
		||||
                return "↓";
 | 
			
		||||
            case "left":
 | 
			
		||||
                return "←";
 | 
			
		||||
            case "right":
 | 
			
		||||
                return "→";
 | 
			
		||||
            case "backspace":
 | 
			
		||||
            case "bs":
 | 
			
		||||
                return mb_chr(0x232b);
 | 
			
		||||
            default:
 | 
			
		||||
                return $key;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										39
									
								
								src/Renderer/TerminalRenderer.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/Renderer/TerminalRenderer.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,39 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace NoccyLabs\TermBuf\Renderer;
 | 
			
		||||
 | 
			
		||||
use NoccyLabs\TermBuf\TerminalBuffer;
 | 
			
		||||
 | 
			
		||||
class TerminalRenderer
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public function render(TerminalBuffer $buffer)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        $lines = $buffer->getLines();
 | 
			
		||||
        $cols = $buffer->getColumns();
 | 
			
		||||
 | 
			
		||||
        $this->clearScreen();
 | 
			
		||||
        for ($line = 0; $line < $lines; $line++) {
 | 
			
		||||
            for ($column = 0; $column < $cols; $column++) {
 | 
			
		||||
                $this->renderChar($line, $column, $buffer->bufferGetRaw($line, $column));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function clearScreen()
 | 
			
		||||
    {
 | 
			
		||||
        echo "\e[H\e[2J";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function renderChar(int $line, int $column, array $raw)
 | 
			
		||||
    {
 | 
			
		||||
        [ $char, $attrs ] = $raw;
 | 
			
		||||
        if ($char === ' ') return;
 | 
			
		||||
        $attr = join(";", $attrs)??"0";
 | 
			
		||||
        printf("\e[%d;%dH\e[%sm%s", $line + 1, $column + 1, $attr, $char);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										187
									
								
								src/TerminalBuffer.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										187
									
								
								src/TerminalBuffer.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,187 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace NoccyLabs\TermBuf;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TerminalBuffer
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    private $buffer = [];
 | 
			
		||||
 | 
			
		||||
    private $cursorLine = 0;
 | 
			
		||||
 | 
			
		||||
    private $cursorColumn = 0;
 | 
			
		||||
 | 
			
		||||
    private $columns = 0;
 | 
			
		||||
 | 
			
		||||
    private $lines = 0;
 | 
			
		||||
 | 
			
		||||
    private $textAttributes = [];
 | 
			
		||||
 | 
			
		||||
    private $wrapAtEnd = true;
 | 
			
		||||
 | 
			
		||||
    private $scrollAtEnd = true;
 | 
			
		||||
 | 
			
		||||
    public function __construct(int $columns, int $lines)
 | 
			
		||||
    {
 | 
			
		||||
        $this->columns = $columns;
 | 
			
		||||
        $this->lines = $lines;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getLines(): int
 | 
			
		||||
    {
 | 
			
		||||
        return $this->lines;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getColumns(): int
 | 
			
		||||
    {
 | 
			
		||||
        return $this->columns;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function translateBufferOffs(int $line, int $column): int
 | 
			
		||||
    {
 | 
			
		||||
        return ($line * $this->columns) + $column;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function bufferGetRaw(int $line, int $column): array
 | 
			
		||||
    {
 | 
			
		||||
        $offs = 'c'.$this->translateBufferOffs($line, $column);
 | 
			
		||||
        if (array_key_exists($offs, $this->buffer)) {
 | 
			
		||||
            return $this->buffer[$offs];
 | 
			
		||||
        }
 | 
			
		||||
        return [ ' ', [ 0 ] ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function bufferSetRaw(int $line, int $column, array $raw)
 | 
			
		||||
    {
 | 
			
		||||
        $offs = 'c'.$this->translateBufferOffs($line, $column);
 | 
			
		||||
        $this->buffer[$offs] = $raw;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function bufferSetChar(int $line, int $column, string $char, array $attr=null)
 | 
			
		||||
    {
 | 
			
		||||
        $this->bufferSetRaw($line, $column, [
 | 
			
		||||
            $char,
 | 
			
		||||
            $attr??$this->textAttributes
 | 
			
		||||
        ]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setAttributes(array $attr)
 | 
			
		||||
    {
 | 
			
		||||
        $this->textAttributes = $attr;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getAttributes(): array
 | 
			
		||||
    {
 | 
			
		||||
        return $this->textAttributes;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setCursor(int $line, int $column)
 | 
			
		||||
    {
 | 
			
		||||
        $this->cursorColumn = $column;
 | 
			
		||||
        if ($line >= $this->lines) {
 | 
			
		||||
            $scrollBy = $line - $this->lines + 1;
 | 
			
		||||
            $this->scrollBuffer(-$scrollBy);
 | 
			
		||||
            $line = $this->lines - 1;
 | 
			
		||||
        }
 | 
			
		||||
        $this->cursorLine = $line;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getCursorPosition(): array
 | 
			
		||||
    {
 | 
			
		||||
        return [ $this->cursorLine, $this->cursorColumn ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function moveCursorColumns(int $columns)
 | 
			
		||||
    {
 | 
			
		||||
        $lines = 0;
 | 
			
		||||
        while (($this->cursorColumn + $columns) > $this->columns) {
 | 
			
		||||
            $lines++;
 | 
			
		||||
            $columns -= $this->columns;
 | 
			
		||||
        }
 | 
			
		||||
        // TODO: This will always wrap, make it respect the wrap and scroll attributes
 | 
			
		||||
        $this->setCursor($this->cursorLine + $lines, $this->cursorColumn + $columns);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function moveCursorRows(int $rows)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function write(string $text)
 | 
			
		||||
    {
 | 
			
		||||
        //printf("write %d bytes\n", mb_strlen($text));
 | 
			
		||||
        for ($ch = 0; $ch < mb_strlen($text); $ch++) {
 | 
			
		||||
            $c = mb_substr($text, $ch, 1);
 | 
			
		||||
            if (mb_ord($c) >= 32) {
 | 
			
		||||
                //printf("\r[%s] %d,%d\n", $c, $this->cursorLine, $this->cursorColumn); usleep(250000);
 | 
			
		||||
                $this->bufferSetChar( $this->cursorLine, $this->cursorColumn, $c);
 | 
			
		||||
                $this->moveCursorColumns(1);
 | 
			
		||||
            } elseif ($c == chr(10)) {
 | 
			
		||||
                //printf("\r[NL] %d,%d\n", $c, $this->cursorLine, $this->cursorColumn); usleep(250000);
 | 
			
		||||
                $line = $this->cursorLine + 1;
 | 
			
		||||
                $this->setCursor($line, 0);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function writeAnsi(string $text)
 | 
			
		||||
    {
 | 
			
		||||
        for ($ch = 0; $ch < mb_strlen($text); $ch++) {
 | 
			
		||||
            $c = mb_substr($text, $ch, 1);
 | 
			
		||||
            $cc = mb_substr($text, $ch, 2);
 | 
			
		||||
            if ($cc == "\e[") {
 | 
			
		||||
                $sb = null; $ch += 2;
 | 
			
		||||
                while ($ch < mb_strlen($text)) { 
 | 
			
		||||
                    $cp = mb_substr($text, $ch++, 1);
 | 
			
		||||
                    if (ctype_alpha($cp)) { $ch--; break; }
 | 
			
		||||
                    $sb .= $cp;
 | 
			
		||||
                }
 | 
			
		||||
                $attr = explode(";", $sb);
 | 
			
		||||
                $this->setAttributes($attr);
 | 
			
		||||
            } else {
 | 
			
		||||
                $this->write($c);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Scroll lines in the buffer, from $firstLine to $lastLine, moving every line by
 | 
			
		||||
     * $scroll lines while adding empty lines at top or bottom.
 | 
			
		||||
     */
 | 
			
		||||
    public function scrollLines(int $firstLine, int $lastLine, int $scroll = -1)
 | 
			
		||||
    {
 | 
			
		||||
        if ($scroll < 0) {
 | 
			
		||||
            for ($n = 0; $n < abs($scroll); $n++) {
 | 
			
		||||
                for ($line = $firstLine; $line < $lastLine; $line++) {
 | 
			
		||||
                    for ($col = 0; $col < $this->columns; $col++) {
 | 
			
		||||
                        $this->bufferSetRaw($line, $col, $this->bufferGetRaw($line + 1, $col));
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                for ($col = 0; $col < $this->columns; $col++) {
 | 
			
		||||
                    $this->bufferSetRaw($lastLine, $col, [' ', []]);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        } elseif ($scroll > 0) {
 | 
			
		||||
            $first = $firstLine + $scroll; // add scroll to top
 | 
			
		||||
            $last = $lastLine - $scroll; // remove from bottom
 | 
			
		||||
            for ($line = $last - 1; $line >= $first; $line--) {
 | 
			
		||||
                for ($col = 0; $col < $this->columns; $col++) {
 | 
			
		||||
                    $this->bufferSetRaw($line, $col, $this->bufferGetRaw($line - 1, $col));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            for ($line = $firstLine; $line <= $firstLine + $scroll; $line++) {
 | 
			
		||||
                for ($col = 0; $col < $this->columns; $col++) {
 | 
			
		||||
                    $this->bufferSetRaw($line, $col, [' ', []]);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function scrollBuffer(int $scroll)
 | 
			
		||||
    {
 | 
			
		||||
        $this->scrollLines(0, $this->lines - 1, $scroll);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user