Initial commit
This commit is contained in:
commit
eceacf34cd
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
/composer.lock
|
||||||
|
/vendor
|
||||||
|
*~
|
||||||
|
|
167
SCRIPTING.md
Normal file
167
SCRIPTING.md
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
# FakeBash Scripting
|
||||||
|
|
||||||
|
## Understanding the parser
|
||||||
|
|
||||||
|
The parser is CSV-based, and as such does not care about unquoted spaces.
|
||||||
|
For that reason, the following two examples are the same:
|
||||||
|
|
||||||
|
write "Hello World"
|
||||||
|
write H e l l o " " W o r l d
|
||||||
|
write "Hello " World
|
||||||
|
|
||||||
|
You can use unicode with the syntax `u{CODE}`:
|
||||||
|
|
||||||
|
write u{26c4} " A snowman!"
|
||||||
|
|
||||||
|
### Conventions
|
||||||
|
|
||||||
|
* Annotations are prefixed with an at-sign (`@`)
|
||||||
|
* Bookmarks are prefixed with a percent sign (`%`)
|
||||||
|
* Comments start with a hash sign (`#`) and cover the entire line
|
||||||
|
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
### annotate - Add an anotation
|
||||||
|
|
||||||
|
Annotations can be added to point to any cell in the virtual terminal.
|
||||||
|
|
||||||
|
annotate @useful-tip "This is a useful tip\nthat spans 2 lines"
|
||||||
|
delay 2000
|
||||||
|
unannotate @useful-tip
|
||||||
|
|
||||||
|
Annotations doesn't need to have a name and may have a position:
|
||||||
|
|
||||||
|
annotate "This will annotate char 5 on line 1" 0 4
|
||||||
|
|
||||||
|
You can also use bookmarks for positioning the annotations:
|
||||||
|
|
||||||
|
mark %where
|
||||||
|
...
|
||||||
|
annotate "This will annotate the bookmark %where" at %where
|
||||||
|
|
||||||
|
### clearannotate - Remove all annotations
|
||||||
|
|
||||||
|
Removes all annontations in one go.
|
||||||
|
|
||||||
|
clearannotate
|
||||||
|
|
||||||
|
### delay - Wait for time to pass
|
||||||
|
|
||||||
|
Only useful if you have `set fps` to a non-zero value, this command will
|
||||||
|
simulate time passing and render any frames needed.
|
||||||
|
|
||||||
|
delay 100
|
||||||
|
|
||||||
|
### goto - Goto a bookmark
|
||||||
|
|
||||||
|
mark %somewhere
|
||||||
|
write "Hello"
|
||||||
|
goto %somewhere
|
||||||
|
write "Goodbye"
|
||||||
|
|
||||||
|
### log - Log debug info
|
||||||
|
|
||||||
|
This command just sends output to the console for debugging.
|
||||||
|
|
||||||
|
log "The parameter is " $1
|
||||||
|
|
||||||
|
### mark - Set a bookmark
|
||||||
|
|
||||||
|
mark %here
|
||||||
|
|
||||||
|
### moveto - Go to position or bookmark
|
||||||
|
|
||||||
|
moveto %here
|
||||||
|
moveto 0 0
|
||||||
|
|
||||||
|
### savepng - Save the terminal as a png
|
||||||
|
|
||||||
|
savepng output.png
|
||||||
|
|
||||||
|
### set - Set options
|
||||||
|
|
||||||
|
The `set` command is used to configure Faketerm.
|
||||||
|
|
||||||
|
Terminal-related:
|
||||||
|
|
||||||
|
set terminal 80x25 # Width and height of window
|
||||||
|
|
||||||
|
Typing related:
|
||||||
|
|
||||||
|
set typedelay 250 # Delay in ms when using the type command
|
||||||
|
set typejitter 100 # Random amount of +/- ms when using type
|
||||||
|
|
||||||
|
Animation:
|
||||||
|
|
||||||
|
set fps 30 # Enable rendering of frames
|
||||||
|
set video out.mp4 # Convert frames to a video/gif
|
||||||
|
|
||||||
|
Annotations:
|
||||||
|
|
||||||
|
set annotationsize 3 # Font size for annotations
|
||||||
|
|
||||||
|
|
||||||
|
### sub - Subroutine
|
||||||
|
|
||||||
|
The `sub` command defines a subroutine, all the way up to the
|
||||||
|
`endsub` command. Subroutines can take indexed arguments. The
|
||||||
|
arguments are not checked, and empty arguments are replaced with
|
||||||
|
empty strings.
|
||||||
|
|
||||||
|
sub hello
|
||||||
|
write "Hello, " $1 <return>
|
||||||
|
endsub
|
||||||
|
|
||||||
|
hello "World"
|
||||||
|
|
||||||
|
### type - Simulate typing to the fake terminal
|
||||||
|
|
||||||
|
This command works just like `write`, only it writes character by character.
|
||||||
|
|
||||||
|
type "Hello World"
|
||||||
|
|
||||||
|
### unannotate - Remove an annotation
|
||||||
|
|
||||||
|
Removes an annotation
|
||||||
|
|
||||||
|
unannotate @id
|
||||||
|
|
||||||
|
### write - Write to the fake terminal
|
||||||
|
|
||||||
|
Write output to the current cursor position of the virtual terminal. Extra
|
||||||
|
tags can be added as arguments, but be aware there are no closing tags.
|
||||||
|
|
||||||
|
write <sgr bold> "Hello World" <sgr> <return>
|
||||||
|
|
||||||
|
Supported tags are:
|
||||||
|
|
||||||
|
<sgr> - Set output style, no parameters resets style.
|
||||||
|
<home> - Move to the beginning of the current line
|
||||||
|
<move> - Move the cursor
|
||||||
|
<erase> - Erase line or screen
|
||||||
|
<return> - Move to the beginning of the next line
|
||||||
|
|
||||||
|
### writefile - Write a file to the fake terminal
|
||||||
|
|
||||||
|
This command writes the output of a file line by line to the fake terminal
|
||||||
|
with an optional delay between each line.
|
||||||
|
|
||||||
|
writefile "command-output.txt" 100
|
||||||
|
|
||||||
|
## Write and Type tags
|
||||||
|
|
||||||
|
The following can be used with the <sgr> tag:
|
||||||
|
|
||||||
|
sgr fg:<color>
|
||||||
|
bg:<color>
|
||||||
|
bold
|
||||||
|
underline
|
||||||
|
|
||||||
|
The following with the <move> tag:
|
||||||
|
|
||||||
|
move [steps] <direction>
|
||||||
|
|
||||||
|
The following with the <erase> tag:
|
||||||
|
|
||||||
|
erase {screen|to-eol|to-bol|to-top|to-bottom}
|
29
bin/faketerm
Executable file
29
bin/faketerm
Executable file
@ -0,0 +1,29 @@
|
|||||||
|
#!/usr/bin/env php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use NoccyLabs\FakeTerm\FakeTerminal;
|
||||||
|
|
||||||
|
require_once __DIR__."/../vendor/autoload.php";
|
||||||
|
|
||||||
|
$opts = getopt("hf:o:v:");
|
||||||
|
|
||||||
|
$r = sprintf("%02x%02x", rand(0,255), rand(0,255));
|
||||||
|
$output = array_key_exists("o", $opts) ? $opts["o"] : "/tmp/ft_${r}_%04d.png";
|
||||||
|
$input = array_key_exists("f", $opts) ? $opts["f"] : null;
|
||||||
|
$video = array_key_exists("v", $opts) ? $opts["v"] : null;
|
||||||
|
|
||||||
|
if (!$input) {
|
||||||
|
fwrite(STDERR, "Error: Need to specify at least -f, preferably -o\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
$ft = new FakeTerminal();
|
||||||
|
|
||||||
|
if ($video) {
|
||||||
|
printf("Rendering to video: %s\n", $video);
|
||||||
|
$ft->setOutputVideo($video);
|
||||||
|
}
|
||||||
|
|
||||||
|
$ft->setOutput($output);
|
||||||
|
$ft->executeFile($input);
|
||||||
|
|
26
composer.json
Normal file
26
composer.json
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"name": "noccylabs/faketerm",
|
||||||
|
"description": "Script fake terminals into videos",
|
||||||
|
"type": "application",
|
||||||
|
"license": "GPL-3.0-or-later",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Christopher Vagnetoft",
|
||||||
|
"email": "cvagnetoft@gmail.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"NoccyLabs\\FakeTerm\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"repositories": [
|
||||||
|
{
|
||||||
|
"type": "path",
|
||||||
|
"url": "../php-termbuf"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"noccylabs/termbuf": "@dev"
|
||||||
|
}
|
||||||
|
}
|
416
src/FakeTerminal.php
Normal file
416
src/FakeTerminal.php
Normal file
@ -0,0 +1,416 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace NoccyLabs\FakeTerm;
|
||||||
|
|
||||||
|
use NoccyLabs\TermBuf\Renderer\GdRenderer;
|
||||||
|
use NoccyLabs\TermBuf\Renderer\TerminalRenderer;
|
||||||
|
use NoccyLabs\TermBuf\TerminalBuffer;
|
||||||
|
|
||||||
|
class FakeTerminal
|
||||||
|
{
|
||||||
|
/** @var TerminalBuffer */
|
||||||
|
private $term;
|
||||||
|
|
||||||
|
/** @var GdRenderer */
|
||||||
|
private $renderer;
|
||||||
|
|
||||||
|
private $subs = [];
|
||||||
|
|
||||||
|
private $execMode = null;
|
||||||
|
|
||||||
|
private $execTarget = null;
|
||||||
|
|
||||||
|
private $typeDelay = 100;
|
||||||
|
|
||||||
|
private $typeJitter = 0;
|
||||||
|
|
||||||
|
private $elapsedMs = 0;
|
||||||
|
|
||||||
|
private $fps = 0;
|
||||||
|
|
||||||
|
private $frame = 0;
|
||||||
|
|
||||||
|
private $frameDuration = 0;
|
||||||
|
|
||||||
|
private $output;
|
||||||
|
|
||||||
|
private $bookmarks = [];
|
||||||
|
|
||||||
|
private $outputVideo = null;
|
||||||
|
|
||||||
|
private $writtenFiles = [];
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->renderer = new GdRenderer();
|
||||||
|
$this->dumper = new TerminalRenderer();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setOutput(string $output)
|
||||||
|
{
|
||||||
|
$this->output = $output;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setOutputVideo(string $outputVideo)
|
||||||
|
{
|
||||||
|
$this->outputVideo = $outputVideo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function executeFile(string $filename)
|
||||||
|
{
|
||||||
|
$fp = fopen($filename, "r");
|
||||||
|
$lc = 0;
|
||||||
|
while (!feof($fp)) {
|
||||||
|
$lc++;
|
||||||
|
$ln = trim(fgets($fp));
|
||||||
|
if ("" == $ln || $ln[0] == "#") continue;
|
||||||
|
$lb = str_getcsv($ln, " ");
|
||||||
|
try {
|
||||||
|
$this->evaluate($lb);
|
||||||
|
}
|
||||||
|
catch (\Throwable $t) {
|
||||||
|
fprintf(STDERR, "Error executing %s line %d: %s\n", $filename, $lc, $t->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "\r\e[K";
|
||||||
|
if ($this->outputVideo) {
|
||||||
|
fprintf(STDERR, "Converting %s → %s\n", $this->output, $this->outputVideo);
|
||||||
|
passthru(sprintf("ffmpeg -v error -r %d -i %s -y %s", $this->fps, $this->output, $this->outputVideo));
|
||||||
|
fprintf(STDERR, "Cleaning up %d files...\n", count($this->writtenFiles));
|
||||||
|
foreach ($this->writtenFiles as $fn) {
|
||||||
|
unlink($fn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function evaluate(array $data)
|
||||||
|
{
|
||||||
|
if ($this->execMode == "sub") {
|
||||||
|
if ($data[0] == "endsub") {
|
||||||
|
$this->execMode = null;
|
||||||
|
$this->execTarget = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$this->subs[$this->execTarget]->sub[] = $data;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$args = $data;
|
||||||
|
$cmd = array_shift($args);
|
||||||
|
|
||||||
|
switch ($cmd) {
|
||||||
|
case 'set':
|
||||||
|
$prop = array_shift($args);
|
||||||
|
$value = array_shift($args);
|
||||||
|
$this->setProp($prop, $value);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'sub':
|
||||||
|
$subname = array_shift($args);
|
||||||
|
$subargs = $args;
|
||||||
|
$this->subs[$subname] = (object)[
|
||||||
|
'sub' => [],
|
||||||
|
'args' => $subargs
|
||||||
|
];
|
||||||
|
$this->execMode = "sub";
|
||||||
|
$this->execTarget = $subname;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'log':
|
||||||
|
echo "log: ";
|
||||||
|
echo join("", $args);
|
||||||
|
echo "\n";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'write':
|
||||||
|
$this->writeTerm($args);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'type':
|
||||||
|
$this->writeTerm($args, true);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'writefile':
|
||||||
|
$file = file(array_shift($args));
|
||||||
|
$delay = intval(array_shift($args)??30);
|
||||||
|
foreach ($file as $line) {
|
||||||
|
$this->writeTerm([ $line ]);
|
||||||
|
$this->delayMs($delay);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'delay':
|
||||||
|
$delay = intval(array_shift($args));
|
||||||
|
$this->delayMs($delay);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'savepng':
|
||||||
|
$name = array_shift($args);
|
||||||
|
$this->writeFrame($name);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'mark':
|
||||||
|
$id = array_shift($args);
|
||||||
|
$this->bookmarks[$id] = $this->term->getCursorPosition();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'moveto':
|
||||||
|
$id = array_shift($args);
|
||||||
|
if (ctype_digit($id)) {
|
||||||
|
$line = $id;
|
||||||
|
$column = array_shift($args);
|
||||||
|
} else {
|
||||||
|
if (!array_key_exists($id, $this->bookmarks)) break;
|
||||||
|
[$line, $column] = $this->bookmarks[$id];
|
||||||
|
}
|
||||||
|
$this->term->setCursor($line, $column);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'annotate':
|
||||||
|
$id = array_shift($args);
|
||||||
|
if ($id[0] != "@") {
|
||||||
|
$text = $id;
|
||||||
|
$id = uniqid();
|
||||||
|
} else {
|
||||||
|
$text = array_shift($args);
|
||||||
|
}
|
||||||
|
$line = array_shift($args);
|
||||||
|
$column = array_shift($args);
|
||||||
|
if ($line == "here" || $line == null) {
|
||||||
|
[$line,$column] = $this->term->getCursorPosition();
|
||||||
|
} elseif ($line == "at") {
|
||||||
|
$bmid = $column;
|
||||||
|
[$line,$column] = array_key_exists($bmid, $this->bookmarks) ? $this->bookmarks[$bmid] : [0,0];
|
||||||
|
}
|
||||||
|
$this->renderer->addAnnotation($id, $line, $column, $text);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'unannotate':
|
||||||
|
$id = array_shift($args);
|
||||||
|
$this->renderer->removeAnnotation($id);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'clearannotate':
|
||||||
|
$this->renderer->clearAnnotations();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'annotatelength':
|
||||||
|
$id = array_shift($args);
|
||||||
|
$this->renderer->updateAnnotation($id, [ 'length' => intval(array_shift($args))]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if (array_key_exists($cmd, $this->subs)) {
|
||||||
|
$this->callSub($cmd, $args);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw new \LogicException("No such command ${cmd}");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function writeFrame($name=null)
|
||||||
|
{
|
||||||
|
$this->renderer->render($this->term);
|
||||||
|
|
||||||
|
if ($name) {
|
||||||
|
$this->renderer->writePng($name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$file = sprintf($this->output, $this->frame++);
|
||||||
|
$this->renderer->writePng($file);
|
||||||
|
|
||||||
|
//$this->dumper->render($this->term);
|
||||||
|
//printf("\e[0m\rwrite: \e[1m%s\e[0m", $file);
|
||||||
|
$this->writtenFiles[] = $file;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setFps(float $fps)
|
||||||
|
{
|
||||||
|
$this->fps = $fps;
|
||||||
|
if ($fps > 0) {
|
||||||
|
$this->frameDuration = 1000 / $fps;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function delayMs(int $ms)
|
||||||
|
{
|
||||||
|
if ($this->fps == 0) return;
|
||||||
|
|
||||||
|
$this->elapsedMs += $ms;
|
||||||
|
|
||||||
|
$lastFrameMs = $this->frame * $this->frameDuration;
|
||||||
|
$nextFrameMs = $lastFrameMs + $this->frameDuration;
|
||||||
|
while ($this->elapsedMs >= $nextFrameMs) {
|
||||||
|
$this->writeFrame();
|
||||||
|
$nextFrameMs += $this->frameDuration;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function writeTerm(array $args, bool $type=false)
|
||||||
|
{
|
||||||
|
//printf("write: %s\n", json_encode($args));
|
||||||
|
$buf = null;
|
||||||
|
while (count($args)) {
|
||||||
|
$arg = array_shift($args);
|
||||||
|
if ($arg[0] == "<" && strlen($arg) > 1) {
|
||||||
|
if (substr($arg, -1, 1) == ">") {
|
||||||
|
$this->setupTerm(substr($arg, 1, -1));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$buf = [ $arg ];
|
||||||
|
while ($tail = array_shift($args)) {
|
||||||
|
$buf[] = $tail;
|
||||||
|
if (substr($tail, -1, 1) == ">") {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$buf = join(" ", $buf);
|
||||||
|
$this->setupTerm(substr($buf, 1, -1));
|
||||||
|
$buf = null;
|
||||||
|
} elseif (preg_match('/u\{([0-9a-f]+)\}/', $arg, $match)) {
|
||||||
|
|
||||||
|
$arg = mb_chr(hexdec($match[1]));
|
||||||
|
//printf("write: '%s'\n", $arg);
|
||||||
|
if (!$type) {
|
||||||
|
$this->term->write($arg);
|
||||||
|
} else {
|
||||||
|
for ($c = 0; $c < mb_strlen($arg); $c++) {
|
||||||
|
$this->term->write(mb_substr($arg, $c, 1));
|
||||||
|
$this->delayMs($this->typeDelay + rand(-$this->typeJitter,$this->typeJitter));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
//printf("write: '%s'\n", $arg);
|
||||||
|
if (!$type) {
|
||||||
|
$this->term->write($arg);
|
||||||
|
} else {
|
||||||
|
for ($c = 0; $c < mb_strlen($arg); $c++) {
|
||||||
|
$this->term->write(mb_substr($arg, $c, 1));
|
||||||
|
$this->delayMs($this->typeDelay + rand(-$this->typeJitter,$this->typeJitter));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setupTerm(string $prop) {
|
||||||
|
$props = explode(" ", $prop);
|
||||||
|
$mode = array_shift($props);
|
||||||
|
switch ($mode) {
|
||||||
|
|
||||||
|
case 'home':
|
||||||
|
$this->term->setCursor($this->term->getCursorPosition()[0], 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'return':
|
||||||
|
$this->term->setCursor($this->term->getCursorPosition()[0] + 1, 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'move':
|
||||||
|
// move [steps] <dir>
|
||||||
|
// dir = { up, down, left, right }
|
||||||
|
|
||||||
|
case 'erase':
|
||||||
|
// erase <to>
|
||||||
|
// to = { screen, to-eol, to-bol, to-top, to-bottom }
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'sgr':
|
||||||
|
$sgr = [ 0 ];
|
||||||
|
while ($k = array_shift($props)) {
|
||||||
|
if (strpos($k, ":")!==false) {
|
||||||
|
[$k,$v] = explode(":", $k, 2);
|
||||||
|
} else {
|
||||||
|
$v = true;
|
||||||
|
}
|
||||||
|
switch ($k) {
|
||||||
|
case 'bold':
|
||||||
|
$sgr[] = 1;
|
||||||
|
break;
|
||||||
|
case 'fg':
|
||||||
|
$sgr[] = 30 + $this->colorToIndex($v);
|
||||||
|
break;
|
||||||
|
case 'bg':
|
||||||
|
$sgr[] = 40 + $this->colorToIndex($v);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fprintf(STDERR, "Unknown attribute: %s\n", $k);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// printf("sgr: %s\n", json_encode($sgr));
|
||||||
|
$this->term->setAttributes($sgr);
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function colorToIndex(string $color):int {
|
||||||
|
switch ($color) {
|
||||||
|
case 'black': return 0;
|
||||||
|
case 'red': return 1;
|
||||||
|
case 'green': return 2;
|
||||||
|
case 'yellow': return 3;
|
||||||
|
case 'blue': return 4;
|
||||||
|
case 'magenta': return 5;
|
||||||
|
case 'cyan': return 6;
|
||||||
|
case 'white': return 7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function callSub(string $sub, array $args=[])
|
||||||
|
{
|
||||||
|
foreach ($this->subs[$sub]->sub as $data) {
|
||||||
|
foreach ($data as $k=>$v) {
|
||||||
|
if ($v[0] == '$' && strlen($v) > 1) {
|
||||||
|
$i = intval(substr($v, 1)) - 1;
|
||||||
|
$data[$k] = array_key_exists($i, $args)?$args[$i]:"";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->evaluate($data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setProp(string $prop, $value)
|
||||||
|
{
|
||||||
|
switch ($prop) {
|
||||||
|
case 'terminal':
|
||||||
|
[ $columns, $lines ] = explode("x", $value, 2);
|
||||||
|
$this->setTerminalSize(intval($columns), intval($lines));
|
||||||
|
break;
|
||||||
|
case 'colors':
|
||||||
|
$this->colors = intval($value);
|
||||||
|
break;
|
||||||
|
case 'typedelay':
|
||||||
|
$this->typeDelay = intval($value);
|
||||||
|
break;
|
||||||
|
case 'typejitter':
|
||||||
|
$this->typeJitter = intval($value);
|
||||||
|
break;
|
||||||
|
case 'fps':
|
||||||
|
$this->setFps(floatval($value));
|
||||||
|
break;
|
||||||
|
case 'annotationsize':
|
||||||
|
$this->annotationFontSize = intval($value);
|
||||||
|
break;
|
||||||
|
case 'video':
|
||||||
|
$this->outputVideo = $value;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new \LogicException("Bad property for set: ${prop}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setTerminalSize(int $columns, int $rows)
|
||||||
|
{
|
||||||
|
$this->term = new TerminalBuffer($columns, $rows);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user