add mouse support

This commit is contained in:
Christopher Jeffrey 2013-01-27 11:00:02 -06:00
parent 8eaad35dfa
commit a10edf116e

View File

@ -57,6 +57,10 @@ Program.prototype.listen = function() {
self.emit('keypress', ch, key);
});
this.input.on('data', function(data) {
self.emit('data', data);
});
readline.emitKeypressEvents(this.input);
this.input.resume();
@ -72,6 +76,162 @@ Program.prototype.listen = function() {
// self.x = cursor.x;
// self.y = cursor.y;
// });
this.on('newListener', function fn(type) {
if (type === 'mouse') {
self.removeListener('newListener', fn);
self.bindMouse();
}
});
this.setMouse({ normalMouse: true });
var i = 5;
//this.on('keypress', function(ch, key) {
// if (key.name === 'mouse') console.log(key);
// if (!--i) this.setMouse({ normalMouse: false });
//});
this.on('mouse', function(data) {
console.log(data);
if (!--i) this.setMouse({ normalMouse: false });
});
};
// XTerm mouse events
// http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#Mouse%20Tracking
// To better understand these
// the xterm code is very helpful:
// Relevant files:
// button.c, charproc.c, misc.c
// Relevant functions in xterm/button.c:
// BtnCode, EmitButtonCode, EditorButton, SendMousePosition
Program.prototype.bindMouse = function() {
this.on('data', this._bindMouse.bind(this));
this.bindMouse = function() {};
};
Program.prototype._bindMouse = function(s) {
// send a mouse event:
// regular/utf8: ^[[M Cb Cx Cy
// urxvt: ^[[ Cb ; Cx ; Cy M
// sgr: ^[[ Cb ; Cx ; Cy M/m
// vt300: ^[[ 24(1/3/5)~ [ Cx , Cy ] \r
// locator: CSI P e ; P b ; P r ; P c ; P p & w
// motion example of a left click:
// ^[[M 3<^[[M@4<^[[M@5<^[[M@6<^[[M@7<^[[M#7<
// mouseup, mousedown, mousewheel
// left click: ^[[M 3<^[[M#3<
// mousewheel up: ^[[M`3>
var self = this;
// FROM: node/lib/readline.js
var ch
, key = {
name: undefined,
ctrl: false,
meta: false,
shift: false
}
, parts;
if (Buffer.isBuffer(s)) {
if (s[0] > 127 && s[1] === undefined) {
s[0] -= 128;
s = '\x1b' + s.toString('utf-8');
} else {
s = s.toString('utf-8');
}
}
// XTerm / X10
if (parts = /^\x1b\[M([\x20-\x7f\x00]{3})/.exec(s)) {
var b = parts[1].charCodeAt(0)
, x = parts[1].charCodeAt(1)
, y = parts[1].charCodeAt(2);
key.name = 'mouse';
key.type = 'X10';
key.button = b;
key.x = x - 32;
key.y = y - 32;
self.emit('keypress', null, key);
self.emit('mouse', key);
return;
}
// URxvt
if (parts = /^\x1b\[((?:\d+;){3})M/.exec(s)) {
var parts = parts[1].split(';')
, b = +parts[0]
, x = +parts[1]
, y = +parts[2];
key.name = 'mouse';
key.type = 'urxvt';
key.button = b;
key.x = x;
key.y = y;
self.emit('keypress', null, key);
self.emit('mouse', key);
return;
}
// SGR
if (parts = /^\x1b\[<((?:\d+;){3})([mM])/.exec(s)) {
var down = parts[2] === 'm'
, parts = parts[1].split(';')
, b = +parts[0]
, x = +parts[1]
, y = +parts[2];
key.name = 'mouse';
key.type = 'sgr';
key.button = b;
key.x = x;
key.y = y;
key.down = down;
self.emit('keypress', null, key);
self.emit('mouse', key);
return;
}
// DEC
if (parts = /^\x1b\[<((?:\d+;){4})&w/.exec(s)) {
var parts = parts[1].split(';')
, b = +parts[0]
, x = +parts[1]
, y = +parts[2]
, page = +parts[3];
key.name = 'mouse';
key.type = 'dec';
key.button = b;
key.x = x;
key.y = y;
self.emit('keypress', null, key);
self.emit('mouse', key);
return;
}
// vt300
if (parts = /^\x1b\[24([0135])~\[(\d+),(\d+)\]\r/.exec(s)) {
var b = +parts[1]
, x = +parts[2]
, y = +parts[3];
key.name = 'mouse';
key.type = 'vt300';
key.button = b;
key.x = x;
key.y = y;
self.emit('keypress', null, key);
self.emit('mouse', key);
return;
}
};
Program.prototype.receive = function(text, callback) {
@ -1082,6 +1242,77 @@ Program.prototype.normalBuffer = function() {
return this.resetMode('?1049');
};
// Set Mouse
Program.prototype.setMouse = function(opt) {
// Ps = 9 -> Send Mouse X & Y on button press. See the sec-
// tion Mouse Tracking.
// Ps = 9 -> Don't send Mouse X & Y on button press.
// x10 mouse
if (opt.x10Mouse != null) {
if (opt.x10Mouse) this.setMode('?9');
else this.resetMode('?9');
}
// Ps = 1 0 0 0 -> Send Mouse X & Y on button press and
// release. See the section Mouse Tracking.
// Ps = 1 0 0 0 -> Don't send Mouse X & Y on button press and
// release. See the section Mouse Tracking.
// vt200 mouse
if (opt.vt200Mouse != null) {
if (opt.vt200Mouse) this.setMode('?1000');
else this.resetMode('?1000');
}
// Ps = 1 0 0 1 -> Use Hilite Mouse Tracking.
// Ps = 1 0 0 1 -> Don't use Hilite Mouse Tracking.
if (opt.hiliteTracking != null) {
if (opt.hiliteTracking) this.setMode('?1001');
else this.resetMode('?1001');
}
// Ps = 1 0 0 2 -> Use Cell Motion Mouse Tracking.
// Ps = 1 0 0 2 -> Don't use Cell Motion Mouse Tracking.
// button event mouse
if (opt.normalMouse != null) {
if (opt.normalMouse) this.setMode('?1002');
else this.resetMode('?1002');
}
// Ps = 1 0 0 3 -> Use All Motion Mouse Tracking.
// Ps = 1 0 0 3 -> Don't use All Motion Mouse Tracking.
// any event mouse
if (opt.normalMouse != null) {
if (opt.normalMouse) this.setMode('?1003');
else this.resetMode('?1003');
}
// Ps = 1 0 0 4 -> Send FocusIn/FocusOut events.
// Ps = 1 0 0 4 -> Don't send FocusIn/FocusOut events.
if (opt.sendFocus != null) {
if (opt.sendFocus) this.setMode('?1004');
else this.resetMode('?1004');
}
// Ps = 1 0 0 5 -> Enable Extended Mouse Mode.
// Ps = 1 0 0 5 -> Disable Extended Mouse Mode.
if (opt.utfMouse != null) {
if (opt.utfMouse) this.setMode('?1005');
else this.resetMode('?1005');
}
// sgr mouse
if (opt.sgrMouse != null) {
if (opt.sgrMouse) this.setMode('?1006');
else this.resetMode('?1006');
}
// urxvt mouse
if (opt.urxvtMouse != null) {
if (opt.urxvtMouse) this.setMode('?1015');
else this.resetMode('?1015');
}
};
// CSI Ps ; Ps r
// Set Scrolling Region [top;bottom] (default = full size of win-
// dow) (DECSTBM).