From 8fd350f0fc17bf48703f2f2093de5c610c956b07 Mon Sep 17 00:00:00 2001 From: Elliot Saba Date: Wed, 5 Oct 2016 07:23:06 +0000 Subject: [PATCH 1/4] Add `Terminado` example --- terminado_demo/README.md | 6 ++ terminado_demo/app.py | 16 ++++ terminado_demo/index.html | 89 ++++++++++++++++++ terminado_demo/terminado_attach.js | 142 +++++++++++++++++++++++++++++ 4 files changed, 253 insertions(+) create mode 100644 terminado_demo/README.md create mode 100644 terminado_demo/app.py create mode 100644 terminado_demo/index.html create mode 100644 terminado_demo/terminado_attach.js diff --git a/terminado_demo/README.md b/terminado_demo/README.md new file mode 100644 index 00000000..70e4e18b --- /dev/null +++ b/terminado_demo/README.md @@ -0,0 +1,6 @@ +Using Terminado with Xterm.js +============================= + +Xterm.js is a very flexible system, and as such can be mapped to work with a wide variety of websocket/terminal backends. [Terminado](https://github.com/takluyver/terminado) is one such backend written in Python using `Tornado` as the underlying web framework. This directory shows a more-or-less barebones example of integrating `Terminado` and `Xterm.js`. To do so requires some small edits to `Xterm.js`'s `attach.js` to change the websocket communication format. Briefly, `Terminado` wraps messages in arrays with an element denoting what kind of data it holds, and so rather than simply sending user-typed data directly, one sends `['stdin', data]`. + +To run this example, first install Terminado via `pip install terminado`, then simply run `python ./app.py` and connect to http://localhost:8000 to open a shell on localhost. diff --git a/terminado_demo/app.py b/terminado_demo/app.py new file mode 100644 index 00000000..28fd1cdd --- /dev/null +++ b/terminado_demo/app.py @@ -0,0 +1,16 @@ +"""A single common terminal for all websockets. +""" +import tornado.web +from tornado.ioloop import IOLoop +from terminado import TermSocket, SingleTermManager + +if __name__ == '__main__': + term_manager = SingleTermManager(shell_command=['bash']) + handlers = [ + (r"/websocket", TermSocket, {'term_manager': term_manager}), + (r"/()", tornado.web.StaticFileHandler, {'path':'index.html'}), + (r"/terminado_attach.js()", tornado.web.StaticFileHandler, {'path':'terminado_attach.js'}), + ] + app = tornado.web.Application(handlers, static_path="../dist/") + app.listen(8010) + IOLoop.current().start() diff --git a/terminado_demo/index.html b/terminado_demo/index.html new file mode 100644 index 00000000..b2977ebf --- /dev/null +++ b/terminado_demo/index.html @@ -0,0 +1,89 @@ + + + +Terminado Xterm.js example page + + + + + + + +
+ + diff --git a/terminado_demo/terminado_attach.js b/terminado_demo/terminado_attach.js new file mode 100644 index 00000000..6031c5ad --- /dev/null +++ b/terminado_demo/terminado_attach.js @@ -0,0 +1,142 @@ +/* + * Implements the attach method that + * attaches the terminal to a Terminado WebSocket stream. + * + * The bidirectional argument indicates, whether the terminal should + * send data to the socket as well and is true, by default. + */ + +(function (attach) { + if (typeof exports === 'object' && typeof module === 'object') { + /* + * CommonJS environment + */ + module.exports = attach(require('../../src/xterm')); + } else if (typeof define == 'function') { + /* + * Require.js is available + */ + define(['../../src/xterm'], attach); + } else { + /* + * Plain browser environment + */ + attach(window.Terminal); + } +})(function (Xterm) { + 'use strict'; + + /** + * This module provides methods for attaching a terminal to a WebSocket + * stream. + * + * @module xterm/addons/attach/attach + */ + var exports = {}; + + /** + * Attaches the given terminal to the given socket. + * + * @param {Xterm} term - The terminal to be attached to the given socket. + * @param {WebSocket} socket - The socket to attach the current terminal. + * @param {boolean} bidirectional - Whether the terminal should send data + * to the socket as well. + * @param {boolean} buffered - Whether the rendering of incoming data + * should happen instantly or at a maximum + * frequency of 1 rendering per 10ms. + */ + exports.attach = function (term, socket, bidirectional, buffered) { + bidirectional = (typeof bidirectional == 'undefined') ? true : bidirectional; + term.socket = socket; + + term._flushBuffer = function () { + term.write(term._attachSocketBuffer); + term._attachSocketBuffer = null; + clearTimeout(term._attachSocketBufferTimer); + term._attachSocketBufferTimer = null; + }; + + term._pushToBuffer = function (data) { + if (term._attachSocketBuffer) { + term._attachSocketBuffer += data; + } else { + term._attachSocketBuffer = data; + setTimeout(term._flushBuffer, 10); + } + }; + + term._getMessage = function (ev) { + var data = JSON.parse(ev.data) + if( data[0] == "stdout" ) { + if (buffered) { + term._pushToBuffer(data[1]); + } else { + term.write(data[1]); + } + } + }; + + term._sendData = function (data) { + socket.send(JSON.stringify(['stdin', data])); + }; + + term._setSize = function (size) { + socket.send(JSON.stringify(['set_size', size.rows, size.cols])); + }; + + socket.addEventListener('message', term._getMessage); + + if (bidirectional) { + term.on('data', term._sendData); + } + term.on('resize', term._setSize); + + socket.addEventListener('close', term.detach.bind(term, socket)); + socket.addEventListener('error', term.detach.bind(term, socket)); + }; + + /** + * Detaches the given terminal from the given socket + * + * @param {Xterm} term - The terminal to be detached from the given socket. + * @param {WebSocket} socket - The socket from which to detach the current + * terminal. + */ + exports.detach = function (term, socket) { + term.off('data', term._sendData); + + socket = (typeof socket == 'undefined') ? term.socket : socket; + + if (socket) { + socket.removeEventListener('message', term._getMessage); + } + + delete term.socket; + }; + + /** + * Attaches the current terminal to the given socket + * + * @param {WebSocket} socket - The socket to attach the current terminal. + * @param {boolean} bidirectional - Whether the terminal should send data + * to the socket as well. + * @param {boolean} buffered - Whether the rendering of incoming data + * should happen instantly or at a maximum + * frequency of 1 rendering per 10ms. + */ + Xterm.prototype.attach = function (socket, bidirectional, buffered) { + return exports.attach(this, socket, bidirectional, buffered); + }; + + /** + * Detaches the current terminal from the given socket. + * + * @param {WebSocket} socket - The socket from which to detach the current + * terminal. + */ + Xterm.prototype.detach = function (socket) { + return exports.detach(this, socket); + }; + + return exports; +}); From fe54da34f81c0fc15bb9986cb71bb8a82000f9b8 Mon Sep 17 00:00:00 2001 From: Elliot Saba Date: Fri, 7 Oct 2016 13:33:10 -0700 Subject: [PATCH 2/4] Add `terminado.js`, an addon for connecting to Terminado backends --- addons/terminado/package.json | 5 ++ .../terminado/terminado.js | 0 terminado_demo/README.md | 6 -- terminado_demo/app.py | 16 ---- terminado_demo/index.html | 89 ------------------- 5 files changed, 5 insertions(+), 111 deletions(-) create mode 100644 addons/terminado/package.json rename terminado_demo/terminado_attach.js => addons/terminado/terminado.js (100%) delete mode 100644 terminado_demo/README.md delete mode 100644 terminado_demo/app.py delete mode 100644 terminado_demo/index.html diff --git a/addons/terminado/package.json b/addons/terminado/package.json new file mode 100644 index 00000000..d6959592 --- /dev/null +++ b/addons/terminado/package.json @@ -0,0 +1,5 @@ +{ + "name": "xterm.terminado", + "main": "terminado.js", + "private": true +} diff --git a/terminado_demo/terminado_attach.js b/addons/terminado/terminado.js similarity index 100% rename from terminado_demo/terminado_attach.js rename to addons/terminado/terminado.js diff --git a/terminado_demo/README.md b/terminado_demo/README.md deleted file mode 100644 index 70e4e18b..00000000 --- a/terminado_demo/README.md +++ /dev/null @@ -1,6 +0,0 @@ -Using Terminado with Xterm.js -============================= - -Xterm.js is a very flexible system, and as such can be mapped to work with a wide variety of websocket/terminal backends. [Terminado](https://github.com/takluyver/terminado) is one such backend written in Python using `Tornado` as the underlying web framework. This directory shows a more-or-less barebones example of integrating `Terminado` and `Xterm.js`. To do so requires some small edits to `Xterm.js`'s `attach.js` to change the websocket communication format. Briefly, `Terminado` wraps messages in arrays with an element denoting what kind of data it holds, and so rather than simply sending user-typed data directly, one sends `['stdin', data]`. - -To run this example, first install Terminado via `pip install terminado`, then simply run `python ./app.py` and connect to http://localhost:8000 to open a shell on localhost. diff --git a/terminado_demo/app.py b/terminado_demo/app.py deleted file mode 100644 index 28fd1cdd..00000000 --- a/terminado_demo/app.py +++ /dev/null @@ -1,16 +0,0 @@ -"""A single common terminal for all websockets. -""" -import tornado.web -from tornado.ioloop import IOLoop -from terminado import TermSocket, SingleTermManager - -if __name__ == '__main__': - term_manager = SingleTermManager(shell_command=['bash']) - handlers = [ - (r"/websocket", TermSocket, {'term_manager': term_manager}), - (r"/()", tornado.web.StaticFileHandler, {'path':'index.html'}), - (r"/terminado_attach.js()", tornado.web.StaticFileHandler, {'path':'terminado_attach.js'}), - ] - app = tornado.web.Application(handlers, static_path="../dist/") - app.listen(8010) - IOLoop.current().start() diff --git a/terminado_demo/index.html b/terminado_demo/index.html deleted file mode 100644 index b2977ebf..00000000 --- a/terminado_demo/index.html +++ /dev/null @@ -1,89 +0,0 @@ - - - -Terminado Xterm.js example page - - - - - - - -
- - From 10dd22a3d4cd09bff26bbfb58b713805ed7b01f3 Mon Sep 17 00:00:00 2001 From: Elliot Saba Date: Sat, 8 Oct 2016 00:36:08 -0700 Subject: [PATCH 3/4] Rename Terminado `attach` and `detach` functions --- addons/terminado/terminado.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/terminado/terminado.js b/addons/terminado/terminado.js index 6031c5ad..01e4615a 100644 --- a/addons/terminado/terminado.js +++ b/addons/terminado/terminado.js @@ -124,7 +124,7 @@ * should happen instantly or at a maximum * frequency of 1 rendering per 10ms. */ - Xterm.prototype.attach = function (socket, bidirectional, buffered) { + Xterm.prototype.terminadoAttach = function (socket, bidirectional, buffered) { return exports.attach(this, socket, bidirectional, buffered); }; @@ -134,7 +134,7 @@ * @param {WebSocket} socket - The socket from which to detach the current * terminal. */ - Xterm.prototype.detach = function (socket) { + Xterm.prototype.terminadoDetach = function (socket) { return exports.detach(this, socket); }; From 8f0ada708f7bae6ebb31f91cff6a35dc02c2c6ee Mon Sep 17 00:00:00 2001 From: Elliot Saba Date: Sat, 8 Oct 2016 20:09:34 -0700 Subject: [PATCH 4/4] Make naming a little more self-consistent --- addons/terminado/terminado.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/addons/terminado/terminado.js b/addons/terminado/terminado.js index 01e4615a..06d70c51 100644 --- a/addons/terminado/terminado.js +++ b/addons/terminado/terminado.js @@ -45,7 +45,7 @@ * should happen instantly or at a maximum * frequency of 1 rendering per 10ms. */ - exports.attach = function (term, socket, bidirectional, buffered) { + exports.terminadoAttach = function (term, socket, bidirectional, buffered) { bidirectional = (typeof bidirectional == 'undefined') ? true : bidirectional; term.socket = socket; @@ -91,8 +91,8 @@ } term.on('resize', term._setSize); - socket.addEventListener('close', term.detach.bind(term, socket)); - socket.addEventListener('error', term.detach.bind(term, socket)); + socket.addEventListener('close', term.terminadoDetach.bind(term, socket)); + socket.addEventListener('error', term.terminadoDetach.bind(term, socket)); }; /** @@ -102,7 +102,7 @@ * @param {WebSocket} socket - The socket from which to detach the current * terminal. */ - exports.detach = function (term, socket) { + exports.terminadoDetach = function (term, socket) { term.off('data', term._sendData); socket = (typeof socket == 'undefined') ? term.socket : socket; @@ -125,7 +125,7 @@ * frequency of 1 rendering per 10ms. */ Xterm.prototype.terminadoAttach = function (socket, bidirectional, buffered) { - return exports.attach(this, socket, bidirectional, buffered); + return exports.terminadoAttach(this, socket, bidirectional, buffered); }; /** @@ -135,7 +135,7 @@ * terminal. */ Xterm.prototype.terminadoDetach = function (socket) { - return exports.detach(this, socket); + return exports.terminadoDetach(this, socket); }; return exports;