Merge pull request #407 from cognitivegears/feature/Remove_position_check

#222 - Removing Cursor Position Report
This commit is contained in:
Bryan Ashby 2022-04-05 19:34:05 -06:00 committed by GitHub
commit 3d1b97cf3b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 214 additions and 127 deletions

View File

@ -28,5 +28,8 @@
], ],
"comma-dangle": 0, "comma-dangle": 0,
"no-trailing-spaces" :"warn" "no-trailing-spaces" :"warn"
},
"parserOptions": {
"ecmaVersion": 2020
} }
} }

View File

@ -28,6 +28,33 @@ npm install # or simply 'yarn'
Report your issue on Xibalba BBS, hop in #enigma-bbs on FreeNode and chat, or Report your issue on Xibalba BBS, hop in #enigma-bbs on FreeNode and chat, or
[file a issue on GitHub](https://github.com/NuSkooler/enigma-bbs/issues). [file a issue on GitHub](https://github.com/NuSkooler/enigma-bbs/issues).
# 0.0.12-beta to 0.0.13-beta
* All features and changes are backwards compatible. There are a few new configuration options in a new `term` section in the configuration. These are all optional, but include the following options in case you use them:
```hjson
{
term: {
// checkUtf8Encoding requires the use of cursor position reports, which are not supported on all terminals.
// Using this with a terminal that does not support cursor position reports results in a 2 second delay
// during the connect process, but provides better autoconfiguration of utf-8
checkUtf8Encoding: true
// Checking the ANSI home position also requires the use of cursor position reports, which are not
// supported on all terminals. Using this with a terminal that does not support cursor position reports
// results in a 3 second delay during the connect process, but works around positioning problems with
// non-standard terminals.
checkAnsiHomePosition: true
}
}
```
In addition to these, there are also new options for `term.cp437TermList` and `term.utf8TermList`. Under most circumstances these should not need to be changed. If you want to customize these lists, more information is available in `config_default.js`
# 0.0.11-beta to 0.0.12-beta # 0.0.11-beta to 0.0.12-beta
* Be aware that `master` is now mainline! This means all `git pull`'s will yield the latest version. See [WHATSNEW](WHATSNEW.md) for more information. * Be aware that `master` is now mainline! This means all `git pull`'s will yield the latest version. See [WHATSNEW](WHATSNEW.md) for more information.
* **BREAKING CHANGE** There is no longer a `prompt.hjson` file. Prompts are now simply part of the menu set in the `prompts` section. If you have an existing system you will need to add your `prompt.hjson` to your `menu.hjson`'s `includes` section at a minimum. Example: * **BREAKING CHANGE** There is no longer a `prompt.hjson` file. Prompts are now simply part of the menu set in the `prompts` section. If you have an existing system you will need to add your `prompt.hjson` to your `menu.hjson`'s `includes` section at a minimum. Example:

View File

@ -1,6 +1,12 @@
# Whats New # Whats New
This document attempts to track **major** changes and additions in ENiGMA½. For details, see GitHub. This document attempts to track **major** changes and additions in ENiGMA½. For details, see GitHub.
## 0.0.13-beta
* Removed terminal `cursor position reports` from most locations in the code. This should greatly increase the number of terminal programs that work with Enigma 1/2. For more information, see [Issue #222](https://github.com/NuSkooler/enigma-bbs/issues/222). This may also resolve other issues, such as [Issue #365](https://github.com/NuSkooler/enigma-bbs/issues/365), and [Issue #320](https://github.com/NuSkooler/enigma-bbs/issues/320). Anyone that previously had terminal incompatibilities please re-check and let us know!
* Bumped up the minimum [Node.js](https://nodejs.org/en/) version to V14. This will allow more expressive Javascript programming syntax with ECMAScript 2020 to improve the development experience.
* Added new configuration options for `term.checkUtf8Encoding`, `term.checkAnsiHomePostion`, `term.cp437TermList`, and `term.utf8TermList`. More information on these options is available in `UPGRADE.md`
* Many additional backward-compatible bug fixes since the first release of 0.0.12-beta. See the [project repository](https://github.com/NuSkooler/enigma-bbs) for more information.
## 0.0.12-beta ## 0.0.12-beta
* The `master` branch has become mainline. What this means to users is `git pull` will always give you the latest and greatest. Make sure to read [Updating](./docs/admin/updating.md) and keep an eye on `WHATSNEW.md` (this file) and [UPGRADE](UPGRADE.md)! See also [ticket #276](https://github.com/NuSkooler/enigma-bbs/issues/276). * The `master` branch has become mainline. What this means to users is `git pull` will always give you the latest and greatest. Make sure to read [Updating](./docs/admin/updating.md) and keep an eye on `WHATSNEW.md` (this file) and [UPGRADE](UPGRADE.md)! See also [ticket #276](https://github.com/NuSkooler/enigma-bbs/issues/276).
* Development now occurs against [Node.js 14 LTS](https://github.com/nodejs/node/blob/master/doc/changelogs/CHANGELOG_V14.md). * Development now occurs against [Node.js 14 LTS](https://github.com/nodejs/node/blob/master/doc/changelogs/CHANGELOG_V14.md).
@ -127,4 +133,4 @@ submit: [
...LOTS more! ...LOTS more!
## Pre 0.0.8-alpha ## Pre 0.0.8-alpha
See GitHub See GitHub

View File

@ -21,8 +21,6 @@ function ANSIEscapeParser(options) {
events.EventEmitter.call(this); events.EventEmitter.call(this);
this.column = 1; this.column = 1;
this.row = 1;
this.scrollBack = 0;
this.graphicRendition = {}; this.graphicRendition = {};
this.parseState = { this.parseState = {
@ -36,11 +34,15 @@ function ANSIEscapeParser(options) {
trailingLF : 'default', // default|omit|no|yes, ... trailingLF : 'default', // default|omit|no|yes, ...
}); });
this.mciReplaceChar = miscUtil.valueWithDefault(options.mciReplaceChar, ''); this.mciReplaceChar = miscUtil.valueWithDefault(options.mciReplaceChar, '');
this.termHeight = miscUtil.valueWithDefault(options.termHeight, 25); this.termHeight = miscUtil.valueWithDefault(options.termHeight, 25);
this.termWidth = miscUtil.valueWithDefault(options.termWidth, 80); this.termWidth = miscUtil.valueWithDefault(options.termWidth, 80);
this.trailingLF = miscUtil.valueWithDefault(options.trailingLF, 'default'); this.trailingLF = miscUtil.valueWithDefault(options.trailingLF, 'default');
this.row = Math.min(options?.startRow ?? 1, this.termHeight);
self.moveCursor = function(cols, rows) { self.moveCursor = function(cols, rows) {
self.column += cols; self.column += cols;
self.row += rows; self.row += rows;
@ -69,14 +71,11 @@ function ANSIEscapeParser(options) {
}; };
self.clearScreen = function() { self.clearScreen = function() {
// :TODO: should be doing something with row/column? self.column = 1;
self.row = 1;
self.emit('clear screen'); self.emit('clear screen');
}; };
/*
self.rowUpdated = function() {
self.emit('row update', self.row + self.scrollBack);
};*/
self.positionUpdated = function() { self.positionUpdated = function() {
self.emit('position update', self.row, self.column); self.emit('position update', self.row, self.column);
@ -190,10 +189,11 @@ function ANSIEscapeParser(options) {
self.emit('mci', { self.emit('mci', {
mci : mciCode, position : [self.row, self.column],
id : id ? parseInt(id, 10) : null, mci : mciCode,
args : args, id : id ? parseInt(id, 10) : null,
SGR : ansi.getSGRFromGraphicRendition(self.graphicRendition, true) args : args,
SGR : ansi.getSGRFromGraphicRendition(self.graphicRendition, true)
}); });
if(self.mciReplaceChar.length > 0) { if(self.mciReplaceChar.length > 0) {
@ -215,6 +215,9 @@ function ANSIEscapeParser(options) {
} }
self.reset = function(input) { self.reset = function(input) {
self.column = 1;
self.row = Math.min(options?.startRow ?? 1, self.termHeight);
self.parseState = { self.parseState = {
// ignore anything past EOF marker, if any // ignore anything past EOF marker, if any
buffer : input.split(String.fromCharCode(0x1a), 1)[0], buffer : input.split(String.fromCharCode(0x1a), 1)[0],

View File

@ -269,19 +269,16 @@ function display(client, art, options, cb) {
termHeight : client.term.termHeight, termHeight : client.term.termHeight,
termWidth : client.term.termWidth, termWidth : client.term.termWidth,
trailingLF : options.trailingLF, trailingLF : options.trailingLF,
startRow : options.startRow,
}); });
let parseComplete = false; let parseComplete = false;
let cprListener;
let mciMap; let mciMap;
const mciCprQueue = []; const mciCprQueue = [];
let artHash; let artHash;
let mciMapFromCache; let mciMapFromCache;
function completed() { function completed() {
if(cprListener) {
client.removeListener('cursor position report', cprListener);
}
if(!options.disableMciCache && !mciMapFromCache) { if(!options.disableMciCache && !mciMapFromCache) {
// cache our MCI findings... // cache our MCI findings...
@ -314,18 +311,6 @@ function display(client, art, options, cb) {
// no cached MCI info // no cached MCI info
mciMap = {}; mciMap = {};
cprListener = function(pos) {
if(mciCprQueue.length > 0) {
mciMap[mciCprQueue.shift()].position = pos;
if(parseComplete && 0 === mciCprQueue.length) {
return completed();
}
}
};
client.on('cursor position report', cprListener);
let generatedId = 100; let generatedId = 100;
ansiParser.on('mci', mciInfo => { ansiParser.on('mci', mciInfo => {
@ -339,18 +324,16 @@ function display(client, art, options, cb) {
mapEntry.focusArgs = mciInfo.args; mapEntry.focusArgs = mciInfo.args;
} else { } else {
mciMap[mapKey] = { mciMap[mapKey] = {
args : mciInfo.args, position : mciInfo.position,
SGR : mciInfo.SGR, args : mciInfo.args,
code : mciInfo.mci, SGR : mciInfo.SGR,
id : id, code : mciInfo.mci,
id : id,
}; };
if(!mciInfo.id) { if(!mciInfo.id) {
++generatedId; ++generatedId;
} }
mciCprQueue.push(mapKey);
client.term.rawWrite(ansi.queryPos());
} }
}); });

View File

@ -4,11 +4,12 @@
// ENiGMA½ // ENiGMA½
var Log = require('./logger.js').log; var Log = require('./logger.js').log;
var renegadeToAnsi = require('./color_codes.js').renegadeToAnsi; var renegadeToAnsi = require('./color_codes.js').renegadeToAnsi;
const Config = require('./config.js').get;
var iconv = require('iconv-lite'); var iconv = require('iconv-lite');
var assert = require('assert'); var assert = require('assert');
var _ = require('lodash'); var _ = require('lodash');
exports.ClientTerminal = ClientTerminal; exports.ClientTerminal = ClientTerminal;
function ClientTerminal(output) { function ClientTerminal(output) {
@ -115,7 +116,8 @@ ClientTerminal.prototype.isNixTerm = function() {
return true; return true;
} }
return [ 'xterm', 'linux', 'screen', 'dumb', 'rxvt', 'konsole', 'gnome', 'x11 terminal emulator' ].includes(this.termType); const utf8TermList = Config().term.utf8TermList;
return utf8TermList.includes(this.termType);
}; };
ClientTerminal.prototype.isANSI = function() { ClientTerminal.prototype.isANSI = function() {
@ -153,7 +155,8 @@ ClientTerminal.prototype.isANSI = function() {
// linux: // linux:
// * JuiceSSH (note: TERM=linux also) // * JuiceSSH (note: TERM=linux also)
// //
return [ 'ansi', 'pcansi', 'pc-ansi', 'ansi-bbs', 'qansi', 'scoansi', 'syncterm', 'ansi-256color', 'ansi-256color-rgb' ].includes(this.termType); const cp437TermList = Config().term.cp437TermList;
return cp437TermList.includes(this.termType);
}; };
// :TODO: probably need to update these to convert IAC (0xff) -> IACIAC (escape it) // :TODO: probably need to update these to convert IAC (0xff) -> IACIAC (escape it)

View File

@ -17,6 +17,24 @@ module.exports = () => {
achievementFile : 'achievements.hjson', achievementFile : 'achievements.hjson',
}, },
term : {
// checkUtf8Encoding requires the use of cursor position reports, which are not supported on all terminals.
// Using this with a terminal that does not support cursor position reports results in a 2 second delay
// during the connect process, but provides better autoconfiguration of utf-8
checkUtf8Encoding : true,
// Checking the ANSI home position also requires the use of cursor position reports, which are not
// supported on all terminals. Using this with a terminal that does not support cursor position reports
// results in a 3 second delay during the connect process, but works around positioning problems with
// non-standard terminals.
checkAnsiHomePosition: true,
// List of terms that should be assumed to use cp437 encoding
cp437TermList : ['ansi', 'pcansi', 'pc-ansi', 'ansi-bbs', 'qansi', 'scoansi', 'syncterm', 'ansi-256color', 'ansi-256color-rgb'],
// List of terms that should be assumed to use utf8 encoding
utf8TermList : ['xterm', 'linux', 'screen', 'dumb', 'rxvt', 'konsole', 'gnome', 'x11 terminal emulator'],
},
users : { users : {
usernameMin : 2, usernameMin : 2,
usernameMax : 16, // Note that FidoNet wants 36 max usernameMax : 16, // Note that FidoNet wants 36 max

View File

@ -4,6 +4,7 @@
// ENiGMA½ // ENiGMA½
const ansi = require('./ansi_term.js'); const ansi = require('./ansi_term.js');
const Events = require('./events.js'); const Events = require('./events.js');
const Config = require('./config.js').get;
const { Errors } = require('./enig_error.js'); const { Errors } = require('./enig_error.js');
// deps // deps
@ -18,6 +19,13 @@ function ansiDiscoverHomePosition(client, cb) {
// think of home as 0,0. If this is the case, we need to offset // think of home as 0,0. If this is the case, we need to offset
// our positioning to accommodate for such. // our positioning to accommodate for such.
// //
if( !Config().term.checkAnsiHomePosition ) {
// Skip (and assume 1,1) if the home position check is disabled.
return cb(null);
}
const done = (err) => { const done = (err) => {
client.removeListener('cursor position report', cprListener); client.removeListener('cursor position report', cprListener);
clearTimeout(giveUpTimer); clearTimeout(giveUpTimer);
@ -68,8 +76,9 @@ function ansiAttemptDetectUTF8(client, cb) {
// //
// We currently only do this if the term hasn't already been ID'd as a // We currently only do this if the term hasn't already been ID'd as a
// "*nix" terminal -- that is, xterm, etc. // "*nix" terminal -- that is, xterm, etc.
// // Also skip this check if checkUtf8Encoding is disabled in the config
if(!client.term.isNixTerm()) {
if(!client.term.isNixTerm() || !Config().term.checkUtf8Encoding) {
return cb(null); return cb(null);
} }
@ -119,6 +128,8 @@ function ansiAttemptDetectUTF8(client, cb) {
return giveUp(); return giveUp();
}, 2000); }, 2000);
client.once('cursor position report', cprListener); client.once('cursor position report', cprListener);
client.term.rawWrite(ansi.goHome() + ansi.queryPos()); client.term.rawWrite(ansi.goHome() + ansi.queryPos());
} }
@ -216,7 +227,6 @@ function connectEntry(client, nextMenu) {
}, },
function discoverHomePosition(callback) { function discoverHomePosition(callback) {
ansiDiscoverHomePosition(client, () => { ansiDiscoverHomePosition(client, () => {
// :TODO: If CPR for home fully fails, we should bail out on the connection with an error, e.g. ANSI support required
return callback(null); // we try to continue anyway return callback(null); // we try to continue anyway
}); });
}, },

View File

@ -603,7 +603,7 @@ exports.FullScreenEditorModule = exports.getModule = class FullScreenEditorModul
theme.displayThemedAsset( theme.displayThemedAsset(
footerArt, footerArt,
self.client, self.client,
{ font : self.menuConfig.font }, { font : self.menuConfig.font, startRow: self.header.height + self.body.height },
function displayed(err, artData) { function displayed(err, artData) {
callback(err, artData); callback(err, artData);
} }
@ -626,19 +626,34 @@ exports.FullScreenEditorModule = exports.getModule = class FullScreenEditorModul
async.series( async.series(
[ [
function displayHeaderAndBody(callback) { function displayHeaderAndBody(callback) {
async.eachSeries( comps, function dispArt(n, next) { async.waterfall(
theme.displayThemedAsset( [
art[n], function displayHeader(callback) {
self.client, theme.displayThemedAsset(
{ font : self.menuConfig.font }, art['header'],
function displayed(err) { self.client,
next(err); { font : self.menuConfig.font },
function displayed(err, artInfo) {
return callback(err, artInfo);
}
);
},
function displayBody(artInfo, callback) {
theme.displayThemedAsset(
art['header'],
self.client,
{ font : self.menuConfig.font, startRow: artInfo.height + 1 },
function displayed(err, artInfo) {
return callback(err, artInfo);
}
);
} }
); ],
}, function complete(err) { function complete(err) {
//self.body.height = self.client.term.termHeight - self.header.height - 1; //self.body.height = self.client.term.termHeight - self.header.height - 1;
callback(err); callback(err);
}); }
);
}, },
function displayFooter(callback) { function displayFooter(callback) {
// we have to treat the footer special // we have to treat the footer special
@ -700,31 +715,39 @@ exports.FullScreenEditorModule = exports.getModule = class FullScreenEditorModul
assert(_.isObject(art)); assert(_.isObject(art));
async.series( async.waterfall(
[ [
function beforeDisplayArt(callback) { function beforeDisplayArt(callback) {
self.beforeArt(callback); self.beforeArt(callback);
}, },
function displayHeaderAndBodyArt(callback) { function displayHeader(callback) {
async.eachSeries( [ 'header', 'body' ], function dispArt(n, next) { theme.displayThemedAsset(
theme.displayThemedAsset( art.header,
art[n], self.client,
self.client, { font : self.menuConfig.font },
{ font : self.menuConfig.font }, function displayed(err, artInfo) {
function displayed(err, artData) { if(artInfo) {
if(artData) { mciData['header'] = artInfo;
mciData[n] = artData; self.header = {height: artInfo.height};
self[n] = { height : artData.height };
}
next(err);
} }
); return callback(err, artInfo);
}, function complete(err) { }
callback(err); );
});
}, },
function displayFooter(callback) { function displayBody(artInfo, callback) {
theme.displayThemedAsset(
art.body,
self.client,
{ font : self.menuConfig.font, startRow: artInfo.height + 1 },
function displayed(err, artInfo) {
if(artInfo) {
mciData['body'] = artInfo;
self.body = {height: artInfo.height - self.header.height};
}
return callback(err, artInfo);
});
},
function displayFooter(artInfo, callback) {
self.setInitialFooterMode(); self.setInitialFooterMode();
var footerName = self.getFooterName(); var footerName = self.getFooterName();
@ -825,8 +848,7 @@ exports.FullScreenEditorModule = exports.getModule = class FullScreenEditorModul
{ {
const fromView = self.viewControllers.header.getView(MciViewIds.header.from); const fromView = self.viewControllers.header.getView(MciViewIds.header.from);
const area = getMessageAreaByTag(self.messageAreaTag); const area = getMessageAreaByTag(self.messageAreaTag);
if(fromView !== undefined) {
if(fromView !== undefined) {
if(area && area.realNames) { if(area && area.realNames) {
fromView.setText(self.client.user.properties[UserProps.RealName] || self.client.user.username); fromView.setText(self.client.user.properties[UserProps.RealName] || self.client.user.username);
} else { } else {

View File

@ -56,14 +56,14 @@ exports.MenuModule = class MenuModule extends PluginModule {
initSequence() { initSequence() {
const self = this; const self = this;
const mciData = {}; const mciData = {};
let pausePosition; let pausePosition = {row: 0, column: 0};
const hasArt = () => { const hasArt = () => {
return _.isString(self.menuConfig.art) || return _.isString(self.menuConfig.art) ||
(Array.isArray(self.menuConfig.art) && _.has(self.menuConfig.art[0], 'acs')); (Array.isArray(self.menuConfig.art) && _.has(self.menuConfig.art[0], 'acs'));
}; };
async.series( async.waterfall(
[ [
function beforeArtInterrupt(callback) { function beforeArtInterrupt(callback) {
return self.displayQueuedInterruptions(callback); return self.displayQueuedInterruptions(callback);
@ -73,7 +73,7 @@ exports.MenuModule = class MenuModule extends PluginModule {
}, },
function displayMenuArt(callback) { function displayMenuArt(callback) {
if(!hasArt()) { if(!hasArt()) {
return callback(null); return callback(null, null);
} }
self.displayAsset( self.displayAsset(
@ -86,18 +86,15 @@ exports.MenuModule = class MenuModule extends PluginModule {
mciData.menu = artData.mciMap; mciData.menu = artData.mciMap;
} }
return callback(null); // any errors are non-fatal if(artData) {
pausePosition.row = artData.height + 1;
}
return callback(null, artData); // any errors are non-fatal
} }
); );
}, },
function moveToPromptLocation(callback) { function displayPromptArt(artData, callback) {
if(self.menuConfig.prompt) {
// :TODO: fetch and move cursor to prompt location, if supplied. See notes/etc. on placements
}
return callback(null);
},
function displayPromptArt(callback) {
if(!_.isString(self.menuConfig.prompt)) { if(!_.isString(self.menuConfig.prompt)) {
return callback(null); return callback(null);
} }
@ -106,41 +103,41 @@ exports.MenuModule = class MenuModule extends PluginModule {
return callback(Errors.MissingConfig('Prompt specified but no "promptConfig" block found')); return callback(Errors.MissingConfig('Prompt specified but no "promptConfig" block found'));
} }
const options = Object.assign({}, self.menuConfig.config);
if(_.isNumber(artData?.height)) {
options.startRow = artData.height + 1;
}
self.displayAsset( self.displayAsset(
self.menuConfig.promptConfig.art, self.menuConfig.promptConfig.art,
self.menuConfig.config, options,
(err, artData) => { (err, artData) => {
if(artData) { if(artData) {
mciData.prompt = artData.mciMap; mciData.prompt = artData.mciMap;
pausePosition.row = artData.height + 1;
} }
return callback(err); // pass err here; prompts *must* have art return callback(err); // pass err here; prompts *must* have art
} }
); );
}, },
function recordCursorPosition(callback) {
if(!self.shouldPause()) {
return callback(null); // cursor position not needed
}
self.client.once('cursor position report', pos => {
pausePosition = { row : pos[0], col : 1 };
self.client.log.trace('After art position recorded', pausePosition );
return callback(null);
});
self.client.term.rawWrite(ansi.queryPos());
},
function afterArtDisplayed(callback) { function afterArtDisplayed(callback) {
return self.mciReady(mciData, callback); return self.mciReady(mciData, callback);
}, },
function displayPauseIfRequested(callback) { function displayPauseIfRequested(callback) {
if(!self.shouldPause()) { if(!self.shouldPause()) {
return callback(null); return callback(null, null);
}
if(self.client.term.termHeight > 0 && pausePosition.row > self.client.termHeight) {
// If this scrolled, the prompt will go to the bottom of the screen
pausePosition.row = self.client.termHeight;
} }
return self.pausePrompt(pausePosition, callback); return self.pausePrompt(pausePosition, callback);
}, },
function finishAndNext(callback) { function finishAndNext(artInfo, callback) {
self.finishedLoading(); self.finishedLoading();
self.realTimeInterrupt = 'allowed'; self.realTimeInterrupt = 'allowed';
return self.autoNextMenu(callback); return self.autoNextMenu(callback);
@ -512,7 +509,7 @@ exports.MenuModule = class MenuModule extends PluginModule {
this.optionalMoveToPosition(position); this.optionalMoveToPosition(position);
return theme.displayThemedPause(this.client, cb); return theme.displayThemedPause(this.client, {position}, cb);
} }
promptForInput( { formName, formId, promptName, prevFormName, position } = {}, options, cb) { promptForInput( { formName, formId, promptName, prevFormName, position } = {}, options, cb) {

View File

@ -171,22 +171,15 @@ exports.getModule = class ShowArtModule extends MenuModule {
return callback(err); return callback(err);
} }
const mciData = { menu : artData.mciMap }; const mciData = { menu : artData.mciMap };
return callback(null, mciData); if(self.client.term.termHeight > 0 && artData.height > self.client.term.termHeight) {
// We must have scrolled, adjust the positioning for pause
artData.height = self.client.term.termHeight;
}
const pausePosition = { row: artData.height + 1, col: 1};
return callback(null, mciData, pausePosition);
} }
); );
}, },
function recordCursorPosition(mciData, callback) {
if(!options.pause) {
return callback(null, mciData, null); // cursor position not needed
}
self.client.once('cursor position report', pos => {
const pausePosition = { row : pos[0], col : 1 };
return callback(null, mciData, pausePosition);
});
self.client.term.rawWrite(ANSI.queryPos());
},
function afterArtDisplayed(mciData, pausePosition, callback) { function afterArtDisplayed(mciData, pausePosition, callback) {
self.mciReady(mciData, err => { self.mciReady(mciData, err => {
return callback(err, pausePosition); return callback(err, pausePosition);

View File

@ -495,6 +495,7 @@ function displayPreparedArt(options, artInfo, cb) {
sauce : artInfo.sauce, sauce : artInfo.sauce,
font : options.font, font : options.font,
trailingLF : options.trailingLF, trailingLF : options.trailingLF,
startRow : options.startRow,
}; };
art.display(options.client, artInfo.data, displayOpts, (err, mciMap, extraInfo) => { art.display(options.client, artInfo.data, displayOpts, (err, mciMap, extraInfo) => {
return cb(err, { mciMap : mciMap, artInfo : artInfo, extraInfo : extraInfo } ); return cb(err, { mciMap : mciMap, artInfo : artInfo, extraInfo : extraInfo } );
@ -551,6 +552,7 @@ function displayThemedPrompt(name, client, options, cb) {
if(options.clearScreen) { if(options.clearScreen) {
client.term.rawWrite(ansi.resetScreen()); client.term.rawWrite(ansi.resetScreen());
options.position = {row: 1, column: 1};
} }
// //
@ -583,12 +585,15 @@ function displayThemedPrompt(name, client, options, cb) {
return callback(null, promptConfig, artInfo); return callback(null, promptConfig, artInfo);
} }
client.once('cursor position report', pos => { if(_.isNumber(options?.position?.row)) {
artInfo.startRow = pos[0] - artInfo.height; artInfo.startRow = options.position.row;
return callback(null, promptConfig, artInfo); if(client.term.termHeight > 0 && artInfo.startRow + artInfo.height > client.term.termHeight) {
}); // in this case, we will have scrolled
artInfo.startRow = client.term.termHeight - artInfo.height;
}
}
client.term.rawWrite(ansi.queryPos()); return callback(null, promptConfig, artInfo);
}, },
function createMCIViews(promptConfig, artInfo, callback) { function createMCIViews(promptConfig, artInfo, callback) {
const assocViewController = usingTempViewController ? new ViewController( { client : client } ) : options.viewController; const assocViewController = usingTempViewController ? new ViewController( { client : client } ) : options.viewController;
@ -614,7 +619,9 @@ function displayThemedPrompt(name, client, options, cb) {
}); });
}, },
function clearPauseArt(artInfo, assocViewController, callback) { function clearPauseArt(artInfo, assocViewController, callback) {
if(options.clearPrompt) { // Only clear with height if clearPrompt is true and if we were able
// to determine the row
if(options.clearPrompt && artInfo.startRow) {
if(artInfo.startRow && artInfo.height) { if(artInfo.startRow && artInfo.height) {
client.term.rawWrite(ansi.goto(artInfo.startRow, 1)); client.term.rawWrite(ansi.goto(artInfo.startRow, 1));

View File

@ -6,7 +6,7 @@ For Linux environments it's recommended you run the [install script](install-scr
do things manually, read on... do things manually, read on...
## Prerequisites ## Prerequisites
* [Node.js](https://nodejs.org/) version **v12.x LTS or higher** (Other versions may work but are not supported). * [Node.js](https://nodejs.org/) version **v14.x LTS or higher**. Versions under v14 are known not to work due to language level changes.
* :bulb: It is **highly** recommended to use [Node Version Manager (NVM)](https://github.com/creationix/nvm) to manage your Node.js installation if you're on a Linux/Unix environment. * :bulb: It is **highly** recommended to use [Node Version Manager (NVM)](https://github.com/creationix/nvm) to manage your Node.js installation if you're on a Linux/Unix environment.
* [Python](https://www.python.org/downloads/) for compiling Node.js packages with native extensions via `node-gyp`. * [Python](https://www.python.org/downloads/) for compiling Node.js packages with native extensions via `node-gyp`.

View File

@ -67,6 +67,21 @@
description : 'Yet another awesome ENiGMA½ BBS' description : 'Yet another awesome ENiGMA½ BBS'
} }
term: {
// checkUtf8Encoding requires the use of cursor position reports, which are not supported on all terminals.
// Using this with a terminal that does not support cursor position reports results in a 2 second delay
// during the connect process, but provides better autoconfiguration of utf-8
checkUtf8Encoding : true
// Checking the ANSI home position also requires the use of cursor position reports, which are not
// supported on all terminals. Using this with a terminal that does not support cursor position reports
// results in a 3 second delay during the connect process, but works around positioning problems with
// non-standard terminals.
checkAnsiHomePosition: true
// other options here include cp437TermList and utf8TermList, see config_default.js for more information
}
paths: { paths: {
// //
// Other paths can also be configured as well, // Other paths can also be configured as well,

View File

@ -1,6 +1,6 @@
{ {
"name": "enigma-bbs", "name": "enigma-bbs",
"version": "0.0.12-beta", "version": "0.0.13-beta",
"description": "ENiGMA½ Bulletin Board System", "description": "ENiGMA½ Bulletin Board System",
"author": "Bryan Ashby <bryan@l33t.codes>", "author": "Bryan Ashby <bryan@l33t.codes>",
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
@ -62,9 +62,9 @@
"yazl": "^2.5.1" "yazl": "^2.5.1"
}, },
"devDependencies": { "devDependencies": {
"eslint": "7.19.0" "eslint": "8.12.0"
}, },
"engines": { "engines": {
"node": ">=12" "node": ">=14"
} }
} }