Евгений Обрезков "behind the terminal"

29
Behind the terminal How to waste half a year of your time on something nobody cares about or

Upload: fwdays

Post on 15-Apr-2017

224 views

Category:

Technology


2 download

TRANSCRIPT

Page 1: Евгений Обрезков "Behind the terminal"

Behind the terminalHow to waste half a year of your time on something

nobody cares about

or

Page 2: Евгений Обрезков "Behind the terminal"

Who am I?

• Developer Advocate at Onix-Systems (open-source, community and so on);

• NodeJS developer with 4+ years of experience;

• Active contributor to Sails ecosystem (in the past);

• Currently working with microcontrollers;

Page 3: Евгений Обрезков "Behind the terminal"

What will I talk about?

• Controlling the cursor in terminal;

• Rendering using our controlled cursor;

• Oops, we need to optimize it;

• Demos;

Page 4: Евгений Обрезков "Behind the terminal"

Controlling the cursor• Move cursor: <ESC>[<ROW>;<COLUMN>f

• Change display mode: <ESC>[<MODE>m

• Change foreground: <ESC>[38;2;<R>;<G>;<B>m

• Change background: <ESC>[48;2;<R>;<G>;<B>m

• Clear the screen: <ESC>[2J

• etc…

Page 5: Евгений Обрезков "Behind the terminal"

How can it be used for implementing canvas?

• cursor.moveTo(x, y) -> process.stdout.write(`\e[${y};${x}f`);

• cursor.background(r, g, b) ->process.stdout.write(`\e[48;2;${r};${g};${b}m`);

• cursor.write(text) ->process.stdout.write(text);

• and so on…

Page 6: Евгений Обрезков "Behind the terminal"

We have implemented mapping API calls to

control sequences, great!

Page 7: Евгений Обрезков "Behind the terminal"

Simple render engine

Move to (10, 10) -> change foreground color to red -> write “Hello, World” -> move to (0, 0).

cursor .moveTo(10, 10) // <ESC>[10;10f .foreground(255, 0, 0) // <ESC>[38;2;255;0;0m .write(‘Hello, World’) // Hello, World .moveTo(0, 0); // <ESC>[0;0f

Page 8: Евгений Обрезков "Behind the terminal"
Page 9: Евгений Обрезков "Behind the terminal"

It works great for static rendering, when you show

the frame only once

Page 10: Евгений Обрезков "Behind the terminal"

But, we want to render 25\30\60\120 times a

second…

Page 11: Евгений Обрезков "Behind the terminal"
Page 12: Евгений Обрезков "Behind the terminal"

Oops, we have a problem

<ESC>[10;10f<ESC>[38;2;255;0;0mHello, World<ESC>[0;0f<ESC>[2J<ESC>[10;10f<ESC>[38;2;255;0;0mHello,World<ESC>[0;0f<ESC>[2J<ESC>[10;10f<ESC>[38;2;255;0;0mHello,World<ESC>[0;0f<ESC>[2J<ESC>[10;10f<ESC>[38;2;255;0;0mHello,World<ESC>[0;0f<ESC>[2J<ESC>[10;10f<ESC>[38;2;255;0;0mHello,World<ESC>[0;0f<ESC>[2J<ESC>[10;10f<ESC>[38;2;255;0;0mHello,World<ESC>[0;0f<ESC>[2J…

Page 13: Евгений Обрезков "Behind the terminal"

If frame is rich enough, you will get spamming a lot of text 25

times per second - that’s not good

Page 14: Евгений Обрезков "Behind the terminal"

Let’s optimize it

Page 15: Евгений Обрезков "Behind the terminal"

Our assumptions• It’s logical to assume, that we need to track which

“pixels” are changed and which not;

• Our current structure is not applicable to building difference map - we have only one big line of control sequences and we know nothing about current state of cursor, “pixels”, etc;

• Solution can be to wrap each cell of the terminal in independent control sequence, so we can control them independently from others;

Page 16: Евгений Обрезков "Behind the terminal"

Wrapping the cellsEach cell must know how to render itself independently. Let’s wrap the cell in all the required sequences for that:

• Position;

• Background\Foreground;

• Display modes;

• Character to write in this cell;

• Reset all settings to default (so it doesn’t collide with other styles);

Page 17: Евгений Обрезков "Behind the terminal"

Wrapped cell

<ESC>[<Y>;<X>f # Position <ESC>[48;2;<R>;<G>;<B>m # Background <ESC>[38;2;<R>;<G>;<B>m # Foreground<ESC>[<DISPLAY_MODE>m # Bold, dim, whatever<CHAR_IN_CELL> # A single character to print<ESC>[0m # Reset all display settings

Page 18: Евгений Обрезков "Behind the terminal"

We got independent cells, think of them as “pixels”

Page 19: Евгений Обрезков "Behind the terminal"

Introducing the “markers”

Page 20: Евгений Обрезков "Behind the terminal"

Cell markers

We have 2 states of the cell: modified and unmodified.

Let’s say, we change background for some cell:

cell[y * <TERMINAL_WIDTH> + x].setBackground();

It changes the cell state to modified and set new background color for our cell, which will rebuild the control sequence for that cell on the next flushing.

Page 21: Евгений Обрезков "Behind the terminal"

Markers allow to not build the difference between cells - we already know that it was not

modified

Page 22: Евгений Обрезков "Behind the terminal"

What if we change background to the same

color?

Page 23: Евгений Обрезков "Behind the terminal"

That’s when difference comes into play

Page 24: Евгений Обрезков "Behind the terminal"

If control sequence in a modified cell is the same as the old control sequence - ignore flushing - we

already flushed it before

Page 25: Евгений Обрезков "Behind the terminal"

Checklist• We have a kind of “virtual terminal” in our render engine

which is represented as an array of wrappers around real cells;

• We removed the control sequence which resets the terminal state. We can clear it via writing cell with whitespace and no styling;

• We have hybrid system with markers and differences between frames;

• Finally, we have 120 FPS in the terminal - amazing!

Page 26: Евгений Обрезков "Behind the terminal"
Page 27: Евгений Обрезков "Behind the terminal"

It’s written entirely in JavaScript

Page 28: Евгений Обрезков "Behind the terminal"

Everybody loves demos!

Page 29: Евгений Обрезков "Behind the terminal"

Thanks

ghaiklor ghaiklor ghaiklor