* Try to handle socket error

* Fix bug in wrapping with MultiLineEditText
* Updates on message base DB layout/triggers
* Detect some terminals via ANSI DSR for device attributes (WIP)
This commit is contained in:
Bryan Ashby 2015-07-06 22:37:11 -06:00
parent 2b963ce6f9
commit 40e0b55424
7 changed files with 83 additions and 11 deletions

View file

@ -73,6 +73,8 @@ var CONTROL = {
hideCursor : '?25l', // Nonstandard - cterm.txt hideCursor : '?25l', // Nonstandard - cterm.txt
showCursor : '?25h', // Nonstandard - cterm.txt showCursor : '?25h', // Nonstandard - cterm.txt
queryDeviceAttributes : 'c', // Nonstandard - cterm.txt
// :TODO: see https://code.google.com/p/conemu-maximus5/wiki/AnsiEscapeCodes // :TODO: see https://code.google.com/p/conemu-maximus5/wiki/AnsiEscapeCodes
// apparently some terms can report screen size and text area via 18t and 19t // apparently some terms can report screen size and text area via 18t and 19t
}; };

View file

@ -60,7 +60,8 @@ function getIntArgArray(array) {
return array; return array;
} }
var RE_DSR_RESPONSE_ANYWHERE = /(?:\u001b\[)([0-9\;]+)([R])/; var RE_DSR_RESPONSE_ANYWHERE = /(?:\u001b\[)([0-9\;]+)(R)/;
var RE_DEV_ATTR_RESPONSE_ANYWHERE = /(?:\u001b\[)[\=\?]([0-9a-zA-Z\;]+)(c)/;
var RE_META_KEYCODE_ANYWHERE = /(?:\u001b)([a-zA-Z0-9])/; var RE_META_KEYCODE_ANYWHERE = /(?:\u001b)([a-zA-Z0-9])/;
var RE_META_KEYCODE = new RegExp('^' + RE_META_KEYCODE_ANYWHERE.source + '$'); var RE_META_KEYCODE = new RegExp('^' + RE_META_KEYCODE_ANYWHERE.source + '$');
var RE_FUNCTION_KEYCODE_ANYWHERE = new RegExp('(?:\u001b+)(O|N|\\[|\\[\\[)(?:' + [ var RE_FUNCTION_KEYCODE_ANYWHERE = new RegExp('(?:\u001b+)(O|N|\\[|\\[\\[)(?:' + [
@ -74,6 +75,7 @@ var RE_ESC_CODE_ANYWHERE = new RegExp( [
RE_FUNCTION_KEYCODE_ANYWHERE.source, RE_FUNCTION_KEYCODE_ANYWHERE.source,
RE_META_KEYCODE_ANYWHERE.source, RE_META_KEYCODE_ANYWHERE.source,
RE_DSR_RESPONSE_ANYWHERE.source, RE_DSR_RESPONSE_ANYWHERE.source,
RE_DEV_ATTR_RESPONSE_ANYWHERE.source,
/\u001b./.source /\u001b./.source
].join('|')); ].join('|'));
@ -110,6 +112,32 @@ function Client(input, output) {
// * http://www.ansi-bbs.org/ansi-bbs-core-server.html // * http://www.ansi-bbs.org/ansi-bbs-core-server.html
// * Christopher Jeffrey's Blessed library @ https://github.com/chjj/blessed/ // * Christopher Jeffrey's Blessed library @ https://github.com/chjj/blessed/
// //
this.getTermClient = function(deviceAttr) {
var termClient = {
//
// See http://www.fbl.cz/arctel/download/techman.pdf
//
// Known clients:
// * Irssi ConnectBot (Android)
//
'63;1;2' : 'arctel',
}[deviceAttr];
if(!termClient) {
if(_.startsWith(deviceAttr, '67;84;101;114;109')) {
//
// See https://github.com/protomouse/synchronet/blob/master/src/conio/cterm.txt
//
// Known clients:
// * SyncTERM
//
termClient = 'cterm';
}
}
return termClient;
};
this.isMouseInput = function(data) { this.isMouseInput = function(data) {
return /\x1b\[M/.test(data) || return /\x1b\[M/.test(data) ||
/\u001b\[M([\x00\u0020-\uffff]{3})/.test(data) || /\u001b\[M([\x00\u0020-\uffff]{3})/.test(data) ||
@ -256,12 +284,18 @@ function Client(input, output) {
var parts; var parts;
if((parts = RE_DSR_RESPONSE_ANYWHERE.exec(s))) { if((parts = RE_DSR_RESPONSE_ANYWHERE.exec(s))) {
if('R' === parts[2]) { if('R' === parts[2]) { // :TODO: this should be a assert -- currently only looking for R, unless we start to handle 'n', or others
var cprArgs = getIntArgArray(parts[1].split(';')); var cprArgs = getIntArgArray(parts[1].split(';'));
if(2 === cprArgs.length) { if(2 === cprArgs.length) {
self.emit('cursor position report', cprArgs); self.emit('cursor position report', cprArgs);
} }
} }
} else if((parts = RE_DEV_ATTR_RESPONSE_ANYWHERE.exec(s))) {
assert('c' === parts[2]);
var termClient = self.getTermClient(parts[1]);
if(termClient) {
self.term.termClient = termClient;
}
} else if('\r' === s) { } else if('\r' === s) {
key.name = 'return'; key.name = 'return';
} else if('\n' === s) { } else if('\n' === s) {

View file

@ -30,6 +30,7 @@ function ClientTerminal(output) {
var termType = 'unknown'; var termType = 'unknown';
var termHeight = 0; var termHeight = 0;
var termWidth = 0; var termWidth = 0;
var termClient = 'unknown';
// Raw values set by e.g. telnet NAWS, ENVIRONMENT, etc. // Raw values set by e.g. telnet NAWS, ENVIRONMENT, etc.
this.env = {}; this.env = {};
@ -64,9 +65,11 @@ function ClientTerminal(output) {
// SCOANSI // SCOANSI
// VT100 // VT100
// XTERM // XTERM
// * PuTTY
// LINUX // LINUX
// QNX // QNX
// SCREEN // SCREEN
// * ConnectBot
// //
if(this.isANSI()) { if(this.isANSI()) {
this.outputEncoding = 'cp437'; this.outputEncoding = 'cp437';
@ -75,6 +78,10 @@ function ClientTerminal(output) {
this.outputEncoding = 'utf8'; this.outputEncoding = 'utf8';
} }
// :TODO: according to this: http://mud-dev.wikidot.com/article:telnet-client-identification
// Windows telnet will send "VTNT". If so, set termClient='windows'
// there are some others on the page as well
Log.debug( { encoding : this.outputEncoding }, 'Set output encoding due to terminal type change'); Log.debug( { encoding : this.outputEncoding }, 'Set output encoding due to terminal type change');
} }
}); });
@ -100,6 +107,17 @@ function ClientTerminal(output) {
} }
} }
}); });
Object.defineProperty(this, 'termClient', {
get : function() {
return termClient;
},
set : function(tc) {
termClient = tc;
Log.debug( { termClient : this.termClient }, 'Set known terminal client');
}
});
} }
ClientTerminal.prototype.isANSI = function() { ClientTerminal.prototype.isANSI = function() {

View file

@ -92,6 +92,13 @@ function connectEntry(client) {
// :TODO: Enthral for example queries cursor position & checks if it worked. This might be good // :TODO: Enthral for example queries cursor position & checks if it worked. This might be good
// :TODO: How to detect e.g. if show/hide cursor can work? Probably can if CPR is avail // :TODO: How to detect e.g. if show/hide cursor can work? Probably can if CPR is avail
//
// Some terminal clients can be detected using a nonstandard ANSI DSR
//
term.rawWrite(ansi.queryDeviceAttributes(0));
// :TODO: PuTTY will apparently respond with "PuTTY" if a CTRL-E is sent to it
// //
// If we don't yet know the client term width/height, // If we don't yet know the client term width/height,
// try with a nonstandard ANSI DSR type request. // try with a nonstandard ANSI DSR type request.

View file

@ -112,17 +112,17 @@ function createMessageBaseTables() {
); );
dbs.message.run( dbs.message.run(
'CREATE TRIGGER message_before_update BEFORE UPDATE ON message BEGIN' + 'CREATE TRIGGER IF NOT EXISTS message_before_update BEFORE UPDATE ON message BEGIN' +
' DELETE FROM message_fts WHERE docid=old.rowid;' + ' DELETE FROM message_fts WHERE docid=old.rowid;' +
'END;' + 'END;' +
'CREATE TRIGGER message_before_delete BEFORE DELETE ON message BEGIN' + 'CREATE TRIGGER IF NOT EXISTS message_before_delete BEFORE DELETE ON message BEGIN' +
' DELETE FROM message_fts WHERE docid=old.rowid;' + ' DELETE FROM message_fts WHERE docid=old.rowid;' +
'END;' + 'END;' +
'' + '' +
'CREATE TRIGGER message_after_update AFTER UPDATE ON message BEGIN' + 'CREATE TRIGGER IF NOT EXISTS message_after_update AFTER UPDATE ON message BEGIN' +
' INSERT INTO message_fts(docid, subject, message) VALUES(new.rowid, new.subject, new.message);' + ' INSERT INTO message_fts(docid, subject, message) VALUES(new.rowid, new.subject, new.message);' +
'END;' + 'END;' +
'CREATE TRIGGER message_after_insert AFTER INSERT ON message BEGIN' + 'CREATE TRIGGER IF NOT EXISTS message_after_insert AFTER INSERT ON message BEGIN' +
' INSERT INTO message_fts(docid, subject, message) VALUES(new.rowid, new.subject, new.message);' + ' INSERT INTO message_fts(docid, subject, message) VALUES(new.rowid, new.subject, new.message);' +
'END;' 'END;'
); );

View file

@ -392,8 +392,9 @@ function MultiLineEditTextView(options) {
var absPos; var absPos;
if(self.getTextLength(index) > self.dimens.width) { if(self.getTextLength(index) > self.dimens.width) {
console.log('textLen=' + self.getTextLength(index) + ' / ' + self.dimens.width + ' / ' + //console.log('textLen=' + self.getTextLength(index) + ' / ' + self.dimens.width + ' / ' +
JSON.stringify(self.getAbsolutePosition(self.cursorPos.row, self.cursorPos.col))) // JSON.stringify(self.getAbsolutePosition(self.cursorPos.row, self.cursorPos.col)))
// //
// Update word wrapping and |cursorOffset| if the cursor // Update word wrapping and |cursorOffset| if the cursor
// was within the bounds of the wrapped text // was within the bounds of the wrapped text
@ -408,22 +409,27 @@ function MultiLineEditTextView(options) {
self.redrawRows(self.cursorPos.row, self.dimens.height); self.redrawRows(self.cursorPos.row, self.dimens.height);
if(!_.isUndefined(cursorOffset)) { if(!_.isUndefined(cursorOffset)) {
console.log('cursorOffset=' + cursorOffset) //console.log('cursorOffset=' + cursorOffset)
self.cursorBeginOfNextLine(); self.cursorBeginOfNextLine();
self.cursorPos.col += cursorOffset; self.cursorPos.col += cursorOffset;
self.client.term.rawWrite(ansi.right(cursorOffset)); self.client.term.rawWrite(ansi.right(cursorOffset));
} else { } else {
//console.log('this path')
self.moveClientCusorToCursorPos();
/*
self.cursorPos.row++; self.cursorPos.row++;
self.cursorPos.col = 1; // we just added 1 char self.cursorPos.col = 1; // we just added 1 char
absPos = self.getAbsolutePosition(self.cursorPos.row, self.cursorPos.col); absPos = self.getAbsolutePosition(self.cursorPos.row, self.cursorPos.col);
console.log('absPos=' + JSON.stringify(absPos)) console.log('absPos=' + JSON.stringify(absPos))
self.client.term.rawWrite(ansi.goto(absPos.row, absPos.col)); self.client.term.rawWrite(ansi.goto(absPos.row, absPos.col));
*/
} }
} else { } else {
// //
// We must only redraw from col -> end of current visible line // We must only redraw from col -> end of current visible line
// //
console.log('textLen=' + self.getTextLength(index)) //console.log('textLen=' + self.getTextLength(index))
absPos = self.getAbsolutePosition(self.cursorPos.row, self.cursorPos.col); absPos = self.getAbsolutePosition(self.cursorPos.row, self.cursorPos.col);
self.client.term.write( self.client.term.write(
ansi.hideCursor() + ansi.hideCursor() +

View file

@ -477,6 +477,11 @@ function TelnetClient(input, output) {
this.input.on('end', function() { this.input.on('end', function() {
self.emit('end'); self.emit('end');
}); });
this.input.on('error', function sockError(err) {
Log.debug(err); // :TODO: probably something better...
self.emit('end');
});
} }
util.inherits(TelnetClient, baseClient.Client); util.inherits(TelnetClient, baseClient.Client);