Image and Terminal element.

This commit is contained in:
Christopher Jeffrey 2015-02-11 17:20:22 -08:00
parent 267e77a089
commit 982e075a90
4 changed files with 186 additions and 107 deletions

View File

@ -871,24 +871,37 @@ A radio button which can be used in a form element.
#### Terminal (from Box)
A box which spins up a pseudo terminal and renders the output. (Requires
term.js to be installed).
A box which spins up a pseudo terminal and renders the output. Useful for
writing a terminal multiplexer, or something similar to an mc-like file
manager. Requires term.js and pty.js to be installed. See
`example/multiplex.js` for an example terminal multiplexer.
##### Options:
- inherits all from Box.
- **handler** - handler for input data.
- **shell** - name of shell. `$SHELL` by default.
- **args** - args for shell.
- **cursor** - can be `line`, `underline`, and `block`.
- Other options similar to term.js'.
##### Properties:
- inherits all from Box.
- **term** - reference to the headless term.js terminal.
- **pty** - reference to the pty.js pseudo terminal.
##### Events:
- inherits all from Box.
- **title** - window title from terminal.
- Other events similar to ScrollableBox.
##### Methods:
- inherits all from Box.
- **write(data)** - write data to the terminal.
- Other methods similar to ScrollableBox.
#### Image (from Box)
@ -900,8 +913,9 @@ terminals.
##### Options:
- inherits all from Box.
- **file** - path to image
- **w3m** - path to w3mimgdisplay
- **file** - path to image.
- **w3m** - path to w3mimgdisplay. if a proper w3mimgdisplay path is not given,
blessed will search the entire disk for the binary.
##### Properties:
@ -918,6 +932,7 @@ terminals.
- **clearImage(callback)** - clear the current image.
- **imageSize(img, callback)** - get the size of an image file in pixels.
- **termSize(callback)** - get the size of the terminal in pixels.
- **getPixelRatio(callback)** - get the pixel to cell ratio for the terminal.
### Positioning

View File

