Skip to content

Commit

Permalink
Emit C0 control codes for all single byte control codes
Browse files Browse the repository at this point in the history
  • Loading branch information
clue committed Jun 3, 2016
1 parent aa483da commit b2c9227
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 10 deletions.
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,19 @@ Each generic C1 code gets emitted as an `c1` event with its raw 2-byte sequence:
$stream->on('c1', function ($sequence) { … });
```

All other [C0 control codes](https://en.wikipedia.org/wiki/C0_and_C1_control_codes#C0_.28ASCII_and_derivatives.29),
also known as [ASCII control codes](https://en.wikipedia.org/wiki/ASCII#ASCII_control_code_chart),
are supported by just emitting their single-byte value.
Each generic C0 code gets emitted as an `c0` event with its raw single-byte value:

```php
$stream->on('c0', function ($code) {
if ($code === "\n") {
echo 'ENTER pressed';
}
});
```

## Install

The recommended way to install this library is [through Composer](https://getcomposer.org).
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "clue/term-react",
"description": "Streaming terminal emulator, built on top of React PHP",
"keywords": ["terminal", "control codes", "xterm", "csi", "osc", "apc", "dps", "pm", "c1", "streaming", "ReactPHP"],
"keywords": ["terminal", "control codes", "xterm", "csi", "osc", "apc", "dps", "pm", "C1", "C0", "streaming", "ReactPHP"],
"homepage": "https://github.com/clue/php-term-react",
"license": "MIT",
"authors": [
Expand Down
2 changes: 2 additions & 0 deletions examples/stdin-codes.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@

$parser->on('csi', $decoder);
$parser->on('osc', $decoder);
$parser->on('c1', $decoder);
$parser->on('c0', $decoder);

$parser->on('data', function ($bytes) {
echo 'Data: ' . $bytes . PHP_EOL;
Expand Down
35 changes: 26 additions & 9 deletions src/ControlCodeParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -96,27 +96,44 @@ public function handleData($data)
$this->buffer .= $data;

while ($this->buffer !== '') {
// search ESC (\x1B = \033)
$esc = strpos($this->buffer, "\x1B");
// search for first control character (C0 and DEL)
$c0 = false;
for ($i = 0; isset($this->buffer[$i]); ++$i) {
$code = ord($this->buffer[$i]);
if ($code < 0x20 || $code === 0x7F) {
$c0 = $i;
break;
}
}

// no ESC found, emit whole buffer as data
if ($esc === false) {
// no C0 found, emit whole buffer as data
if ($c0 === false) {
$data = $this->buffer;
$this->buffer = '';

$this->emit('data', array($data));
return;
}

// ESC found somewhere inbetween, emit everything before ESC as data
if ($esc !== 0) {
$data = substr($this->buffer, 0, $esc);
$this->buffer = substr($this->buffer, $esc);
// C0 found somewhere inbetween, emit everything before C0 as data
if ($c0 !== 0) {
$data = substr($this->buffer, 0, $c0);
$this->buffer = substr($this->buffer, $c0);

$this->emit('data', array($data));
}

// ESC is now at start of buffer
// C0 is now at start of buffer
// check if this is a normal C0 code or an ESC (\x1B = \033)
// normal C0 will be emitted, ESC will be parsed further
if ($this->buffer[0] !== "\x1B") {
$data = $this->buffer[0];
$this->buffer = (string)substr($this->buffer, 1);

$this->emit('c0', array($data));
break;
}

// check following byte to determine type
if (!isset($this->buffer[1])) {
// type currently unknown, wait for next data chunk
Expand Down
16 changes: 16 additions & 0 deletions tests/ControlCodeParserTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@ public function testEmitsDataInMultipleChunks()
$this->assertEquals('helloworld', $buffer);
}

public function testEmitsC0AsOneChunk()
{
$this->parser->on('data', $this->expectCallableNever());
$this->parser->on('c0', $this->expectCallableOnce("\n"));

$this->input->emit('data', array("\n"));
}

public function testEmitsCsiAsOneChunk()
{
$this->parser->on('data', $this->expectCallableNever());
Expand Down Expand Up @@ -71,6 +79,14 @@ public function testEmitsChunkedEndCsiAsOneChunk()
$this->input->emit('data', array("A"));
}

public function testEmitsDataAndC0()
{
$this->parser->on('data', $this->expectCallableOnceWith("hello world"));
$this->parser->on('c0', $this->expectCallableOnceWith("\n"));

$this->input->emit('data', array("hello world\n"));
}

public function testEmitsCsiAndData()
{
$this->parser->on('data', $this->expectCallableOnceWith("hello"));
Expand Down

0 comments on commit b2c9227

Please sign in to comment.