prevent memory leaks when using multiple screens. see #157.
This commit is contained in:
parent
44017d988b
commit
c488c08501
|
@ -436,6 +436,10 @@ The screen on which every other node renders.
|
|||
- __screenshot([xi, xl, yi, yl])__ - Take an SGR screenshot of the screen
|
||||
within the region. Returns a string containing only characters and SGR codes.
|
||||
Can be displayed by simply echoing it in a terminal.
|
||||
- __destroy()__ - destroy the screen object and remove it from the global list.
|
||||
only useful if using multiple screens.
|
||||
- __program.destroy()__ - destroy the program object and remove it from the
|
||||
global list. only useful if using multiple programs.
|
||||
|
||||
|
||||
#### Element (from Node)
|
||||
|
|
|
@ -31,6 +31,8 @@ function Program(options) {
|
|||
return new Program(options);
|
||||
}
|
||||
|
||||
Program.bind(this);
|
||||
|
||||
EventEmitter.call(this);
|
||||
|
||||
if (!options || options.__proto__ !== Object.prototype) {
|
||||
|
@ -104,19 +106,6 @@ function Program(options) {
|
|||
this._buf = '';
|
||||
this._flush = this.flush.bind(this);
|
||||
|
||||
unshiftEvent(process, 'exit', function() {
|
||||
// Ensure the buffer is flushed (it should
|
||||
// always be at this point, but who knows).
|
||||
self.flush();
|
||||
// Ensure _exiting is set (could technically
|
||||
// use process._exiting).
|
||||
self._exiting = true;
|
||||
});
|
||||
|
||||
if (!Program.global) {
|
||||
Program.global = this;
|
||||
}
|
||||
|
||||
if (options.tput !== false) {
|
||||
this.setupTput();
|
||||
}
|
||||
|
@ -124,8 +113,41 @@ function Program(options) {
|
|||
this.listen();
|
||||
}
|
||||
|
||||
Program.global = null;
|
||||
|
||||
Program.total = 0;
|
||||
|
||||
Program.list = [];
|
||||
|
||||
Program.bind = function(program) {
|
||||
if (!Program.global) {
|
||||
Program.global = program;
|
||||
}
|
||||
|
||||
if (!~Program.list.indexOf(program)) {
|
||||
Program.list.push(program);
|
||||
Program.total++;
|
||||
}
|
||||
|
||||
if (Program._bound) return;
|
||||
Program._bound = true;
|
||||
|
||||
unshiftEvent(process, 'exit', function() {
|
||||
Program.list.forEach(function(program) {
|
||||
// Ensure the buffer is flushed (it should
|
||||
// always be at this point, but who knows).
|
||||
program.flush();
|
||||
// Ensure _exiting is set (could technically
|
||||
// use process._exiting).
|
||||
program._exiting = true;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
Program.prototype.__proto__ = EventEmitter.prototype;
|
||||
|
||||
Program.prototype.type = 'program';
|
||||
|
||||
Program.prototype.log = function() {
|
||||
return this._log('LOG', util.format.apply(util, arguments));
|
||||
};
|
||||
|
@ -272,9 +294,8 @@ Program.prototype.listen = function() {
|
|||
var keys = require('./keys')
|
||||
, self = this;
|
||||
|
||||
if (!this.output.isTTY) {
|
||||
// TODO: Add an ncurses-like warning.
|
||||
}
|
||||
if (this.input._blessedListened) return;
|
||||
this.input._blessedListened = true;
|
||||
|
||||
// unshiftEvent(process, 'exit', function() {
|
||||
// if (self._originalTitle) {
|
||||
|
@ -344,6 +365,9 @@ Program.prototype.listen = function() {
|
|||
}
|
||||
});
|
||||
|
||||
if (this.output._blessedListened) return;
|
||||
this.output._blessedListened = true;
|
||||
|
||||
// Output
|
||||
function resize() {
|
||||
self.cols = self.output.columns;
|
||||
|
@ -366,6 +390,18 @@ Program.prototype.listen = function() {
|
|||
});
|
||||
};
|
||||
|
||||
Program.prototype.destroy = function() {
|
||||
var index = Program.list.indexOf(this);
|
||||
if (~index) {
|
||||
Program.list.splice(index, 1);
|
||||
Program.total--;
|
||||
if (Program.total === 0) {
|
||||
Program.global = null;
|
||||
}
|
||||
this.emit('destroy');
|
||||
}
|
||||
};
|
||||
|
||||
Program.prototype.key = function(key, listener) {
|
||||
if (typeof key === 'string') key = key.split(/\s*,\s*/);
|
||||
key.forEach(function(key) {
|
||||
|
|
|
@ -35,6 +35,8 @@ function Screen(options) {
|
|||
return new Screen(options);
|
||||
}
|
||||
|
||||
Screen.bind(this);
|
||||
|
||||
options = options || {};
|
||||
if (options.rsety && options.listen) {
|
||||
options = { program: options };
|
||||
|
@ -64,10 +66,6 @@ function Screen(options) {
|
|||
|
||||
this.tput = this.program.tput;
|
||||
|
||||
if (!Screen.global) {
|
||||
Screen.global = this;
|
||||
}
|
||||
|
||||
Node.call(this, options);
|
||||
|
||||
this.autoPadding = options.autoPadding !== false;
|
||||
|
@ -175,35 +173,6 @@ function Screen(options) {
|
|||
|
||||
this.setMaxListeners(Infinity);
|
||||
|
||||
Screen.total++;
|
||||
|
||||
process.on('uncaughtException', function(err) {
|
||||
if (process.listeners('uncaughtException').length > Screen.total) {
|
||||
return;
|
||||
}
|
||||
self.leave();
|
||||
err = err || new Error('Uncaught Exception.');
|
||||
console.error(err.stack ? err.stack + '' : err + '');
|
||||
nextTick(function() {
|
||||
process.exit(1);
|
||||
});
|
||||
});
|
||||
|
||||
['SIGTERM', 'SIGINT', 'SIGQUIT'].forEach(function(signal) {
|
||||
process.on(signal, function() {
|
||||
if (process.listeners(signal).length > Screen.total) {
|
||||
return;
|
||||
}
|
||||
nextTick(function() {
|
||||
process.exit(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
process.on('exit', function() {
|
||||
self.leave();
|
||||
});
|
||||
|
||||
this.enter();
|
||||
|
||||
this.postEnter();
|
||||
|
@ -213,6 +182,59 @@ Screen.global = null;
|
|||
|
||||
Screen.total = 0;
|
||||
|
||||
Screen.list = [];
|
||||
|
||||
Screen.signals = true;
|
||||
|
||||
Screen.bind = function(screen) {
|
||||
if (!Screen.global) {
|
||||
Screen.global = screen;
|
||||
}
|
||||
|
||||
if (!~Screen.list.indexOf(screen)) {
|
||||
Screen.list.push(screen);
|
||||
Screen.total++;
|
||||
}
|
||||
|
||||
if (Screen._bound) return;
|
||||
Screen._bound = true;
|
||||
|
||||
process.on('uncaughtException', function(err) {
|
||||
if (process.listeners('uncaughtException').length > Screen.total) {
|
||||
return;
|
||||
}
|
||||
Screen.list.slice().forEach(function(screen) {
|
||||
screen.destroy();
|
||||
});
|
||||
err = err || new Error('Uncaught Exception.');
|
||||
console.error(err.stack ? err.stack + '' : err + '');
|
||||
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 > Screen.total) {
|
||||
return;
|
||||
}
|
||||
nextTick(function() {
|
||||
process.exit(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
process.on('exit', function() {
|
||||
Screen.list.slice().forEach(function(screen) {
|
||||
screen.destroy();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
Screen.prototype.__proto__ = Node.prototype;
|
||||
|
||||
Screen.prototype.type = 'screen';
|
||||
|
@ -284,6 +306,7 @@ Screen.prototype.postEnter = function() {
|
|||
var self = this;
|
||||
if (this.options.debug) {
|
||||
this.debugLog = new Log({
|
||||
screen: this,
|
||||
parent: this,
|
||||
hidden: true,
|
||||
draggable: true,
|
||||
|
@ -326,6 +349,22 @@ Screen.prototype.postEnter = function() {
|
|||
}
|
||||
};
|
||||
|
||||
Screen.prototype.destroy = function() {
|
||||
this.leave();
|
||||
|
||||
var index = Screen.list.indexOf(this);
|
||||
if (~index) {
|
||||
Screen.list.splice(index, 1);
|
||||
Screen.total--;
|
||||
if (Screen.total === 0) {
|
||||
Screen.global = null;
|
||||
}
|
||||
this.emit('destroy');
|
||||
}
|
||||
|
||||
this.program.destroy();
|
||||
};
|
||||
|
||||
Screen.prototype.log = function() {
|
||||
if (this.debugLog) {
|
||||
this.debugLog.log.apply(this.debugLog, arguments);
|
||||
|
|
Loading…
Reference in New Issue