]>
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');
8 // Interpret as Command Sequences.
9 const kEscape
= new Buffer([0xFF, 0xF4, 0XFF
, 0xFD, 0x06]); // IAC IP IAC DO TIMING_MARK
10 const kNAWSRequest
= new Buffer([0xFF, 0xFD, 0X1F
]); // IAC DO NAWS
11 const kNAWSResponse
= new Buffer([0xFF, 0xFB, 0X1F
, 0xFF, 0xFA, 0X1F
]); // IAC WILL NAWS IAC SB NAWS
13 // Main tomato sauce class. Properties:
14 // * screens <TomatoSauce.IScreen[]>
15 // * renderers <TomatoSauce.IRenderer[]>
20 // The main entry point is the #run() function.
22 // It emits a listening event that contains the server information on
23 // the `server` key inside the `data` property of the event.
25 // It also emits an error event that contains the error information on
26 // the `error` key inside the `data` property of the event.
27 const TomatoSauce
= class TomatoSauce
extends EventEmitter
{
29 constructor (config
) {
35 Object
.assign(this, config
|| {});
38 // Here's where things get started.
44 // Creates a socket, server based on the configuration. Emits the
45 // listening event when ready.
47 const server
= Net
.createServer();
49 server
.listen(this.port
, function () {
50 this.emit('listening', {
59 // Send the error event all the way up.
60 this.server
.on('error', function (err
) {
68 // Send the error event all the way up.
69 this.server
.on('connection', function (socket
) {
70 this._renderScreen(socket
);
74 // Obtains viewport size, and initializes a random screen with a random
75 // renderer, setting the required interval to draw.
76 _renderScreen (socket
) {
77 let connectionData
= {
81 screen: this._getScreen(),
82 renderer: this._getRenderer(),
87 socket
.write(kNAWSRequest
);
89 socket
.on('data', function (data
) {
90 if (data
.slice(0, 6).compare(kNAWSResponse
) === 0) {
91 connectionData
.width
= Util
.parse16BitBuffer(data
.slice(6, 8));
92 connectionData
.height
= Util
.parse16BitBuffer(data
.slice(8, 10));
94 socket
.write('\x1B[2J'); // Clear the Screen (CSI 2 J)
95 interval
= setInterval(this._writeMessage
.bind(this, connectionData
), this.frequency
);
98 if (data
.compare(kEscape
) === 0) {
100 clearInterval(interval
);
106 // Resets the cursor, gets a frame and sends it to the socket.
107 _writeMessage (connectionData
) {
108 let payload
= connectionData
.screen(connectionData
.modulation
, connectionData
.width
, connectionData
.height
, connectionData
.renderer
);
109 let message
= `\x1B[1;1H${payload}`; // reset cursor position before payload
111 connectionData
.modulation
= (connectionData
.modulation
+ this.modulation
) % 256;
112 connectionData
.socket
.write(message
);
116 return Util
.pickRandom(this.screens
);
120 return Util
.pickRandom(this.renderers
);
125 TomatoSauce
.prototype.port
= 9999;
126 TomatoSauce
.prototype.frequency
= 333;
127 TomatoSauce
.prototype.modulation
= 5;
129 module
.exports
= TomatoSauce
;