Fix resizing, text zoom, alternate screen bug, and infinite loop/hang.
BUG=chromium-os:25197,chromium-os:25984,chromium-os:25985,chromium-os:25986,chromium-os:25785,chromium-os:25787
TEST=test_harness.html, 55/55 tests passed.
Change-Id: I8ba0433e97ac548b3b4e101c395f985990b89c0f
Reviewed-on: https://gerrit.chromium.org/gerrit/15640
Reviewed-by: Zelidrag Hornung <zelidrag@chromium.org>
Commit-Ready: Robert Ginda <rginda@chromium.org>
Tested-by: Robert Ginda <rginda@chromium.org>
diff --git a/hterm/js/terminal.js b/hterm/js/terminal.js
index 558dd07..cb4ecc7 100644
--- a/hterm/js/terminal.js
+++ b/hterm/js/terminal.js
@@ -19,7 +19,7 @@
* displayed twice as wide as standard latin characters. This is to support
* CJK (and possibly other character sets).
*/
-hterm.Terminal = function(fontSize, opt_lineHeight) {
+hterm.Terminal = function() {
// Two screen instances.
this.primaryScreen_ = new hterm.Screen();
this.alternateScreen_ = new hterm.Screen();
@@ -33,11 +33,8 @@
// screen.
this.screenSize = new hterm.Size(0, 0);
- // The pixel dimensions of a single character on the screen.
- this.characterSize_ = new hterm.Size(0, 0);
-
// The scroll port we'll be using to display the visible rows.
- this.scrollPort_ = new hterm.ScrollPort(this, fontSize, opt_lineHeight);
+ this.scrollPort_ = new hterm.ScrollPort(this);
this.scrollPort_.subscribe('resize', this.onResize_.bind(this));
this.scrollPort_.subscribe('scroll', this.onScroll_.bind(this));
this.scrollPort_.subscribe('paste', this.onPaste_.bind(this));
@@ -64,26 +61,6 @@
// The DIV element for the visible cursor.
this.cursorNode_ = null;
- // Default font family for the terminal text.
- this.defaultFontFamily_ = '"DejaVu Sans Mono", "Everson Mono", FreeMono, ' +
- '"Andale Mono", "Lucida Console", monospace'
-
- // The default colors for text with no other color attributes.
- this.backgroundColor = 'black';
- this.foregroundColor = 'white';
-
- // Default tab with of 8 to match xterm.
- this.tabWidth = 8;
-
- // The color of the visible cursor.
- this.cursorColor = 'rgba(255,0,0,0.5)';
-
- // If true, scroll to the bottom on any keystroke.
- this.scrollOnKeystroke = true;
-
- // If true, scroll to the bottom on terminal output.
- this.scrollOnOutput = false;
-
// Cursor position and attributes saved with DECSC.
this.savedOptions_ = {};
@@ -108,6 +85,50 @@
};
/**
+ * Default font family for the terminal text.
+ */
+
+hterm.Terminal.prototype.defaultFontFamily =
+ '"DejaVu Sans Mono", "Everson Mono", FreeMono, ' +
+ '"Andale Mono", "Lucida Console", monospace';
+
+/**
+ * The default colors for text with no other color attributes.
+ */
+hterm.Terminal.prototype.backgroundColor = 'black';
+hterm.Terminal.prototype.foregroundColor = 'white';
+
+/**
+ * Default tab with of 8 to match xterm.
+ */
+hterm.Terminal.prototype.tabWidth = 8;
+
+/**
+ * The color of the visible cursor.
+ */
+hterm.Terminal.prototype.cursorColor = 'rgba(255,0,0,0.5)';
+
+/**
+ * If true, scroll to the bottom on any keystroke.
+ */
+hterm.Terminal.prototype.scrollOnKeystroke = true;
+
+/**
+ * If true, scroll to the bottom on terminal output.
+ */
+hterm.Terminal.prototype.scrollOnOutput = false;
+
+/**
+ * The default font size in pixels.
+ */
+hterm.Terminal.prototype.defaultFontSizePx = 15;
+
+/**
+ * The assumed width of a scrollbar.
+ */
+hterm.Terminal.prototype.scrollbarWidthPx = 16;
+
+/**
* Create a new instance of a terminal command and run it with a given
* argument string.
*
@@ -156,6 +177,27 @@
}
/**
+ * Set the font size for this terminal.
+ */
+hterm.Terminal.prototype.setFontSize = function(px) {
+ this.scrollPort_.setFontSize(px);
+};
+
+/**
+ * Get the current font size.
+ */
+hterm.Terminal.prototype.getFontSize = function() {
+ return this.scrollPort_.getFontSize();
+};
+
+/**
+ * Set the CSS "font-family" for this terminal.
+ */
+hterm.Terminal.prototype.setFontFamily = function(str) {
+ this.scrollPort_.setFontFamily(str);
+};
+
+/**
* Return a copy of the current cursor position.
*
* @return {hterm.RowCol} The RowCol object representing the current position.
@@ -181,20 +223,36 @@
* @param {hterm.RowCol} cursor The position to restore.
*/
hterm.Terminal.prototype.restoreCursor = function(cursor) {
- this.screen_.setCursorPosition(cursor.row, cursor.column);
- this.screen_.cursorPosition.overflow = cursor.overflow;
+ var row = hterm.clamp(cursor.row, 0, this.screenSize.height - 1);
+ var column = hterm.clamp(cursor.column, 0, this.screenSize.width - 1);
+ this.screen_.setCursorPosition(row, column);
+ if (cursor.column > column ||
+ cursor.column == column && cursor.overflow) {
+ this.screen_.cursorPosition.overflow = true;
+ }
};
/**
* Set the width of the terminal, resizing the UI to match.
*/
hterm.Terminal.prototype.setWidth = function(columnCount) {
- this.div_.style.width = this.characterSize_.width * columnCount + 16 + 'px';
+ this.div_.style.width = this.scrollPort_.characterSize.width *
+ columnCount + this.scrollbarWidthPx + 'px';
this.realizeSize_(columnCount, this.screenSize.height);
this.scheduleSyncCursorPosition_();
};
/**
+ * Set the height of the terminal, resizing the UI to match.
+ */
+hterm.Terminal.prototype.setHeight = function(rowCount) {
+ this.div_.style.height =
+ this.scrollPort_.characterSize.height * rowCount + 'px';
+ this.realizeSize_(this.screenSize.width, rowCount);
+ this.scheduleSyncCursorPosition_();
+};
+
+/**
* Deal with terminal size changes.
*
*/
@@ -278,8 +336,7 @@
// We just removed rows from the top of the screen, we need to update
// the cursor to match.
- cursor.row -= deltaRows;
-
+ cursor.row = Math.max(cursor.row - deltaRows, 0);
} else if (deltaRows > 0) {
// Screen got larger.
@@ -296,6 +353,7 @@
this.appendRows_(deltaRows);
}
+ this.setVTScrollRegion(null, null);
this.restoreCursor(cursor);
};
@@ -524,21 +582,18 @@
this.div_ = div;
this.scrollPort_.decorate(div);
- this.scrollPort_.setFontFamily(this.defaultFontFamily_);
+ this.scrollPort_.setFontFamily(this.defaultFontFamily);
+ this.scrollPort_.setFontSize(this.defaultFontSize);
this.document_ = this.scrollPort_.getDocument();
- // Get character dimensions from the scrollPort.
- this.characterSize_.height = this.scrollPort_.getRowHeight();
- this.characterSize_.width = this.scrollPort_.getCharacterWidth();
-
this.cursorNode_ = this.document_.createElement('div');
this.cursorNode_.style.cssText =
('position: absolute;' +
'top: -99px;' +
'display: block;' +
- 'width: ' + this.characterSize_.width + 'px;' +
- 'height: ' + this.characterSize_.height + 'px;' +
+ 'width: ' + this.scrollPort_.characterSize.width + 'px;' +
+ 'height: ' + this.scrollPort_.characterSize.height + 'px;' +
'-webkit-transition: opacity, background-color 100ms linear;' +
'background-color: ' + this.cursorColor);
this.document_.body.appendChild(this.cursorNode_);
@@ -752,14 +807,13 @@
var lastColumn;
do {
- if (this.screen_.cursorPosition.overflow)
- this.newLine();
+ this.newLine();
+ lastColumn = overflow.characterLength;
if (!this.options_.insertMode)
this.screen_.deleteChars(overflow.characterLength);
this.screen_.prependNodes(overflow);
- lastColumn = overflow.characterCount;
overflow = this.screen_.maybeClipCurrentRow();
} while (overflow);
@@ -786,7 +840,6 @@
hterm.Terminal.prototype.setVTScrollRegion = function(scrollTop, scrollBottom) {
this.vtScrollTop_ = scrollTop;
this.vtScrollBottom_ = scrollBottom;
- this.setAbsoluteCursorPosition(0, 0);
};
/**
@@ -1426,16 +1479,24 @@
var cursor = this.saveCursor();
this.screen_ = state ? this.alternateScreen_ : this.primaryScreen_;
- this.screen_.setColumnCount(this.screenSize.width);
+ if (this.screen_.rowsArray.length &&
+ this.screen_.rowsArray[0].rowIndex != this.scrollbackRows_.length) {
+ // If the screen changed sizes while we were away, our rowIndexes may
+ // be incorrect.
+ var offset = this.scrollbackRows_.length;
+ var ary = this.screen_.rowsArray;
+ for (i = 0; i < ary.length; i++) {
+ ary[i].rowIndex = offset + i;
+ }
+ }
- var rowDelta = this.screenSize.height - this.screen_.getHeight();
- if (rowDelta > 0)
- this.appendRows_(rowDelta);
+ this.realizeWidth_(this.screenSize.width);
+ this.realizeHeight_(this.screenSize.height);
+ this.scrollPort_.syncScrollHeight();
+ this.scrollPort_.invalidate();
this.restoreCursor(cursor);
-
- this.scrollPort_.invalidate();
- this.syncCursorPosition_();
+ this.scrollPort_.resize();
};
/**
@@ -1510,14 +1571,18 @@
if (cursorRowIndex > bottomRowIndex) {
// Cursor is scrolled off screen, move it outside of the visible area.
- this.cursorNode_.style.top = -this.characterSize_.height;
+ this.cursorNode_.style.top = -this.scrollPort_.characterSize.height + 'px';
return;
}
+ this.cursorNode_.style.width = this.scrollPort_.characterSize.width + 'px';
+ this.cursorNode_.style.height = this.scrollPort_.characterSize.height + 'px';
+
this.cursorNode_.style.top = this.scrollPort_.visibleRowTopMargin +
- this.characterSize_.height * (cursorRowIndex - topRowIndex);
- this.cursorNode_.style.left = this.characterSize_.width *
- this.screen_.cursorPosition.column;
+ this.scrollPort_.characterSize.height * (cursorRowIndex - topRowIndex) +
+ 'px';
+ this.cursorNode_.style.left = this.scrollPort_.characterSize.width *
+ this.screen_.cursorPosition.column + 'px';
this.cursorNode_.setAttribute('title',
'(' + this.screen_.cursorPosition.row +
@@ -1583,8 +1648,16 @@
*/
hterm.Terminal.prototype.onResize_ = function() {
var columnCount = Math.floor(this.scrollPort_.getScreenWidth() /
- this.characterSize_.width);
- var rowCount = this.scrollPort_.visibleRowCount;
+ this.scrollPort_.characterSize.width);
+ var rowCount = Math.floor(this.scrollPort_.getScreenHeight() /
+ this.scrollPort_.characterSize.height);
+
+ if (!(columnCount || rowCount)) {
+ // We avoid these situations since they happen sometimes when the terminal
+ // gets removed from the document, and we can't deal with that.
+ return;
+ }
+
this.realizeSize_(columnCount, rowCount);
this.scheduleSyncCursorPosition_();
};