From ab02708306b235c393ed3f07c3edfa7773b2b9da Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Mon, 28 Jan 2013 17:30:51 -0600 Subject: [PATCH] improve mouse support --- lib/program.js | 158 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 145 insertions(+), 13 deletions(-) diff --git a/lib/program.js b/lib/program.js index 05c547a..62adebd 100644 --- a/lib/program.js +++ b/lib/program.js @@ -28,11 +28,17 @@ function Program(input, output) { this.cols = this.output.columns || 1; this.rows = this.output.rows || 1; + this.terminal = process.env.TERM || 'xterm'; + this.listen(); } Program.prototype.__proto__ = EventEmitter.prototype; +Program.prototype.term = function(is) { + return this.terminal.indexOf(is) === 0; +}; + Program.prototype.listen = function() { if (!this.input.isTTY || !this.output.isTTY) return; @@ -143,24 +149,64 @@ Program.prototype._bindMouse = function(s) { } // XTerm / X10 - if (parts = /^\x1b\[M([\x20-\x7f\x00]{3})/.exec(s)) { + //if (parts = /^\x1b\[M([\x20-\x7f\x00]{3})/.exec(s)) { + if (parts = /^\x1b\[M([\x00\u0020-\xffff]{3,4})/.exec(s)) { var b = parts[1].charCodeAt(0) , x = parts[1].charCodeAt(1) - , y = parts[1].charCodeAt(2); + , y = parts[1].charCodeAt(2) + , mod; key.name = 'mouse'; key.type = 'X10'; - key.button = b; + + key.raw = [b, x, y, parts[0]]; key.x = x - 32; key.y = y - 32; + if (key.x === 0) key.x = 255; + if (key.y === 0) key.y = 255; + + mod = b >> 3; + key.shift = mod & 4; + key.meta = mod & 8; + key.ctrl = mod & 16; + + b -= 32; + + if (b === 64) { + key.action = 'wheelup'; + key.button = 'middle'; + } else if (b === 65) { + key.action = 'wheeldown'; + key.button = 'middle'; + } else if (b === 3) { + // Could also be a movement. + key.action = 'mouseup'; + key.button = 'unknown'; + } else { + key.action = 'mousedown'; + key.button = + b === 0 ? 'left' + : b === 1 ? 'middle' + : b === 2 ? 'right' + : 'unknown'; + } + + // It's a movement + // Wrong + //if (b > 32 && b < 64) { + // delete key.button; + // key.action = 'movement'; + //} + self.emit('keypress', null, key); self.emit('mouse', key); + return; } // URxvt - if (parts = /^\x1b\[((?:\d+;){3})M/.exec(s)) { + if (parts = /^\x1b\[(\d+;\d+;\d+)M/.exec(s)) { var parts = parts[1].split(';') , b = +parts[0] , x = +parts[1] @@ -168,17 +214,21 @@ Program.prototype._bindMouse = function(s) { key.name = 'mouse'; key.type = 'urxvt'; + key.button = b; key.x = x; key.y = y; + // NOTE: Duplicate of the above. + self.emit('keypress', null, key); self.emit('mouse', key); + return; } // SGR - if (parts = /^\x1b\[<((?:\d+;){3})([mM])/.exec(s)) { + if (parts = /^\x1b\[<(\d+;\d+;\d+)([mM])/.exec(s)) { var down = parts[2] === 'm' , parts = parts[1].split(';') , b = +parts[0] @@ -187,18 +237,32 @@ Program.prototype._bindMouse = function(s) { key.name = 'mouse'; key.type = 'sgr'; - key.button = b; + key.x = x; key.y = y; - key.down = down; + + b &= 3; + + // NOTE: Get mod. And wheel. + + key.action = down + ? 'mousedown' + : 'mouseup'; + + key.button = + b === 0 ? 'left' + : b === 1 ? 'middle' + : b === 2 ? 'right' + : 'unknown'; self.emit('keypress', null, key); self.emit('mouse', key); + return; } // DEC - if (parts = /^\x1b\[<((?:\d+;){4})&w/.exec(s)) { + if (parts = /^\x1b\[<(\d+;\d+;\d+;\d+)&w/.exec(s)) { var parts = parts[1].split(';') , b = +parts[0] , x = +parts[1] @@ -207,12 +271,24 @@ Program.prototype._bindMouse = function(s) { key.name = 'mouse'; key.type = 'dec'; + key.button = b; key.x = x; key.y = y; + key.action = b === 3 + ? 'mouseup' + : 'mousedown'; + + key.button = + b === 2 ? 'left' + : b === 4 ? 'middle' + : b === 6 ? 'right' + : 'unknown'; + self.emit('keypress', null, key); self.emit('mouse', key); + return; } @@ -224,12 +300,31 @@ Program.prototype._bindMouse = function(s) { key.name = 'mouse'; key.type = 'vt300'; - key.button = b; + key.x = x; key.y = y; + key.action = 'mousedown'; + key.button = + b === 1 ? 'left' + : b === 2 ? 'middle' + : b === 5 ? 'right' + : 'unknown'; + self.emit('keypress', null, key); self.emit('mouse', key); + + return; + } + + if (parts = /^\x1b\[(O|I)/.exec(s)) { + key.action = parts[1] === 'I' + ? 'focus' + : 'blur'; + + self.emit('mouse', key); + self.emit(key.action); + return; } }; @@ -1249,8 +1344,45 @@ Program.prototype.normalBuffer = function() { return this.resetMode('?1049'); }; +Program.prototype.enableMouse = function() { + if (this.term('urxvt')) { + return this.setMouse({ urxvtMouse: true }); + } + + if (this.term('xterm') || this.term('screen')) { + return this.setMouse({ + allMotion: true, + utfMouse: true, + sendFocus: true + }); + } + + if (this.term('vt')) { + return this.setMouse({ vt200Mouse: true }); + } +}; + +Program.prototype.disableMouse = function() { + return this.setMouse({ + x10Mouse: false, + vt200Mouse: false, + hiliteTracking: false, + cellMotion: false, + allMotion: false, + sendFocus: false, + utfMouse: false, + sgrMouse: false, + urxvtMouse: false + }); +}; + // Set Mouse Program.prototype.setMouse = function(opt) { + if (opt.normalMouse != null) { + opt.cellMotion = opt.normalMouse; + opt.allMotion = opt.normalMouse; + } + // 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. @@ -1280,16 +1412,16 @@ Program.prototype.setMouse = function(opt) { // 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'); + if (opt.cellMotion != null) { + if (opt.cellMotion) 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'); + if (opt.allMotion != null) { + if (opt.allMotion) this.setMode('?1003'); else this.resetMode('?1003'); }