From ccf3794be2d2390959875f64681b7371d7ac3fb5 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Sat, 13 Jul 2013 23:38:43 -0500 Subject: [PATCH] unicode detect. docs. device attr. focus/blur. smacs checks. --- README.md | 3 ++ lib/program.js | 142 +++++++++++++++++++++++++++++++++++++++++++++++++ lib/widget.js | 17 +++++- 3 files changed, 160 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 94934dc..60741f0 100644 --- a/README.md +++ b/README.md @@ -194,6 +194,9 @@ The screen on which every other node renders. - **keypress** - received on key events. - **element [name]** - global events received for all elements. - **key [name]** - received on key event for [name]. +- **focus, blur** - when the terminal window focuses/blurs. requires a terminal + supporting the focus protocol and focus needs to be passed to + program.enableMouse(). ##### Methods: diff --git a/lib/program.js b/lib/program.js index 6e4a230..68be40f 100644 --- a/lib/program.js +++ b/lib/program.js @@ -50,6 +50,8 @@ function Program(options) { this.terminal = options.terminal || process.env.TERM || 'xterm'; + this.unicode = this.detectUnicode(); + // if (!Program.global) { // Program._write = process.stdout.write; // process.stdout.write = function() {}; @@ -499,6 +501,110 @@ Program.prototype._bindResponse = function(s) { } } + // CSI P s c + // Send Device Attributes (Primary DA). + // CSI > P s c + // Send Device Attributes (Secondary DA). + if (parts = /^\x1b\[(\?|>)(\d*(?:;\d*)*)c/.exec(s)) { + var primary = parts[1] === '?' + , parts = parts[2].split(';').map(function(ch) { return +ch || 0; }) + , out = {}; + + if (primary) { + out.type = 'primary-attribute'; + // VT100-style params: + if (parts[0] === 1 && parts[2] === 2) { + out.term = 'vt100'; + out.advancedVideo = true; + } else if (parts[0] === 1 && parts[2] === 0) { + out.term = 'vt101'; + } else if (parts[0] === 6) { + out.term = 'vt102'; + } else if (parts[0] === 60 + && parts[1] === 1 && parts[2] === 2 + && parts[3] === 6 && parts[4] === 8 + && parts[5] === 9 && parts[6] === 15) { + out.term = 'vt220'; + } else { + // VT200-style params: + parts.forEach(function(attr) { + switch (attr) { + case 1: + out.132cols = true; + break; + case 2: + out.printer = true; + break; + case 6: + out.selectiveErase = true; + break; + case 8: + out.userDefinedKeys = true; + break; + case 9: + out.nationalReplacementCharsets = true; + break; + case 15: + out.technicalCharacters = true; + break; + case 18: + out.userWindows = true; + break; + case 21: + out.horizontalScrolling = true; + break; + case 22: + out.ansiColor = true; + break; + case 29: + out.ansiTextLocator = true; + break; + } + }); + } + } else { + out.type = 'secondary-attribute'; + switch (parts[0]) { + case 0: + out.term = 'vt100'; + break; + case 1: + out.term = 'vt220'; + break; + case 2: + out.term = 'vt240'; + break; + case 18: + out.term = 'vt330'; + break; + case 19: + out.term = 'vt340'; + break; + case 24: + out.term = 'vt320'; + break; + case 41: + out.term = 'vt420'; + break; + case 61: + out.term = 'vt510'; + break; + case 64: + out.term = 'vt520'; + break; + case 65: + out.term = 'vt525'; + break; + } + out.firmwareVersion = parts[1]; + out.romCartridgeRegistrationNumber = parts[2]; + } + + return this.emit('response', { + deviceAttributes: out + }); + } + // CSI Ps n Device Status Report (DSR). // Ps = 5 -> Status Report. Result (``OK'') is // CSI 0 n @@ -673,6 +779,42 @@ Program.prototype._bindResponse = function(s) { } }; +// Not as foolproof as I'd like it to be. +Program.prototype.detectUnicode = function() { + var LANG = process.env.LANG + + ':' + process.env.LANGUAGE + + ':' + process.env.LC_ALL + + ':' + process.env.LC_CTYPE; + + return /utf-?8/i.test(LANG); +}; + +// Could do something goofy like this: +Program.prototype.detectUnicode_ = function(callback) { + var out = String.fromCharCode((0x1b << 8) | '['.charCodeAt(0)) + 'c' + , done; + + this.once('response', function(data) { + if (done) return; + done = true; + if (data.deviceAttributes) { + return callback(null, false); + } + }); + + this.write(out); + + this.cub(2); + this.write(' '); + this.cub(2); + + setTimeout(function() { + if (done) return; + done = true; + return callback(null, true); + }, 100); +}; + Program.prototype.receive = function(text, callback) { var listeners = (this._events && this._events['keypress']) || [] , bak = listeners.slice() diff --git a/lib/widget.js b/lib/widget.js index 79684db..7aba25f 100644 --- a/lib/widget.js +++ b/lib/widget.js @@ -306,6 +306,14 @@ function Screen(options) { self._resizeTimer = setTimeout(resize, time); }); + this.program.on('focus', function() { + self.emit('focus'); + }); + + this.program.on('blur', function() { + self.emit('blur'); + }); + this.program.alternateBuffer(); this.program.hideCursor(); @@ -758,8 +766,13 @@ Screen.prototype.draw = function(start, end) { } } - // TODO: Figure out a way to detect utf8 terminals. - if (this.tput && this.tput.strings.enter_alt_charset_mode && SCLD[ch]) { + if (!this.program.unicode && this.tput + && this.tput.strings.enter_alt_charset_mode && SCLD[ch] + && this.tput.strings.enter_alt_charset_mode !== '\x1b[11m') { + // TODO: Possibly do not check for unicode above. + // For some reason TERM=linux has smacs/rmacs, but it maps to `^[[11m` + // and it does not switch to the DEC SCLD character set. What the hell? + // xterm: \x1b(0, screen: \x0e ch = this.tput.smacs() + SCLD[ch] + this.tput.rmacs(); }