From f8fd4253f26a8eadc05023980a36934f59ad4c48 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Wed, 29 Jul 2015 19:56:54 -0700 Subject: [PATCH] remove listeners property on screen.destroy and program.destroy. fix ignoreLocked. --- README.md | 4 +- lib/program.js | 98 ++++++++++++++++++++++++------------------- lib/widgets/screen.js | 55 ++++++++++++------------ test/widget-exit.js | 36 ++++++++++++++++ 4 files changed, 118 insertions(+), 75 deletions(-) create mode 100644 test/widget-exit.js diff --git a/README.md b/README.md index a6dae9a..73c66a0 100644 --- a/README.md +++ b/README.md @@ -309,8 +309,8 @@ The screen on which every other node renders. debug console which will display when pressing F12. It will display all log and debug messages. - __ignoreLocked__ - Array of keys in their full format (e.g. `C-c`) to ignore - when keys are locked. Useful for creating a key that will _always_ exit no - matter whether the keys are locked. + when keys are locked or grabbed. Useful for creating a key that will _always_ + exit no matter whether the keys are locked. - __dockBorders__ - Automatically "dock" borders with other elements instead of overlapping, depending on position (__experimental__). For example: These border-overlapped elements: diff --git a/lib/program.js b/lib/program.js index ab78d8f..79730a7 100644 --- a/lib/program.js +++ b/lib/program.js @@ -132,8 +132,12 @@ Program.bind = function(program) { if (Program._bound) return; Program._bound = true; - unshiftEvent(process, 'exit', function() { + unshiftEvent(process, 'exit', Program._exitHandler = function() { Program.instances.forEach(function(program) { + // Potentially reset window title on exit: + // if (program._originalTitle) { + // program.setTitle(program._originalTitle); + // } // Ensure the buffer is flushed (it should // always be at this point, but who knows). program.flush(); @@ -142,15 +146,6 @@ Program.bind = function(program) { program._exiting = true; }); }); - - // Potentially reset window title on exit: - // unshiftEvent(process, 'exit', function() { - // Program.instances.forEach(function(program) { - // if (program._originalTitle) { - // program.setTitle(program._originalTitle); - // } - // }); - // }); }; Program.prototype.__proto__ = EventEmitter.prototype; @@ -307,8 +302,9 @@ Program.prototype.term = function(is) { }; Program.prototype.listen = function() { + var self = this; + // Potentially reset window title on exit: - // var self = this; // if (!this.isRxvt) { // if (!this.isVTE) this.setTitleModeFeature(3); // this.manipulateWindow(21, function(err, data) { @@ -319,14 +315,35 @@ Program.prototype.listen = function() { // Listen for keys/mouse on input if (!this.input._blessedListened) { - this.input._blessedListened = true; + this.input._blessedListened = 1; this._listenInput(); + } else { + this.input._blessedListened++; } + this.on('newListener', this._newHandler = function fn(type) { + if (type === 'keypress' || type === 'mouse') { + self.removeListener('newListener', fn); + if (self.input.setRawMode && !self.input.isRaw) { + self.input.setRawMode(true); + self.input.resume(); + } + } + }); + + this.on('newListener', function fn(type) { + if (type === 'mouse') { + self.removeListener('newListener', fn); + self.bindMouse(); + } + }); + // Listen for resize on output if (!this.output._blessedListened) { - this.output._blessedListened = true; + this.output._blessedListened = 1; this._listenOutput(); + } else { + this.output._blessedListened++; } }; @@ -335,7 +352,7 @@ Program.prototype._listenInput = function() { , self = this; // Input - this.input.on('keypress', function(ch, key) { + this.input.on('keypress', this.input._keypressHandler = function(ch, key) { key = key || { ch: ch }; if (key.name === 'undefined' @@ -371,7 +388,7 @@ Program.prototype._listenInput = function() { }); }); - this.input.on('data', function(data) { + this.input.on('data', this.input._dataHandler = function(data) { Program.instances.forEach(function(program) { if (program.input !== self.input) return; program.emit('data', data); @@ -379,29 +396,6 @@ Program.prototype._listenInput = function() { }); keys.emitKeypressEvents(this.input); - - this.on('newListener', function fn(type) { - if (type === 'keypress' || type === 'mouse') { - Program.instances.forEach(function(program) { - if (program.input !== self.input) return; - program.removeListener('newListener', fn); - if (program.input.setRawMode && !program.input.isRaw) { - program.input.setRawMode(true); - program.input.resume(); - } - }); - } - }); - - this.on('newListener', function fn(type) { - if (type === 'mouse') { - Program.instances.forEach(function(program) { - if (program.input !== self.input) return; - program.removeListener('newListener', fn); - program.bindMouse(); - }); - } - }); }; Program.prototype._listenOutput = function() { @@ -423,7 +417,7 @@ Program.prototype._listenOutput = function() { }); } - this.output.on('resize', function() { + this.output.on('resize', this.output._resizeHandler = function() { Program.instances.forEach(function(program) { if (program.output !== self.output) return; if (!program.options.resizeTimeout) { @@ -454,12 +448,20 @@ Program.prototype.destroy = function() { if (Program.total === 0) { Program.global = null; - process.removeAllListeners('exit'); + process.removeListener('exit', Program._exitHandler); + delete Program._exitHandler; - this.input.removeAllListeners('keypress'); - this.input.removeAllListeners('data'); - this.input.removeAllListeners('newListener'); - this.output.removeAllListeners('resize'); + delete Program._bound; + } + + this.input._blessedListened--; + this.output._blessedListened--; + + if (this.input._blessedListened === 0) { + this.input.removeListener('keypress', this.input._keypressHandler); + this.input.removeListener('data', this.input._dataHandler); + delete this.input._keypressHandler; + delete this.input._dataHandler; if (this.input.setRawMode) { if (this.input.isRaw) { @@ -471,6 +473,14 @@ Program.prototype.destroy = function() { } } + if (this.output._blessedListened === 0) { + this.output.removeListener('resize', this.output._resizeHandler); + delete this.output._resizeHandler; + } + + this.removeListener('newListener', this._newHandler); + delete this._newHandler; + this.destroyed = true; this.emit('destroy'); } diff --git a/lib/widgets/screen.js b/lib/widgets/screen.js index 8710653..a18db24 100644 --- a/lib/widgets/screen.js +++ b/lib/widgets/screen.js @@ -35,7 +35,7 @@ function Screen(options) { return new Screen(options); } - Screen.bind(this, options || {}); + Screen.bind(this); options = options || {}; if (options.rsety && options.listen) { @@ -193,14 +193,9 @@ Screen.total = 0; Screen.instances = []; -Screen.signals = true; - -Screen.bind = function(screen, options) { +Screen.bind = function(screen) { if (!Screen.global) { Screen.global = screen; - if (options.signals === false) { - Screen.signals = false; - } } if (!~Screen.instances.indexOf(screen)) { @@ -211,7 +206,7 @@ Screen.bind = function(screen, options) { if (Screen._bound) return; Screen._bound = true; - process.on('uncaughtException', function(err) { + process.on('uncaughtException', Screen._exceptionHandler = function(err) { if (process.listeners('uncaughtException').length > 1) { return; } @@ -220,33 +215,24 @@ Screen.bind = function(screen, options) { }); err = err || new Error('Uncaught Exception.'); console.error(err.stack ? err.stack + '' : err + ''); - if (Screen.total === 0) { - return process.exit(1); - } nextTick(function() { process.exit(1); }); }); - // XXX Multiple signal handlers and removal of signal - // handlers does not work, so we have this option instead. - if (Screen.signals) { - ['SIGTERM', 'SIGINT', 'SIGQUIT'].forEach(function(signal) { - process.on(signal, function() { - if (process.listeners(signal).length > 1) { - return; - } - if (Screen.total === 0) { - return process.exit(0); - } - nextTick(function() { - process.exit(0); - }); + ['SIGTERM', 'SIGINT', 'SIGQUIT'].forEach(function(signal) { + var name = '_' + signal.toLowerCase() + 'Handler'; + process.on(signal, Screen[name] = function() { + if (process.listeners(signal).length > 1) { + return; + } + nextTick(function() { + process.exit(0); }); }); - } + }); - process.on('exit', function() { + process.on('exit', Screen._exitHandler = function() { Screen.instances.slice().forEach(function(screen) { screen.destroy(); }); @@ -404,7 +390,18 @@ Screen.prototype.destroy = function() { if (Screen.total === 0) { Screen.global = null; - process.removeAllListeners('uncaughtException'); + process.removeListener('uncaughtException', Screen._exceptionHandler); + process.removeListener('SIGTERM', Screen._sigtermHandler); + process.removeListener('SIGINT', Screen._sigintHandler); + process.removeListener('SIGQUIT', Screen._sigquitHandler); + process.removeListener('exit', Screen._exitHandler); + delete Screen._exceptionHandler; + delete Screen._sigtermHandler; + delete Screen._sigintHandler; + delete Screen._sigquitHandler; + delete Screen._exitHandler; + + delete Screen._bound; } this.destroyed = true; @@ -564,7 +561,7 @@ Screen.prototype._listenKeys = function(el) { var focused = self.focused , grabKeys = self.grabKeys; - if (!grabKeys) { + if (!grabKeys || ~self.ignoreLocked.indexOf(key.full)) { self.emit('keypress', ch, key); self.emit('key ' + key.full, ch, key); } diff --git a/test/widget-exit.js b/test/widget-exit.js new file mode 100644 index 0000000..d6bb9f5 --- /dev/null +++ b/test/widget-exit.js @@ -0,0 +1,36 @@ +var blessed = require('../'); + +var screen = blessed.screen({ + dump: __dirname + '/logs/exit.log', + smartCSR: true, + autoPadding: true, + warnings: true, + ignoreLocked: ['C-q'] +}); + +var box = blessed.prompt({ + parent: screen, + left: 'center', + top: 'center', + width: '70%', + height: 'shrink', + border: 'line' +}); + +screen.render(); + +box.input('Input: ', '', function(err, data) { + screen.destroy(); + if (process.argv[2] === 'resume') { + process.stdin.resume(); + } else if (process.argv[2] === 'end') { + process.stdin.setRawMode(false); + process.stdin.end(); + } + if (err) throw err; + console.log('Input: ' + data); +}); + +screen.key('C-q', function(ch, key) { + return process.exit(0); +});