Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(669)

Side by Side Diff: chrome/browser/resources/hterm/js/terminal.js

Issue 8680034: Initial landing of Screen, Terminal, and VT100 classes. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 9 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. Use
2 // of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 /**
6 * Constructor for the Terminal class.
7 *
8 * A Terminal pulls together the hterm.ScrollPort, hterm.Screen and hterm.VT100
9 * classes to provide the complete terminal functionality.
10 *
11 * There are a number of lower-level Terminal that can be directly called
klimek 2011/11/24 19:13:51 Sentence doesn't parse.
rginda 2011/11/28 20:39:47 It does if you read it fast enough :) Fixed.
12 * directly to manipulate the cursor, text, scroll region, and other terminal
13 * attributes. However, the primary method is interpret(), which parses VT
14 * escape sequences and invokes the appropriate Terminal methods.
15 *
16 * This class was heavily influenced by Cory Maccarrone's Framebuffer class.
17 *
18 * TODO(rginda): Eventually we're going to need to support characters which are
19 * displayed twice as wide as standard latin characters. This is to support
20 * CJK (and possibly other character sets).
21 */
22 hterm.Terminal = function() {
23 // Two screen instances.
24 this.primaryScreen_ = new hterm.Screen();
25 this.alternateScreen_ = new hterm.Screen();
26
27 // The "current" screen.
28 this.screen_ = this.primaryScreen_;
29
30 // The VT escape sequence interpreter.
31 this.vt100_ = new hterm.VT100(this);
32
33 // The local notion of the screen size. ScreenBuffers also have a size which
34 // indicates their present size. During size changes, the two may disagree.
35 // Also, the inactive screen's size is not altered until it is made the active
36 // screen.
37 this.screenSize = new hterm.Size(0, 0);
38
39 // The pixel dimensions of a single character on the screen.
40 this.characterSize_ = new hterm.Size(0, 0);
41
42 // The scroll port we'll be using to display the visible rows.
43 this.scrollPort_ = new hterm.ScrollPort(this, 15);
44 this.scrollPort_.subscribe('resize', this.onResize_.bind(this));
45 this.scrollPort_.subscribe('scroll', this.onScroll_.bind(this));
46
47 // The rows that have scrolled off screen and are no longer addressable.
48 this.scrollbackRows_ = [];
49
50 // The VT's notion of the top and bottom rows. Used during some VT
51 // cursor positioning and scrolling commands.
52 this.vtScrollTop_ = null;
53 this.vtScrollBottom_ = null;
54
55 // The timeout handle for the blinking cursor, or null if the cursor is not
56 // blinking.
57 this.cursorTimer_ = null;
58
59 // The DIV element for the visible cursor.
60 this.cursorNode_ = null;
61
62 // The default colors for text with no other color attributes.
63 this.backgroundColor = 'black';
64 this.foregroundColor = 'white';
65
66 // The color of the cursor.
67 this.cursorColor = 'rgba(255,0,0,0.5)';
68
69 // The current mode bits for the terminal.
70 this.options_ = new hterm.Options();
71 };
72
73 /**
74 * Methods called by Cory's vt100 interpreter which we haven't implemented yet.
75 */
76 hterm.Terminal.prototype.reset =
77 hterm.Terminal.prototype.clearColorAndAttributes =
78 hterm.Terminal.prototype.setForegroundColor256 =
79 hterm.Terminal.prototype.setBackgroundColor256 =
80 hterm.Terminal.prototype.setForegroundColor =
81 hterm.Terminal.prototype.setBackgroundColor =
82 hterm.Terminal.prototype.setAttributes =
83 hterm.Terminal.prototype.resize =
84 hterm.Terminal.prototype.setSpecialCharactersEnabled =
85 hterm.Terminal.prototype.setTabStopAtCursor =
86 hterm.Terminal.prototype.clearTabStops =
87 hterm.Terminal.prototype.saveCursor =
88 hterm.Terminal.prototype.restoreCursor =
89 hterm.Terminal.prototype.reverseLineFeed = function() {
90 throw 'NOT IMPLEMENTED';
91 };
92
93 /**
94 * Interpret a sequence of characters.
95 *
96 * Incomplete escape sequences are buffered until the next call.
97 *
98 * @param {string} str Sequence of characters to interpret or pass through.
99 */
100 hterm.Terminal.prototype.interpret = function(str) {
101 this.vt100_.interpretString(str);
102 this.scheduleSyncCursorPosition_();
103 };
104
105 /**
106 * Take over the given DIV for use as the terminal display.
107 *
108 * @param {HTMLDivElement} div The div to use as the terminal display.
109 */
110 hterm.Terminal.prototype.decorate = function(div) {
111 this.scrollPort_.decorate(div);
112 this.document_ = this.scrollPort_.getDocument();
113
114 // Get character dimensions from the scrollPort.
115 this.characterSize_.height = this.scrollPort_.getRowHeight();
116 this.characterSize_.width = this.scrollPort_.getCharacterWidth();
117
118 this.cursorNode_ = this.document_.createElement('div');
119 this.cursorNode_.style.cssText =
120 ('position: absolute;' +
121 'display: none;' +
122 'width: ' + this.characterSize_.width + 'px;' +
123 'height: ' + this.characterSize_.height + 'px;' +
124 'background-color: ' + this.cursorColor);
125 this.document_.body.appendChild(this.cursorNode_);
126
127 this.setReverseVideo(false);
128 };
129
130 /**
131 * Return the HTML Element for a given row index.
132 *
133 * This is a method from the RowProvider interface. The ScrollPort uses
134 * it to fetch rows on demand as they are scrolled into view.
135 *
136 * TODO(rginda): Consider saving scrollback rows as (HTML source, text content)
137 * pairs to conserve memory.
138 *
139 * @param {integer} index The zero-based row index, measured relative to the
140 * start of the scrollback buffer. On-screen rows will always have the
141 * largest indicies.
142 * @return {HTMLElement} The 'x-row' element containing for the requested row.
143 */
144 hterm.Terminal.prototype.getRowNode = function(index) {
145 if (index < this.scrollbackRows_.length) {
146 this.scrollbackRows_[index].rowIndex = index;
klimek 2011/11/24 19:13:51 This is unexpected (and seems a breach in the arch
rginda 2011/11/28 20:39:47 This is the visual representation of a row. It's
147 return this.scrollbackRows_[index];
148 }
149
150 var screenIndex = index - this.scrollbackRows_.length;
151 this.screen_.rowsArray[screenIndex].rowIndex = index;
152 return this.screen_.rowsArray[screenIndex];
153 };
154
155 /**
156 * Return the text content for a given range of rows.
157 *
158 * This is a method from the RowProvider interface. The ScrollPort uses
159 * it to fetch text content on demand when the user attempts to copy their
160 * selection to the clipboard.
161 *
162 * @param {integer} start The zero-based row index to start from, measured
163 * relative to the start of the scrollback buffer. On-screen rows will
164 * always have the largest indicies.
165 * @param {integer} end The zero-based row index to end on, measured
166 * relative to the start of the scrollback buffer.
167 * @return {string} A single string containing the text value of the range of
168 * rows. Lines will be newline delimited, with no trailing newline.
169 */
170 hterm.Terminal.prototype.getRowsText = function(start, end) {
171 var ary = [];
172 for (var i = start; i < end; i++) {
173 var node = this.getRowNode(i);
174 ary.push(node.textContent);
175 }
176
177 return ary.join('\n');
178 };
179
180 /**
181 * Return the text content for a given row.
182 *
183 * This is a method from the RowProvider interface. The ScrollPort uses
184 * it to fetch text content on demand when the user attempts to copy their
185 * selection to the clipboard.
186 *
187 * @param {integer} index The zero-based row index to return, measured
188 * relative to the start of the scrollback buffer. On-screen rows will
189 * always have the largest indicies.
190 * @return {string} A string containing the text value of the selected row.
191 */
192 hterm.Terminal.prototype.getRowText = function(index) {
193 var node = this.getRowNode(index);
194 return row.textContent;
195 };
196
197 /**
198 * Return the total number of rows in the addressable screen and in the
199 * scrollback buffer of this terminal.
200 *
201 * This is a method from the RowProvider interface. The ScrollPort uses
202 * it to compute the size of the scrollbar.
203 *
204 * @return {integer} The number of rows in this terminal.
205 */
206 hterm.Terminal.prototype.getRowCount = function() {
207 return this.scrollbackRows_.length + this.screen_.rowsArray.length;
208 };
209
210 /**
211 * Create DOM nodes for new rows and append them to the end of the terminal.
212 *
213 * This is the only correct way to add a new DOM node for a row. Notice that
214 * the new row is appended to the bottom of the list of rows, and does not
215 * require renumbering (of the rowIndex property) of previous rows.
216 *
217 * If you think you want a new blank row somewhere in the middle of the
218 * terminal, look into moveRows_().
219 *
220 * This method does not pay attention to vtScrollTop/Bottom, since you should
221 * be using moveRows() in cases where they would matter.
222 *
223 * The cursor will be positioned at column 0 of the first inserted line.
224 */
225 hterm.Terminal.prototype.appendRows_ = function(count) {
226 var cursorRow = this.screen_.rowsArray.length;
227 var offset = this.scrollbackRows_.length + cursorRow;
228 for (var i = 0; i < count; i++) {
229 var row = this.document_.createElement('x-row');
230 row.appendChild(this.document_.createTextNode(''));
231 row.rowIndex = offset + i;
232 this.screen_.pushRow(row);
233 }
234
235 var extraRows = this.screen_.rowsArray.length - this.screenSize.height;
236 if (extraRows > 0) {
237 var ary = this.screen_.shiftRows(extraRows);
238 Array.prototype.push.apply(this.scrollbackRows_, ary);
239 //console.log('ssd');
Vladislav Kaznacheev 2011/11/28 18:39:36 debugging code
rginda 2011/11/28 20:39:47 Done.
240 this.scheduleScrollDown_();
241 }
242
243 if (cursorRow >= this.screen_.rowsArray.length)
244 cursorRow = this.screen_.rowsArray.length - 1;
245
246 this.screen_.setCursorPosition(cursorRow, 0);
247 };
248
249 /**
250 * Relocate rows from one part of the addressable screen to another.
251 *
252 * This is used to recycle rows during VT scrolls (those which are driven
253 * by VT commands, rather than by the user manipulating the scrollbar.)
254 *
255 * In this case, the blank lines scrolled into the scroll region are made of
256 * the nodes we scrolled off. These have their rowIndex properties carefully
257 * renumbered so as not to confuse the ScrollPort.
258 *
259 * TODO(rginda): I'm not sure why this doesn't require a scrollport repaint.
260 * It may just be luck. I wouldn't be surprised if we actually needed to call
261 * scrollPort_.invalidateRowRange, but I'm going to wait for evidence before
262 * adding it.
263 */
264 hterm.Terminal.prototype.moveRows_ = function(fromIndex, count, toIndex) {
265 var ary = this.screen_.removeRows(fromIndex, count);
266 this.screen_.insertRows(toIndex, ary);
267
268 var start, end;
269 if (fromIndex < toIndex) {
270 start = fromIndex;
271 end = fromIndex + count;
272 } else {
273 start = toIndex;
274 end = toIndex + count;
275 }
276
277 this.renumberRows_(start, end);
278 };
279
280 /**
281 * Renumber the rowIndex property of the given range of rows.
282 *
283 * The start and end indicies are relative to the screen, not the scrollback.
284 * Rows in the scrollback buffer cannot be renumbered. Since they are not
285 * addressable (you cant delete them, scroll them, etc), you should have
286 * no need to renumber scrollback rows.
287 */
288 hterm.Terminal.prototype.renumberRows_ = function(start, end) {
289 var offset = this.scrollbackRows_.length;
290 for (var i = start; i < end; i++) {
291 this.screen_.rowsArray[i].rowIndex = offset + i;
292 }
293 };
294
295 /**
296 * Print a string to the terminal.
297 *
298 * This respects the current insert and wraparound modes. It will add new lines
299 * to the end of the terminal, scrolling off the top into the scrollback buffer
300 * if necessary.
301 *
302 * The string is *not* parsed for escape codes. Use the interpret() method if
303 * that's what you're after.
304 *
305 * @param{string} str The string to print.
306 */
307 hterm.Terminal.prototype.print = function(str) {
308 do {
309 if (this.options_.insertMode) {
310 str = this.screen_.insertString(str);
311 } else {
312 str = this.screen_.overwriteString(str);
313 }
314
315 if (this.options_.wraparound && str) {
316 this.newLine();
317 } else {
318 break;
319 }
320 } while (str);
321
322 this.scheduleSyncCursorPosition_();
323 };
324
325 /**
326 * Return the top row index according to the VT.
327 *
328 * This will return 0 unless the terminal has been told to restrict scrolling
329 * to some lower row. It is used for some VT cursor positioning and scrolling
330 * commands.
331 *
332 * @return {integer} The topmost row in the terminal's scroll region.
333 */
334 hterm.Terminal.prototype.getVTScrollTop = function() {
335 if (this.vtScrollTop_ != null)
336 return this.vtScrollTop_;
337
338 return 0;
339 }
340
341 /**
342 * Return the bottom row index according to the VT.
343 *
344 * This will return the height of the terminal unless the it has been told to
345 * restrict scrolling to some higher row. It is used for some VT cursor
346 * positioning and scrolling commands.
347 *
348 * @return {integer} The bottommost row in the terminal's scroll region.
349 */
350 hterm.Terminal.prototype.getVTScrollBottom = function() {
351 if (this.vtScrollBottom_ != null)
352 return this.vtScrollBottom_;
353
354 return this.screenSize.height;
355 }
356
357 /**
358 * Process a '\n' character.
359 *
360 * If the cursor is on the final row of the terminal this will append a new
361 * blank row to the screen and scroll the topmost row into the scrollback
362 * buffer.
363 *
364 * Otherwise, this moves the cursor to column zero of the next row.
365 */
366 hterm.Terminal.prototype.newLine = function() {
367 if (this.screen_.cursorPosition.row == this.screen_.rowsArray.length - 1) {
368 this.appendRows_(1);
369 } else {
370 this.screen_.setCursorPosition(this.screen_.cursorPosition.row + 1, 0);
371 }
372 };
373
374 /**
375 * Like newLine(), except maintain the cursor column.
376 */
377 hterm.Terminal.prototype.lineFeed = function() {
378 var column = this.screen_.cursorPosition.column;
379 this.newLine();
380 this.setCursorColumn(column);
381 };
382
383 /**
384 * Replace all characters to the left of the current cursor with the space
385 * character.
386 *
387 * TODO(rginda): This should probably *remove* the characters (not just replace
388 * with a space) if there are no characters at or beyond the current cursor
389 * position. Once it does that, it'll have the same text-attribute related
390 * issues as hterm.Screen.prototype.clearCursorRow :/
391 */
392 hterm.Terminal.prototype.eraseToLeft = function() {
393 var currentColumn = this.screen_.cursorPosition.column;
394 this.setCursorColumn(0);
395 this.screen_.overwriteString(hterm.getWhitespace(currentColumn + 1));
396 this.setCursorColumn(currentColumn);
397 };
398
399 /**
400 * Erase a given number of characters to the right of the cursor, shifting
401 * remaining characters to the left.
402 *
403 * The cursor position is unchanged.
404 *
405 * TODO(rginda): Test that this works even when the cursor is positioned beyond
406 * the end of the text.
407 *
408 * TODO(rginda): This likely has text-attribute related troubles similar to the
409 * todo on hterm.Screen.prototype.clearCursorRow.
410 */
411 hterm.Terminal.prototype.eraseToRight = function(opt_count) {
412 var currentColumn = this.screen_.cursorPosition.column;
413
414 var maxCount = this.screenSize.width - currentColumn;
415 var count = (opt_count && opt_count < maxCount) ? opt_count : maxCount;
416 this.screen_.deleteChars(count);
417 this.setCursorColumn(currentColumn);
418 };
419
420 /**
421 * Erase the current line.
422 *
423 * The cursor position is unchanged.
424 *
425 * TODO(rginda): This relies on hterm.Screen.prototype.clearCursorRow, which
426 * has a text-attribute related TODO.
427 */
428 hterm.Terminal.prototype.eraseLine = function() {
429 var currentColumn = this.screen_.cursorPosition.column;
430 this.screen_.clearCursorRow();
431 this.setCursorColumn(currentColumn);
432 };
433
434 /**
435 * Erase all characters from the start of the scroll region to the current
436 * cursor position.
437 *
438 * The cursor position is unchanged.
439 *
440 * TODO(rginda): This relies on hterm.Screen.prototype.clearCursorRow, which
441 * has a text-attribute related TODO.
442 */
443 hterm.Terminal.prototype.eraseAbove = function() {
444 var currentRow = this.screen_.cursorPosition.row;
445 var currentColumn = this.screen_.cursorPosition.column;
446
447 var top = this.getVTScrollTop();
448 for (var i = top; i < currentRow; i++) {
449 this.screen_.setCursorPosition(i, 0);
450 this.screen_.clearCursorRow();
451 }
452
453 this.screen_.setCursorPosition(currentRow, currentColumn);
454 };
455
456 /**
457 * Erase all characters from the current cursor position to the end of the
458 * scroll region.
459 *
460 * The cursor position is unchanged.
461 *
462 * TODO(rginda): This relies on hterm.Screen.prototype.clearCursorRow, which
463 * has a text-attribute related TODO.
464 */
465 hterm.Terminal.prototype.eraseBelow = function() {
466 var currentRow = this.screen_.cursorPosition.row;
467 var currentColumn = this.screen_.cursorPosition.column;
468
469 var bottom = this.getVTScrollBottom();
470 for (var i = currentRow + 1; i < bottom; i++) {
471 this.screen_.setCursorPosition(i, 0);
472 this.screen_.clearCursorRow();
473 }
474
475 this.screen_.setCursorPosition(currentRow, currentColumn);
476 };
477
478 /**
479 * Erase the entire scroll region.
480 *
481 * The cursor position is unchanged.
482 *
483 * TODO(rginda): This relies on hterm.Screen.prototype.clearCursorRow, which
484 * has a text-attribute related TODO.
485 */
486 hterm.Terminal.prototype.clear = function() {
487 var currentRow = this.screen_.cursorPosition.row;
488 var currentColumn = this.screen_.cursorPosition.column;
489
490 var top = this.getVTScrollTop();
491 var bottom = this.getVTScrollBottom();
492
493 for (var i = top; i < bottom; i++) {
494 this.screen_.setCursorPosition(i, 0);
495 this.screen_.clearCursorRow();
496 }
497
498 this.screen_.setCursorPosition(currentRow, currentColumn);
499 };
500
501 /**
502 * VT command to insert lines at the current cursor row.
503 *
504 * This respects the current scroll region. Rows pushed off the bottom are
505 * lost (they won't show up in the scrollback buffer).
506 *
507 * TODO(rginda): This relies on hterm.Screen.prototype.clearCursorRow, which
508 * has a text-attribute related TODO.
509 *
510 * @param {integer} count The number of lines to insert.
511 */
512 hterm.Terminal.prototype.insertLines = function(count) {
513 var currentRow = this.screen_.cursorPosition.row;
514
515 var bottom = this.getVTScrollBottom();
516 count = Math.min(count, bottom - currentRow);
517
518 var start = bottom - count;
519 if (start != currentRow)
520 this.moveRows_(start, count, currentRow);
521
522 for (var i = 0; i < count; i++) {
523 this.screen_.setCursorPosition(currentRow + i, 0);
524 this.screen_.clearCursorRow();
525 }
526
527 this.screen_.setCursorPosition(currentRow, 0);
528 };
529
530 /**
531 * VT command to delete lines at the current cursor row.
532 *
533 * New rows are added to the bottom of scroll region to take their place. New
534 * rows are strictly there to take up space and have no content or style.
535 */
536 hterm.Terminal.prototype.deleteLines = function(count) {
537 var currentRow = this.screen_.cursorPosition.row;
538 var currentColumn = this.screen_.cursorPosition.column;
539
540 var top = currentRow;
541 var bottom = this.getVTScrollBottom();
542
543 var maxCount = bottom - top;
544 count = Math.min(count, maxCount);
545
546 var moveStart = bottom - count;
547 if (count != maxCount)
548 this.moveRows_(top, count, moveStart);
549
550 for (var i = 0; i < count; i++) {
551 this.screen_.setCursorPosition(moveStart + i, 0);
552 this.screen_.clearCursorRow();
553 }
554
555 this.screen_.setCursorPosition(currentRow, currentColumn);
556 };
557
558 /**
559 * Inserts the given number of spaces at the current cursor position.
560 *
561 * The cursor is left at the end of the inserted spaces.
562 */
563 hterm.Terminal.prototype.insertSpace = function(count) {
564 var ws = hterm.getWhitespace(count);
565 this.screen_.insertString(ws);
566 };
567
568 /**
569 * Forward-delete the specified number of characters starting at the cursor
570 * position.
571 *
572 * @param {integer} count The number of characters to delete.
573 */
574 hterm.Terminal.prototype.deleteChars = function(count) {
575 this.screen_.deleteChars(count);
576 };
577
578 /**
579 * Shift rows above the cursor upwards by a given number of lines.
580 *
581 * This function respects the current scroll region.
582 *
583 * New rows are inserted at the bottom of the scroll region to fill the
584 * vacated rows. The new rows not filled out with the current text attributes.
585 *
586 * This function does not affect the scrollback rows at all. Rows shifted
587 * off the top are lost.
588 *
589 * @param {integer} count The number of rows to scroll.
590 */
591 hterm.Terminal.prototype.vtScrollUp = function(count) {
592 var currentRow = this.screen_.cursorPosition.row;
593 var currentColumn = this.screen_.cursorPosition.column;
594
595 this.setCursorRow(this.getVTScrollTop());
596 this.deleteLines(count);
597
598 this.screen_.setCursorPosition(currentRow, currentColumn);
599 };
600
601 /**
602 * Shift rows below the cursor down by a given number of lines.
603 *
604 * This function respects the current scroll region.
605 *
606 * New rows are inserted at the top of the scroll region to fill the
607 * vacated rows. The new rows not filled out with the current text attributes.
608 *
609 * This function does not affect the scrollback rows at all. Rows shifted
610 * off the bottom are lost.
611 *
612 * @param {integer} count The number of rows to scroll.
613 */
614 hterm.Terminal.prototype.vtScrollDown = function(opt_count) {
615 var currentRow = this.screen_.cursorPosition.row;
616 var currentColumn = this.screen_.cursorPosition.column;
617
618 this.setCursorRow(this.getVTScrollTop());
619 this.insertLines(opt_count);
620
621 this.screen_.setCursorPosition(currentRow, currentColumn);
622 };
623
624 /**
625 * Set the cursor position.
626 *
627 * The cursor row is relative to the scroll region if the terminal has
628 * 'origin mode' enabled, or relative to the addressable screen otherwise.
629 *
630 * @param {integer} row The new zero-based cursor row.
631 * @param {integer} row The new zero-based cursor column.
632 */
633 hterm.Terminal.prototype.setCursorPosition = function(row, column) {
634 if (this.options_.originMode) {
635 var scrollTop = this.getScrollTop();
636 row = hterm.clamp(row + scrollTop, scrollTop, this.getScrollBottom());
637 } else {
638 row = hterm.clamp(row, 0, this.screenSize.height);
639 }
640
641 this.screen_.setCursorPosition(row, column);
642 };
643
644 /**
645 * Set the cursor column.
646 *
647 * @param {integer} column The new zero-based cursor column.
648 */
649 hterm.Terminal.prototype.setCursorColumn = function(column) {
650 this.screen_.setCursorPosition(this.screen_.cursorPosition.row, column);
651 };
652
653 /**
654 * Return the cursor column.
655 *
656 * @return {integer} The zero-based cursor column.
657 */
658 hterm.Terminal.prototype.getCursorColumn = function() {
659 return this.screen_.cursorPosition.column;
660 };
661
662 /**
663 * Set the cursor row.
664 *
665 * The cursor row is relative to the scroll region if the terminal has
666 * 'origin mode' enabled, or relative to the addressable screen otherwise.
667 *
668 * @param {integer} row The new cursor row.
669 */
670 hterm.Terminal.prototype.setCursorRow = function(row) {
671 this.setCursorPosition(row, this.screen_.cursorPosition.column);
672 };
673
674 /**
675 * Return the cursor row.
676 *
677 * @return {integer} The zero-based cursor row.
678 */
679 hterm.Terminal.prototype.getCursorRow = function(row) {
680 return this.screen_.cursorPosition.row;
681 };
682
683 /**
684 * Request that the ScrollPort redraw itself soon.
685 *
686 * The redraw will happen asynchronously, soon after the call stack winds down.
687 * Multiple calls will be coalesced into a single redraw.
688 */
689 hterm.Terminal.prototype.scheduleRedraw_ = function() {
690 if (this.redrawTimeout_)
691 clearTimeout(this.redrawTimeout_);
692
693 var self = this;
694 setTimeout(function() {
695 this.redrawTimeout_ = null;
Vladislav Kaznacheev 2011/11/28 18:39:36 this -> self?
rginda 2011/11/28 20:39:47 Done.
696 self.scrollPort_.redraw_();
697 }, 0);
698 };
699
700 /**
701 * Request that the ScrollPort be scrolled to the bottom.
702 *
703 * The scroll will happen asynchronously, soon after the call stack winds down.
704 * Multiple calls will be coalesced into a single scroll.
705 *
706 * This affects the scrollbar position of the ScrollPort, and has nothing to
707 * do with the VT scroll commands.
708 */
709 hterm.Terminal.prototype.scheduleScrollDown_ = function() {
710 var f = this.scheduleScrollDown_;
711
712 if (f.timeout)
713 clearTimeout(f.timeout);
714
715 var self = this;
716 f.timeout = setTimeout(function() {
717 f.timeout = null;
718 self.scrollPort_.scrollRowToBottom(self.getRowCount());
719 }, 10);
720 };
721
722 /**
723 * Move the cursor up a specified number of rows.
724 *
725 * @param {integer} count The number of rows to move the cursor.
726 */
727 hterm.Terminal.prototype.cursorUp = function(count) {
728 var minHeight = (this.options_.originMode ? this.getVTScrollTop() : 0);
729 var maxHeight = (this.options_.originMode ? this.getVTScrollBottom() :
730 this.screenSize.height - 1);
731
732 var row = hterm.clamp(this.screen_.cursorPosition.row - count,
733 minHeight, maxHeight);
734 this.setCursorRow(row);
735 };
736
737 /**
738 * Move the cursor down a specified number of rows.
739 *
740 * @param {integer} count The number of rows to move the cursor.
741 */
742 hterm.Terminal.prototype.cursorDown = function(count) {
743 var minHeight = (this.options_.originMode ? this.getVTScrollTop() : 0);
Vladislav Kaznacheev 2011/11/28 18:39:36 I am a bit concerned that cursorDown and cursorUp
rginda 2011/11/28 20:39:47 Done.
744 var maxHeight = (this.options_.originMode ? this.getVTScrollBottom() :
745 this.screenSize.height - 1);
746
747 var row = hterm.clamp(this.screen_.cursorPosition.row + count,
748 minHeight, maxHeight);
749 this.setCursorRow(row);
750 };
751
752 /**
753 * Move the cursor left a specified number of columns.
754 *
755 * @param {integer} count The number of columns to move the cursor.
756 */
757 hterm.Terminal.prototype.cursorLeft = function(count) {
758 var column = hterm.clamp(this.screen_.cursorPosition.column - count,
759 0, this.screenSize.width);
760 this.setCursorColumn(column);
761 };
762
763 /**
764 * Move the cursor right a specified number of columns.
765 *
766 * @param {integer} count The number of columns to move the cursor.
767 */
768 hterm.Terminal.prototype.cursorRight = function(count) {
769 var column = hterm.clamp(this.screen_.cursorPosition.column + count,
770 0, this.screenSize.width);
771 this.setCursorColumn(column);
772 };
773
774 /**
775 * Reverse the foreground and background colors of the terminal.
776 *
777 * This only affects text that was drawn with no attributes.
778 *
779 * TODO(rginda): Test xterm to see if reverse is respected for text that has
780 * been drawn with attributes that happen to coincide with the default
781 * 'no-attribute' colors. My guess is probably not.
782 */
783 hterm.Terminal.prototype.setReverseVideo = function(state) {
784 if (state) {
785 this.scrollPort_.setForegroundColor(this.backgroundColor);
786 this.scrollPort_.setBackgroundColor(this.foregroundColor);
787 } else {
788 this.scrollPort_.setForegroundColor(this.foregroundColor);
789 this.scrollPort_.setBackgroundColor(this.backgroundColor);
790 }
791 };
792
793 /**
794 * Set the origin mode bit.
795 *
796 * If origin mode is on, certain VT cursor and scrolling commands measure their
797 * row parameter relative to the VT scroll region. Otherwise, row 0 corresponds
798 * to the top of the addressable screen.
799 *
800 * Defaults to off.
801 *
802 * @param {boolean} state True to set origin mode, false to unset.
803 */
804 hterm.Terminal.prototype.setOriginMode = function(state) {
805 this.options_.originMode = state;
806 };
807
808 /**
809 * Set the insert mode bit.
810 *
811 * If insert mode is on, existing text beyond the cursor position will be
812 * shifted right to make room for new text. Otherwise, new text overwrites
813 * any existing text.
814 *
815 * Defaults to off.
816 *
817 * @param {boolean} state True to set insert mode, false to unset.
818 */
819 hterm.Terminal.prototype.setInsertMode = function(state) {
820 this.options_.insertMode = state;
821 };
822
823 /**
824 * Set the wraparound mode bit.
825 *
826 * If wraparound mode is on, certain VT commands will allow the cursor to wrap
827 * to the start of the following row. Otherwise, the cursor is clamped to the
828 * end of the screen and attempts to write past it are ignored.
829 *
830 * Defaults to on.
831 *
832 * @param {boolean} state True to set wraparound mode, false to unset.
833 */
834 hterm.Terminal.prototype.setWraparound = function(state) {
835 this.options_.wraparound = state;
836 };
837
838 /**
839 * Set the reverse-wraparound mode bit.
840 *
841 * If wraparound mode is off, certain VT commands will allow the cursor to wrap
842 * to the end of the previous row. Otherwise, the cursor is clamped to column
843 * 0.
844 *
845 * Defaults to off.
846 *
847 * @param {boolean} state True to set reverse-wraparound mode, false to unset.
848 */
849 hterm.Terminal.prototype.setReverseWraparound = function(state) {
850 this.options_.reverseWraparound = state;
851 };
852
853 /**
854 * Selects between the primary and alternate screens.
855 *
856 * If alternate mode is on, the alternate screen is active. Otherwise the
857 * primary screen is active.
858 *
859 * Swapping screens has no effect on the scrollback buffer.
860 *
861 * Each screen maintains its own cursor position.
862 *
863 * Defaults to off.
864 *
865 * @param {boolean} state True to set alternate mode, false to unset.
866 */
867 hterm.Terminal.prototype.setAlternateMode = function(state) {
868 this.screen_ = state ? this.alternateScreen_ : this.primaryScreen_;
869
870 this.screen_.setColumnCount(this.screenSize.width);
871
872 var rowDelta = this.screenSize.height - this.screen_.getHeight();
873 if (rowDelta > 0)
874 this.appendRows_(rowDelta);
875
876 this.scrollPort_.invalidateRowRange(
877 this.scrollbackRows_.length,
878 this.scrollbackRows_.length + this.screenSize.height);
879
880 if (this.screen_.cursorPosition.row == -1)
881 this.screen_.setCursorPosition(0, 0);
882
883 this.syncCursorPosition_();
884 };
885
886 /**
887 * Set the cursor-blink mode bit.
888 *
889 * If cursor-blink is on, the cursor will blink when it is visible. Otherwise
890 * a visible cursor does not blink.
891 *
892 * You should make sure to turn blinking off if you're going to dispose of a
893 * terminal, otherwise you'll leak a timeout.
894 *
895 * Defaults to on.
896 *
897 * @param {boolean} state True to set cursor-blink mode, false to unset.
898 */
899 hterm.Terminal.prototype.setCursorBlink = function(state) {
900 this.options_.cursorBlink = state;
901
902 if (!state && this.cursorTimer_) {
903 clearTimeout(this.cursorTimer_);
904 this.cursorTimer_ = null;
905 }
906
907 if (this.options_.cursorVisible)
908 this.setCursorVisible(true);
909 };
910
911 /**
912 * Set the cursor-visible mode bit.
913 *
914 * If cursor-visible is on, the cursor will be visible. Otherwise it will not.
915 *
916 * Defaults to on.
917 *
918 * @param {boolean} state True to set cursor-visible mode, false to unset.
919 */
920 hterm.Terminal.prototype.setCursorVisible = function(state) {
921 this.options_.cursorVisible = state;
922
923 if (!state) {
924 this.cursorNode_.style.display = 'none';
925 return;
926 }
927
928 this.cursorNode_.style.display = 'block';
929
930 if (this.options_.cursorBlink) {
931 if (this.cursorTimer_)
932 return;
933
934 this.cursorTimer_ = setInterval(this.onCursorBlink_.bind(this), 500);
935 } else {
936 if (!this.cursorTimer_)
937 return;
938
939 clearTimeout(this.cursorTimer_);
940 this.cursorTimer_ = null;
941 }
942 };
943
944 /**
945 * Synchronizes the visible cursor with the current cursor coordinates.
946 */
947 hterm.Terminal.prototype.syncCursorPosition_ = function() {
948 var topRowIndex = this.scrollPort_.getTopRowIndex();
949 var bottomRowIndex = this.scrollPort_.getBottomRowIndex(topRowIndex);
950 var cursorRowIndex = this.scrollbackRows_.length +
951 this.screen_.cursorPosition.row;
952
953 if (cursorRowIndex > bottomRowIndex) {
954 // Cursor is scrolled off screen, move it outside of the visible area.
955 this.cursorNode_.style.top = -this.characterSize_.height;
956 return;
957 }
958
959 this.cursorNode_.style.top = this.scrollPort_.visibleRowTopMargin +
960 this.characterSize_.height * (cursorRowIndex - topRowIndex);
961 this.cursorNode_.style.left = this.characterSize_.width *
962 this.screen_.cursorPosition.column;
963 };
964
965 /**
966 * Synchronizes the visible cursor with the current cursor coordinates.
967 *
968 * The sync will happen asynchronously, soon after the call stack winds down.
969 * Multiple calls will be coalesced into a single sync.
970 */
971 hterm.Terminal.prototype.scheduleSyncCursorPosition_ = function() {
972 var f = this.scheduleSyncCursorPosition_;
973
974 if (f.timeout)
975 clearTimeout(f.timeout);
976
977 var self = this;
978 setTimeout(function() {
979 self.syncCursorPosition_();
980 f.timeout = null;
981 }, 100);
982 };
983
984 /**
985 * React when the ScrollPort is scrolled.
986 */
987 hterm.Terminal.prototype.onScroll_ = function() {
988 this.scheduleSyncCursorPosition_();
989 };
990
991 /**
992 * React when the ScrollPort is resized.
993 */
994 hterm.Terminal.prototype.onResize_ = function() {
995 var width = Math.floor(this.scrollPort_.getScreenWidth() /
996 this.characterSize_.width);
997 var height = this.scrollPort_.visibleRowCount;
998
999 if (width == this.screenSize.width && height == this.screenSize.height)
1000 return;
1001
1002 this.screenSize.resize(width, height);
1003
1004 var screenHeight = this.screen_.getHeight();
1005
1006 var deltaRows = this.screenSize.height - screenHeight;
1007
1008 if (deltaRows < 0) {
1009 // Screen got smaller.
1010 var ary = this.screen_.shiftRows(-deltaRows);
1011 this.scrollbackRows_.push.apply(this.scrollbackRows_, ary);
1012 } else if (deltaRows > 0) {
1013 // Screen got larger.
1014
1015 if (deltaRows <= this.scrollbackRows_.length) {
1016 var scrollbackCount = Math.min(deltaRows, this.scrollbackRows_.length);
1017 var rows = this.scrollbackRows_.splice(
1018 0, this.scrollbackRows_.length - scrollbackCount);
1019 this.screen_.unshiftRows(rows);
1020 deltaRows -= scrollbackCount;
1021 }
1022
1023 if (deltaRows)
1024 this.appendRows_(deltaRows);
1025 }
1026
1027 this.screen_.setColumnCount(this.screenSize.width);
1028
1029 if (this.screen_.cursorPosition.row == -1)
1030 this.screen_.setCursorPosition(0, 0);
1031 };
1032
1033 /**
1034 * Service the cursor blink timeout.
1035 */
1036 hterm.Terminal.prototype.onCursorBlink_ = function() {
1037 if (this.cursorNode_.style.display == 'block') {
1038 this.cursorNode_.style.display = 'none';
1039 } else {
1040 this.cursorNode_.style.display = 'block';
1041 }
1042 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698