add multiple screen docs and telnet server example.

This commit is contained in:
Christopher Jeffrey 2015-08-05 07:18:47 -07:00
parent b068a8d036
commit 90c441a329
2 changed files with 263 additions and 0 deletions

131
README.md
View File

@ -188,6 +188,8 @@ screen.render();
- [Poisitioning](#positioning)
- [Rendering](#rendering)
- [Artificial Cursors](#artificial-cursors)
- [Multiple Screens](#multiple-screens)
- [Server Side Usage](#server-side-usage)
### Notes
@ -2120,6 +2122,135 @@ var screen = blessed.screen({
```
#### Multiple Screens
Blessed supports the ability to create multiple screens. This may not seem
useful at first, but if you're writing a program that serves terminal
interfaces over http, telnet, or any other protocol, this can be very useful.
##### Server Side Usage
A simple telnet server might look like this (see examples/blessed-telnet.js for
a full example):
``` js
telnet.createServer(function(client) {
client.do.transmit_binary();
client.do.terminal_type();
client.do.window_size();
client.on('terminal type', function(data) {
// https://tools.ietf.org/html/rfc884
if (data.command === 'sb' && data.buf[3] === 1) {
var TERM = data.buf.slice(4, -2).toString('ascii');
screen.program.terminal = TERM;
screen.program.tput.terminal = TERM;
screen.program.tput.setup();
screen.render();
}
});
client.on('window size', function(data) {
if (data.command === 'sb') {
client.columns = data.columns;
client.rows = data.rows;
client.emit('resize');
}
});
// Make the client look like a tty:
client.setRawMode = function(mode) {
client.isRaw = mode;
if (!client.writable) return;
if (mode) {
client.do.suppress_go_ahead();
client.will.suppress_go_ahead();
client.will.echo();
} else {
client.dont.suppress_go_ahead();
client.wont.suppress_go_ahead();
client.wont.echo();
}
};
client.isTTY = true;
client.isRaw = false;
client.columns = 80;
client.rows = 24;
var screen = blessed.screen({
smartCSR: true,
input: client,
output: client,
term: 'xterm-256color'
});
client.on('close', function() {
if (!screen.destroyed) {
screen.destroy();
}
});
screen.key(['C-c', 'q'], function(ch, key) {
screen.destroy();
});
screen.on('destroy', function() {
if (client.writable) {
client.destroy();
}
});
screen.data.main = blessed.box({
parent: screen,
left: 'center',
top: 'center',
width: '80%',
height: '90%',
border: 'line',
content: 'Welcome to my server. Here is your own private session.'
});
screen.render();
}).listen(2300);
```
Once you've written something similar and started it, you can simply telnet
into your blessed app:
``` bash
$ telnet localhost 2300
```
Creating a netcat server would also work as long as you disable line buffering
and terminal echo on the commandline via `stty`:
`$ stty -icanon -echo; ncat localhost 3000; stty icanon echo`
Creating a streaming http 1.1 server than runs in the terminal is possible by
curling it with special arguments: `$ curl -sSNT. localhost:8080`.
There are currently no examples of netcat/nc/ncat or http->curl servers yet.
---
The `blessed.screen` constructor can accept `input`, `output`, and `term`
arguments to aid with this. The multiple screens will be managed internally by
blessed. The programmer just has to keep track of the references, however, to
avoid ambiguity, it's possible to explicitly dictate which screen a node is
part of by using the `screen` option when creating an element.
The `screen.destroy()` method is also crucial: this will clean up all event
listeners the screen has bound and make sure it stops listening on the event
loop. Make absolutely certain to remember to clean up your screens once you're
done with them.
A tricky part is making sure to include the ability for the client to send the
TERM which is reset on the serverside, and the terminal size, which is should
also be reset on the serverside. Both of these capabilities are demonstrated
above.
For a working example of a blessed telnet server, see
`examples/blessed-telnet.js`.
### Notes

132
example/blessed-telnet.js Executable file
View File

@ -0,0 +1,132 @@
#!/usr/bin/env node
/**
* blessed-telnet.js
* https://github.com/chjj/blessed
* Copyright (c) 2013-2015, Christopher Jeffrey (MIT License)
* A blessed telnet server.
* See: https://github.com/TooTallNate/node-telnet
*/
process.title = 'blessed-telnet';
var fs = require('fs');
var blessed = require('blessed');
var telnet = require('telnet');
var server = telnet.createServer(function(client) {
client.do.transmit_binary();
client.do.terminal_type();
client.do.window_size();
client.on('terminal type', function(data) {
// https://tools.ietf.org/html/rfc884
if (data.command === 'sb' && data.buf[3] === 1) {
var TERM = data.buf.slice(4, -2).toString('ascii');
screen.program.terminal = TERM;
screen.program.tput.terminal = TERM;
screen.program.tput.setup();
screen.render();
}
});
client.on('window size', function(data) {
if (data.command === 'sb') {
client.columns = data.columns;
client.rows = data.rows;
client.emit('resize');
}
});
// Make the client look like a tty:
client.setRawMode = function(mode) {
client.isRaw = mode;
if (!client.writable) return;
if (mode) {
client.do.suppress_go_ahead();
client.will.suppress_go_ahead();
client.will.echo();
} else {
client.dont.suppress_go_ahead();
client.wont.suppress_go_ahead();
client.wont.echo();
}
};
client.isTTY = true;
client.isRaw = false;
client.columns = 80;
client.rows = 24;
var screen = blessed.screen({
smartCSR: true,
input: client,
output: client,
term: 'xterm-256color'
});
client.on('close', function() {
if (!screen.destroyed) {
screen.destroy();
}
});
screen.key(['C-c', 'q'], function(ch, key) {
screen.destroy();
});
screen.on('destroy', function() {
if (client.writable) {
client.destroy();
}
});
if (test === 'widget-simple') {
return simpleTest(screen);
}
loadTest(screen, test);
});
function simpleTest(screen) {
screen.data.main = blessed.box({
parent: screen,
width: '80%',
height: '90%',
border: 'line',
content: 'Welcome to my server. Here is your own private session.',
style: {
bg: 'red'
}
});
screen.key('i', function() {
screen.data.main.style.bg = 'blue';
screen.render();
});
screen.render();
}
var test = 'widget-' + (process.argv[2] || 'shadow');
if (test === 'widget-png') process.argv.length = 2;
function loadTest(screen, name) {
var Screen = blessed.screen;
var key = screen.key;
blessed.screen = function() { return screen; };
screen.key = function(keys) {
keys = [].concat(keys)
if (~keys.indexOf('q') || ~keys.indexOf('C-c') || ~keys.indexOf('escape')) {
return;
}
return key.apply(screen, arguments);
};
var path = require.resolve('../test/' + name);
delete require.cache[path];
require('../test/' + name);
blessed.screen = Screen;
screen.key = key;
}
server.listen(2300);
console.log('Listening on 2300...');