]>
git.r.bdr.sh - rbdr/tomato-sauce/blob - lib/tomato_sauce.js
3 const Net
= require('net');
4 const EventEmitter
= require('events');
6 const Util
= require('./util');
9 // Interpret as Command Sequences.
10 kEscape: Buffer
.from([0xFF, 0xF4, 0XFF
, 0xFD, 0x06]), // IAC IP IAC DO TIMING_MARK
11 kNAWSRequest: Buffer
.from([0xFF, 0xFD, 0X1F
]), // IAC DO NAWS
12 kNAWSResponse: Buffer
.from([0xFF, 0xFB, 0X1F
, 0xFF, 0xFA, 0X1F
]) // IAC WILL NAWS IAC SB NAWS
16 * A function that represents a screen, it is called frequently and should
17 * return a string consisting of commands to run.
21 * @param {Number} modulation A number between 0 and 255 representing the current
22 * step of the modulation
23 * @param {Number} width The width of the screen
24 * @param {Number} height The height of the screen
25 * @param {IRenderer} renderer The renderer used to colorize the scfeen
26 * @return {String} The commands used to render the screen elements
30 * A function that represents a renderer, it should take in a color in RGB and
31 * return a string with the appropriate code to colorize the terminal.
33 * @interface IRenderer
35 * @param {Number} red The red component of the color between 0 and 255
36 * @param {Number} green The green component of the color between 0 and 255
37 * @param {Number} blue The green component of the color between 0 and 255
38 * @return {String} The commands used to colorize the terminal
42 * The main application for tomato sauce. Listens for connections and serves
43 * random combinations of screens and renderers
45 * The main entry point is the `#run()` function.
47 * It emits a listening event that contains the server information on
48 * the `server` key inside the `data` property of the event.
50 * It also emits an error event that contains the error information on
51 * the `error` key inside the `data` property of the event.
54 * @extends EventEmitter
56 * @param {object} config the configuration object used to extend the properties.
58 * @property {Array<IScreen>} screens an array of screens available to serve
59 * @property {Array<IRenderer>} renderers an array of renderers available to colorize
60 * @property {Number} [port=9999] the port to listen on
61 * @property {Number} [frequency=333] how often to update the screen
62 * @property {Number} [modulation=5] number between 0-255 depicting current modulation step
64 const TomatoSauce
= class TomatoSauce
extends EventEmitter
{
66 constructor(config
= {}) {
78 Object
.assign(this, config
);
82 * Main entry point, initializes the server and binds events for connections
86 * @memberof TomatoSauce
94 // Creates a socket, server based on the configuration. Emits the
95 // listening event when ready.
99 const server
= Net
.createServer();
100 this.server
= server
;
101 server
.listen(this.port
, () => {
103 this.emit('listening', {
111 // Binds the connection and error events
115 // Send the error event all the way up.
116 this.server
.on('error', (error
) => {
125 // Send the error event all the way up.
126 this.server
.on('connection', (socket
) => {
128 this._renderScreen(socket
);
132 // Obtains viewport size, and initializes a random screen with a random
133 // renderer, setting the required interval to draw.
135 _renderScreen(socket
) {
137 const connectionData
= {
141 screen: this._getScreen(),
142 renderer: this._getRenderer(),
147 socket
.write(internals
.kNAWSRequest
);
149 socket
.on('data', (data
) => {
151 if (data
.slice(0, 6).compare(internals
.kNAWSResponse
) === 0) {
152 connectionData
.width
= Util
.parse16BitBuffer(data
.slice(6, 8));
153 connectionData
.height
= Util
.parse16BitBuffer(data
.slice(8, 10));
155 socket
.write('\x1B[2J'); // Clear the Screen (CSI 2 J)
156 interval
= setInterval(this._writeMessage
.bind(this, connectionData
), this.frequency
);
159 if (data
.compare(internals
.kEscape
) === 0) {
161 clearInterval(interval
);
167 // Resets the cursor, gets a frame and sends it to the socket.
169 _writeMessage(connectionData
) {
171 const payload
= connectionData
.screen(connectionData
.modulation
, connectionData
.width
, connectionData
.height
, connectionData
.renderer
);
172 const message
= `\x1B[1;1H${payload}`; // reset cursor position before payload
174 connectionData
.modulation
= (connectionData
.modulation
+ this.modulation
) % 256;
175 connectionData
.socket
.write(message
);
180 return Util
.pickRandom(this.screens
);
185 return Util
.pickRandom(this.renderers
);
189 module
.exports
= TomatoSauce
;