From 6cc8b3cd8a59c01d5485ce4069696776436d2d24 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Thu, 8 Aug 2013 09:06:16 -0500 Subject: [PATCH] improve focus behavior. --- .npmignore | 11 ++++ example/index.html | 53 +++++++++++++++++++ example/index.js | 93 +++++++++++++++++++++++++++++++++ lib/index.js | 3 +- lib/term.js | 125 +++++++++++++++++++++++++++++++++++++++++---- 5 files changed, 274 insertions(+), 11 deletions(-) create mode 100644 .npmignore create mode 100644 example/index.html create mode 100644 example/index.js diff --git a/.npmignore b/.npmignore new file mode 100644 index 00000000..7bf00685 --- /dev/null +++ b/.npmignore @@ -0,0 +1,11 @@ +.git* +build/ +.lock-wscript +out/ +Makefile.gyp +*.Makefile +*.target.gyp.mk +node_modules/ +img/ +test/ +*.node diff --git a/example/index.html b/example/index.html new file mode 100644 index 00000000..f08d0bcc --- /dev/null +++ b/example/index.html @@ -0,0 +1,53 @@ + +tty.js + + +

tty.js

+ + + diff --git a/example/index.js b/example/index.js new file mode 100644 index 00000000..04ca2140 --- /dev/null +++ b/example/index.js @@ -0,0 +1,93 @@ +/** + * tty.js + * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License) + */ + +var http = require('http') + , express = require('express') + , io = require('socket.io') + , pty = require('pty.js'); + +/** + * tty.js + */ + +process.title = 'tty.js'; + +/** + * Open Terminal + */ + +var buff = [] + , socket + , term; + +var term = pty.fork(process.env.SHELL || 'sh', [], { + name: 'xterm', + cols: 80, + rows: 24, + cwd: process.env.HOME +}); + +term.on('data', function(data) { + return !socket + ? buff.push(data) + : socket.emit('data', data); +}); + +console.log('' + + 'Created shell with pty master/slave' + + ' pair (master: %d, pid: %d)', + term.fd, term.pid); + +/** + * App & Server + */ + +var app = express() + , server = http.createServer(app); + +app.use(function(req, res, next) { + var setHeader = res.setHeader; + res.setHeader = function(name) { + switch (name) { + case 'Cache-Control': + case 'Last-Modified': + case 'ETag': + return; + } + return setHeader.apply(res, arguments); + }; + next(); +}); + +app.use(express.static(__dirname)); +app.use(express.static(__dirname + '/../lib')); + +server.listen(8080); + +/** + * Sockets + */ + +io = io.listen(server); + +io.configure(function() { + io.disable('log'); +}); + +io.sockets.on('connection', function(sock) { + socket = sock; + + socket.on('data', function(data) { + term.write(data); + }); + + socket.on('disconnect', function() { + socket = null; + }); + + while (buff.length) { + socket.emit('data', buff.shift()); + } +}); diff --git a/lib/index.js b/lib/index.js index f837da1f..2f4e508f 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,6 +1,7 @@ /** * term.js - an xterm emulator - * Copyright (c) 2013, Christopher Jeffrey (https://github.com/chjj/term.js) + * Copyright (c) 2013, Christopher Jeffrey (MIT License) + * https://github.com/chjj/term.js */ function term(options) { diff --git a/lib/term.js b/lib/term.js index 6cd07ebd..073ee199 100644 --- a/lib/term.js +++ b/lib/term.js @@ -1,7 +1,7 @@ /** * term.js - an xterm emulator - * - * Copyright (c) 2012-2013, Christopher Jeffrey (https://github.com/chjj/term.js) + * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License) + * https://github.com/chjj/term.js * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -262,6 +262,25 @@ Terminal.colors = [ '#eeeeec' ]; +Terminal.xtermColors = [ + '#000000', // black + '#cd0000', // red3 + '#00cd00', // green3 + '#cdcd00', // yellow3 + '#0000ee', // blue2 + '#cd00cd', // magenta3 + '#00cdcd', // cyan3 + '#e5e5e5', // gray90 + '#7f7f7f', // gray50 + '#ff0000', // red + '#00ff00', // green + '#ffff00', // yellow + '#5c5cff', // rgb:5c/5c/ff + '#ff00ff', // magenta + '#00ffff', // cyan + '#ffffff' // white +]; + // Colors 16-255 // Much thanks to TooTallNate for writing this. Terminal.colors = (function() { @@ -326,14 +345,41 @@ Terminal.focus = null; Terminal.prototype.focus = function() { if (Terminal.focus === this) return; + if (Terminal.focus) { - Terminal.focus.cursorState = 0; - Terminal.focus.refresh(Terminal.focus.y, Terminal.focus.y); - if (Terminal.focus.sendFocus) Terminal.focus.send('\x1b[O'); + Terminal.focus.blur(); } - Terminal.focus = this; + if (this.sendFocus) this.send('\x1b[I'); this.showCursor(); + + // try { + // this.element.focus(); + // } catch (e) { + // ; + // } + + // this.emit('focus'); + + Terminal.focus = this; +}; + +Terminal.prototype.blur = function() { + if (Terminal.focus !== this) return; + + this.cursorState = 0; + this.refresh(this.y, this.y); + if (this.sendFocus) this.send('\x1b[O'); + + // try { + // this.element.blur(); + // } catch (e) { + // ; + // } + + // this.emit('blur'); + + Terminal.focus = null; }; /** @@ -341,17 +387,57 @@ Terminal.prototype.focus = function() { */ Terminal.bindKeys = function(document) { - if (Terminal.focus) return; + Terminal._boundDocs = Terminal._boundDocs || []; + + if (~indexOf(Terminal._boundDocs, document)) { + return; + } + + Terminal._boundDocs.push(document); // We could put an "if (Terminal.focus)" check // here, but it shouldn't be necessary. on(document, 'keydown', function(ev) { - return Terminal.focus.keyDown(ev); + if (!Terminal.focus) return; + var target = ev.target || ev.srcElement; + // Should just be body, but we can + // check everything for good measure. + if (target === Terminal.focus.element + || target === Terminal.focus.context + || target === Terminal.focus.document + || target === Terminal.focus.document.body + || target === Terminal.focus.parent) { + return Terminal.focus.keyDown(ev); + } }, true); on(document, 'keypress', function(ev) { - return Terminal.focus.keyPress(ev); + if (!Terminal.focus) return; + var target = ev.target || ev.srcElement; + // Should just be body, but we can + // check everything for good measure. + if (target === Terminal.focus.element + || target === Terminal.focus.context + || target === Terminal.focus.document + || target === Terminal.focus.document.body + || target === Terminal.focus.parent) { + return Terminal.focus.keyPress(ev); + } }, true); + + // If we click somewhere other than a + // terminal, unfocus the terminal. + on(document, 'mousedown', function(ev) { + if (!Terminal.focus) return; + + var el = ev.target || ev.srcElement; + + do { + if (el === Terminal.focus.element) return; + } while (el = el.parentNode); + + Terminal.focus.blur(); + }); }; /** @@ -367,6 +453,9 @@ Terminal.prototype.open = function(parent) { this.element = this.document.createElement('div'); this.element.className = 'terminal'; + this.element.style.outline = 'none'; + this.element.setAttribute('tabindex', 0); + this.children = []; for (; i < this.rows; i++) { @@ -385,6 +474,14 @@ Terminal.prototype.open = function(parent) { this.startBlink(); + on(this.element, 'focus', function() { + self.focus(); + }); + + // on(this.element, 'blur', function() { + // self.blur(); + // }); + on(this.element, 'mousedown', function() { self.focus(); }); @@ -434,7 +531,7 @@ Terminal.prototype.open = function(parent) { this.element.style.backgroundColor = Terminal.defaultColors.bg; this.element.style.color = Terminal.defaultColors.fg; - //this.emit('open'); + this.emit('open'); }; // XTerm mouse events @@ -4231,6 +4328,14 @@ var String = this.String; var setTimeout = this.setTimeout; var setInterval = this.setInterval; +function indexOf(obj, el) { + var i = obj.length; + while (i--) { + if (obj[i] === el) return i; + } + return -1; +} + /** * Expose */