@ -14,6 +14,9 @@ var blessed = require('blessed')
var left = blessed.terminal({
parent: screen,
cursor: 'line',
cursorBlink: true,
screenKeys: false,
left: 0,
top: 2,
bottom: 2,
@ -32,6 +35,9 @@ var left = blessed.terminal({
var right = blessed.terminal({
parent: screen,
cursor: 'block',
cursorBlink: true,
screenKeys: false,
right: 2,
top: 2,
bottom: 2,

View File

@ -5364,7 +5364,7 @@ ProgressBar.prototype.render = function() {
yi = yi + ((yl - yi) - (((yl - yi) * (this.filled / 100)) | 0));
}
dattr = this.sattr(this, this.style.bar.fg, this.style.bar.bg);
dattr = this.sattr(this.style.bar, this.style.bar.fg, this.style.bar.bg);
this.screen.fillRegion(dattr, this.ch, xi, xl, yi, yl);
@ -6481,7 +6481,7 @@ function Terminal(options) {
}
options = options || {};
options.scrollable = true;
options.scrollable = false;
Box.call(this, options);
@ -6489,6 +6489,10 @@ function Terminal(options) {
this.shell = options.shell || process.env.SHELL || 'sh';
this.args = options.args || [];
this.cursor = this.options.cursor;
this.cursorBlink = this.options.cursorBlink;
this.screenKeys = this.options.screenKeys;
this.style = this.style || {};
this.style.bg = this.style.bg || 'default';
this.style.fg = this.style.fg || 'default';
@ -6535,14 +6539,15 @@ Terminal.prototype.bootstrap = function() {
element.offsetParent = element;
this.term = require('term.js')({
//termName: this.screen.program.terminal,
termName: 'xterm',
cols: this.width - this.iwidth,
rows: this.height - this.iheight,
context: element,
document: element,
body: element,
parent: element
parent: element,
cursorBlink: this.cursorBlink,
screenKeys: this.screenKeys
});
this.term.refresh = function() {
@ -6623,15 +6628,11 @@ Terminal.prototype.bootstrap = function() {
});
this.on('focus', function() {
if (self.term.sendFocus) {
self.handler('\x1b[I');
}
self.term.focus();
});
this.on('blur', function() {
if (self.term.sendFocus) {
self.handler('\x1b[O');
}
self.term.blur();
});
this.term.on('title', function(title) {
@ -6650,7 +6651,6 @@ Terminal.prototype.bootstrap = function() {
});
this.pty = require('pty.js').fork(this.shell, this.args, {
//name: this.screen.program.terminal,
name: 'xterm',
cols: this.width - this.iwidth,
rows: this.height - this.iheight,
@ -6687,7 +6687,7 @@ Terminal.prototype.render = function() {
var ret = this._render();
if (!ret) return;
this.dattr = this.sattr(this, this.style.fg, this.style.bg);
this.dattr = this.sattr(this.style, this.style.fg, this.style.bg);
var xi = ret.xi + this.ileft
, xl = ret.xl - this.iright
@ -6702,7 +6702,7 @@ Terminal.prototype.render = function() {
if (!line || !this.term.lines[scrollback + y - yi]) break;
if (y === yi + this.term.y
// && this.term.cursorState
&& this.term.cursorState
&& this.screen.focused === this
&& (this.term.ydisp === this.term.ybase || this.term.selectMode)
&& !this.term.cursorHidden) {
@ -6713,21 +6713,42 @@ Terminal.prototype.render = function() {
for (var x = xi; x < xl; x++) {
if (!line[x] || !this.term.lines[scrollback + y - yi][x - xi]) break;
if (x === cursor) {
line[x][0] = 7;
if (this.cursor === 'line') {
line[x][0] = this.dattr;
line[x][1] = '\u2502';
} else if (this.cursor === 'underline') {
line[x][0] = this.dattr;
line[x][1] = '_';
} else if (this.cursor === 'block' || !this.cursor) {
line[x][0] = this.dattr | (8 << 18);
line[x][1] = ' ';
}
continue;
}
line[x][0] = this.term.lines[scrollback + y - yi][x - xi][0];
// default foreground = 257
if (((line[x][0] >> 9) & 0x1ff) === 257 && !((line[x][0] >> 18) & 8)) {
line[x][0] = (line[x][0] << 18) | (((this.dattr >> 9) & 0x1ff) << 9) | (line[x][0] & 0x1ff);
}
// default background = 256
if ((line[x][0] & 0x1ff) === 256 && !((line[x][0] >> 18) & 8)) {
line[x][0] = (line[x][0] << 18) | (((line[x][0] >> 9) & 0x1ff) << 9) | (this.dattr & 0x1ff);
}
line[x][1] = this.term.lines[scrollback + y - yi][x - xi][1];
// default foreground = 257
if (((line[x][0] >> 9) & 0x1ff) === 257) {
if (!((line[x][0] >> 18) & 8)) {
line[x][0] &= ~(0x1ff << 9);
line[x][0] |= ((this.dattr >> 9) & 0x1ff) << 9;
}
}
// default background = 256
if ((line[x][0] & 0x1ff) === 256) {
if (!((line[x][0] >> 18) & 8)) {
line[x][0] &= ~0x1ff;
line[x][0] |= this.dattr & 0x1ff;
// } else {
// line[x][0] &= ~(0x1ff << 9);
// line[x][0] |= ((this.dattr >> 9) & 0x1ff) << 9;
}
}
}
line.dirty = true;
@ -6747,6 +6768,7 @@ Terminal.prototype._isMouse = function(buf) {
}
}
return (buf[0] === 0x1b && buf[1] === 0x5b && buf[2] === 0x4d)
|| /^\x1b\[M([\x00\u0020-\uffff]{3})/.test(s)
|| /^\x1b\[(\d+;\d+;\d+)M/.test(s)
|| /^\x1b\[<(\d+;\d+;\d+)([mM])/.test(s)
|| /^\x1b\[<(\d+;\d+;\d+;\d+)&w/.test(s)
@ -6754,6 +6776,39 @@ Terminal.prototype._isMouse = function(buf) {
|| /^\x1b\[(O|I)/.test(s);
};
Terminal.prototype.setScroll =
Terminal.prototype.scrollTo = function(offset, always) {
this.term.ydisp = offset;
return this.emit('scroll');
};
Terminal.prototype.getScroll = function() {
return this.term.ydisp;
};
Terminal.prototype.scroll = function(offset, always) {
this.term.scrollDisp(offset);
return this.emit('scroll');
};
Terminal.prototype.resetScroll = function() {
this.term.ydisp = 0;
this.term.ybase = 0;
return this.emit('scroll');
};
Terminal.prototype.getScrollHeight = function() {
return this.term.rows - 1;
};
Terminal.prototype.getScrollPerc = function(s) {
return (this.term.ydisp / this.term.ybase) * 100;
};
Terminal.prototype.setScrollPerc = function(i) {
return this.setScroll((i / 100) * this.term.ybase | 0);
};
/**
* Image
* Good example of w3mimgdisplay commands:
@ -6771,11 +6826,43 @@ function Image(options) {
Box.call(this, options);
if (options.w3m) {
Image.w3mdisplay = options.w3m;
}
if (Image.hasW3MDisplay == null) {
if (fs.existsSync(Image.w3mdisplay)) {
Image.hasW3MDisplay = true;
} else if (options.search !== false) {
var file = findFile('/', 'w3mimgdisplay');
if (file) {
Image.hasW3MDisplay = true;
Image.w3mdisplay = file;
} else {
Image.hasW3MDisplay = false;
}
}
}
this.on('hide', function() {
self._lastFile = self.file;
self.clearImage();
});
this.on('show', function() {
if (!self._lastFile) return;
self.setImage(self._lastFile);
});
this.on('detach', function() {
self._lastFile = self.file;
self.clearImage();
});
this.on('attach', function() {
if (!self._lastFile) return;
self.setImage(self._lastFile);
});
if (this.options.file || this.options.img) {
this.setImage(this.options.file || this.options.img);
@ -6822,12 +6909,6 @@ Image.prototype.spawn = function(file, args, opt, callback) {
Image.prototype.setImage = function(img, callback) {
var self = this;
if (Image.hasW3MDisplay == null) {
return this._waitForW3MDisplay(function() {
return self.setImage(img, callback);
});
}
if (Image.hasW3MDisplay === false) {
if (!callback) return;
return callback(new Error('W3M Image Display not available.'));
@ -6840,9 +6921,6 @@ Image.prototype.setImage = function(img, callback) {
this.file = img;
// NOTE: Fall back to screen.displayImage() instead of
// setImage() if w3mimgdisplay is not present?
function renderImage(ratio, callback) {
// clearImage unsets these:
var _file = self.file;
@ -6953,12 +7031,6 @@ Image.prototype.setImage = function(img, callback) {
Image.prototype.clearImage = function(callback) {
var self = this;
if (Image.hasW3MDisplay == null) {
return this._waitForW3MDisplay(function() {
return self.clearImage(callback);
});
}
if (Image.hasW3MDisplay === false) {
if (!callback) return;
return callback(new Error('W3M Image Display not available.'));
@ -7007,12 +7079,6 @@ Image.prototype.imageSize = function(callback) {
var self = this;
var img = this.file;
if (Image.hasW3MDisplay == null) {
return this._waitForW3MDisplay(function() {
return self.imageSize(callback);
});
}
if (Image.hasW3MDisplay === false) {
if (!callback) return;
return callback(new Error('W3M Image Display not available.'));
@ -7063,12 +7129,6 @@ Image.prototype.imageSize = function(callback) {
Image.prototype.termSize = function(callback) {
var self = this;
if (Image.hasW3MDisplay == null) {
return this._waitForW3MDisplay(function() {
return self.termSize(callback);
});
}
if (Image.hasW3MDisplay === false) {
if (!callback) return;
return callback(new Error('W3M Image Display not available.'));
@ -7099,7 +7159,8 @@ Image.prototype.termSize = function(callback) {
if (!callback) return;
if (!buf.trim()) {
// XXX Bug
// Bug: w3mimgdisplay will sometimes
// output nothing. Try again:
return self.termSize(callback);
}
@ -7140,57 +7201,6 @@ Image.prototype.displayImage = function(callback) {
return this.screen.displayImage(this.file, callback);
};
Image.prototype._waitForW3MDisplay = function(callback) {
var self = this;
if (Image.hasW3MDisplay == null) {
if (Image._checkingW3MDisplay) {
setTimeout(function() {
return self._waitForW3MDisplay(callback);
}, 500);
return;
}
Image._checkingW3MDisplay = true;
var ps = this.screen.spawn('find',
['/', '-name', 'w3mimgdisplay'],
{ stdio: ['ignore', 'pipe', 'ignore'] });
var buf = '';
ps.stdout.setEncoding('utf8');
ps.stdout.on('data', function(data) {
buf += data;
});
ps.on('error', function(err) {
Image.hasW3MDisplay = false;
Image._checkingW3MDisplay = false;
if (!callback) return;
return callback(err);
});
ps.on('exit', function(code) {
if (!buf.trim()) {
Image.hasW3MDisplay = false;
Image._checkingW3MDisplay = false;
if (!callback) return;
return callback(new Error('find failed.'));
}
Image._checkingW3MDisplay = false;
buf = buf.trim().split('\n')[0].trim();
Image.w3mdisplay = buf;
Image.hasW3MDisplay = true;
return self._waitForW3MDisplay(callback);
});
return;
}
if (callback) callback(null, Image.w3mdisplay);
};
/**
* Helpers
*/
@ -7263,6 +7273,43 @@ var wideChars = new RegExp('(['
+ '\\uffe8-\\uffee'
+ '])', 'g');
function findFile(start, target) {
return (function read(dir) {
var files, file, stat, out;
if (dir === '/dev' || dir === '/sys' || dir === '/proc') {
return null;
}
try {
files = fs.readdirSync(dir);
} catch (e) {
files = [];
}
for (var i = 0; i < files.length; i++) {
file = files[i];
if (file === target) {
return (dir === '/' ? '' : dir) + '/' + file;
}
try {
stat = fs.statSync((dir === '/' ? '' : dir) + '/' + file);
} catch (e) {
stat = null;
}
if (stat && stat.isDirectory()) {
out = read((dir === '/' ? '' : dir) + '/' + file);
if (out) return out;
}
}
return null;
})(start);
}
/**
* Helpers
*/

View File

@ -7,7 +7,10 @@ screen = blessed.screen({
});
// To ensure our w3mimgdisplay search works:
if (process.argv[2] === 'find') {
blessed.image.w3mdisplay = '/does/not/exist';
process.argv.length = 2;
}
var file = process.argv[2] || __dirname + '/test-image.png';
@ -35,6 +38,14 @@ setTimeout(function() {
image.rtop = 2;
image.rleft = 7;
screen.render();
setTimeout(function() {
image.detach();
screen.render();
setTimeout(function() {
screen.append(image);
screen.render();
}, 1000);
}, 1000);
}, 1000);
}, 5000);
});