OLD | NEW |
---|---|
(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 }; | |
OLD | NEW |