1 /*- 2 * Copyright (c) 2009, 2013 The FreeBSD Foundation 3 * All rights reserved. 4 * 5 * This software was developed by Ed Schouten under sponsorship from the 6 * FreeBSD Foundation. 7 * 8 * Portions of this software were developed by Oleksandr Rybalko 9 * under sponsorship from the FreeBSD Foundation. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 #include <sys/cdefs.h> 34 __FBSDID("$FreeBSD$"); 35 36 #include <sys/param.h> 37 #include <sys/kernel.h> 38 #include <sys/lock.h> 39 #include <sys/malloc.h> 40 #include <sys/mutex.h> 41 #include <sys/reboot.h> 42 #include <sys/systm.h> 43 44 #include <dev/vt/vt.h> 45 46 static MALLOC_DEFINE(M_VTBUF, "vtbuf", "vt buffer"); 47 48 #define VTBUF_LOCK(vb) mtx_lock_spin(&(vb)->vb_lock) 49 #define VTBUF_UNLOCK(vb) mtx_unlock_spin(&(vb)->vb_lock) 50 51 #define POS_INDEX(c, r) (((r) << 12) + (c)) 52 #define POS_COPY(d, s) do { \ 53 (d).tp_col = (s).tp_col; \ 54 (d).tp_row = (s).tp_row; \ 55 } while (0) 56 57 #ifndef SC_NO_CUTPASTE 58 static int vtbuf_htw(const struct vt_buf *vb, int row); 59 static int vtbuf_wth(const struct vt_buf *vb, int row); 60 static int vtbuf_in_this_range(int begin, int test, int end, int sz); 61 #endif 62 63 /* 64 * line4 65 * line5 <--- curroffset (terminal output to that line) 66 * line0 67 * line1 <--- roffset (history display from that point) 68 * line2 69 * line3 70 */ 71 int 72 vthistory_seek(struct vt_buf *vb, int offset, int whence) 73 { 74 int diff, top, bottom, roffset; 75 76 /* No scrolling if not enabled. */ 77 if ((vb->vb_flags & VBF_SCROLL) == 0) { 78 if (vb->vb_roffset != vb->vb_curroffset) { 79 vb->vb_roffset = vb->vb_curroffset; 80 return (0xffff); 81 } 82 return (0); /* No changes */ 83 } 84 85 /* "top" may be a negative integer. */ 86 bottom = vb->vb_curroffset; 87 top = (vb->vb_flags & VBF_HISTORY_FULL) ? 88 bottom + vb->vb_scr_size.tp_row - vb->vb_history_size : 89 0; 90 91 roffset = 0; /* Make gcc happy. */ 92 switch (whence) { 93 case VHS_SET: 94 if (offset < 0) 95 offset = 0; 96 roffset = top + offset; 97 break; 98 case VHS_CUR: 99 /* 100 * Operate on copy of offset value, since it temporary 101 * can be bigger than amount of rows in buffer. 102 */ 103 roffset = vb->vb_roffset; 104 if (roffset >= bottom + vb->vb_scr_size.tp_row) 105 roffset -= vb->vb_history_size; 106 107 roffset += offset; 108 roffset = MAX(roffset, top); 109 roffset = MIN(roffset, bottom); 110 111 if (roffset < 0) 112 roffset = vb->vb_history_size + roffset; 113 114 break; 115 case VHS_END: 116 /* Go to current offset. */ 117 roffset = vb->vb_curroffset; 118 break; 119 } 120 121 diff = vb->vb_roffset != roffset; 122 vb->vb_roffset = roffset; 123 124 return (diff); 125 } 126 127 void 128 vthistory_addlines(struct vt_buf *vb, int offset) 129 { 130 #ifndef SC_NO_CUTPASTE 131 int cur, sz; 132 #endif 133 134 vb->vb_curroffset += offset; 135 if (vb->vb_curroffset < 0) 136 vb->vb_curroffset = 0; 137 if (vb->vb_curroffset + vb->vb_scr_size.tp_row >= vb->vb_history_size) 138 vb->vb_flags |= VBF_HISTORY_FULL; 139 vb->vb_curroffset %= vb->vb_history_size; 140 if ((vb->vb_flags & VBF_SCROLL) == 0) { 141 vb->vb_roffset = vb->vb_curroffset; 142 } 143 144 #ifndef SC_NO_CUTPASTE 145 sz = vb->vb_history_size; 146 cur = vb->vb_roffset + vb->vb_scr_size.tp_row + sz - 1; 147 if (vtbuf_in_this_range(cur, vb->vb_mark_start.tp_row, cur + offset, sz) || 148 vtbuf_in_this_range(cur, vb->vb_mark_end.tp_row, cur + offset, sz)) { 149 /* clear screen selection */ 150 vb->vb_mark_start.tp_row = vb->vb_mark_end.tp_row; 151 vb->vb_mark_start.tp_col = vb->vb_mark_end.tp_col; 152 } 153 #endif 154 } 155 156 void 157 vthistory_getpos(const struct vt_buf *vb, unsigned int *offset) 158 { 159 160 *offset = vb->vb_roffset; 161 } 162 163 #ifndef SC_NO_CUTPASTE /* Only mouse support use it now. */ 164 /* Translate history row to current view row number. */ 165 static int 166 vtbuf_htw(const struct vt_buf *vb, int row) 167 { 168 169 /* 170 * total 1000 rows. 171 * History offset roffset winrow 172 * 205 200 ((205 - 200 + 1000) % 1000) = 5 173 * 90 990 ((90 - 990 + 1000) % 1000) = 100 174 */ 175 return ((row - vb->vb_roffset + vb->vb_history_size) % 176 vb->vb_history_size); 177 } 178 179 /* Translate current view row number to history row. */ 180 static int 181 vtbuf_wth(const struct vt_buf *vb, int row) 182 { 183 184 return ((vb->vb_roffset + row) % vb->vb_history_size); 185 } 186 187 /* 188 * Test if an index in a circular buffer is within a range. 189 * 190 * begin - start index 191 * end - end index 192 * test - test index 193 * sz - size of circular buffer when it turns over 194 */ 195 static int 196 vtbuf_in_this_range(int begin, int test, int end, int sz) 197 { 198 199 begin %= sz; 200 end %= sz; 201 202 /* check for inversion */ 203 if (begin > end) 204 return (test >= begin || test < end); 205 else 206 return (test >= begin && test < end); 207 } 208 #endif 209 210 int 211 vtbuf_iscursor(const struct vt_buf *vb, int row, int col) 212 { 213 #ifndef SC_NO_CUTPASTE 214 int sc, sr, sz, ec, er, tmp; 215 #endif 216 217 if ((vb->vb_flags & (VBF_CURSOR|VBF_SCROLL)) == VBF_CURSOR && 218 (vb->vb_cursor.tp_row == row) && (vb->vb_cursor.tp_col == col)) 219 return (1); 220 221 #ifndef SC_NO_CUTPASTE 222 /* Mark cut/paste region. */ 223 if (vb->vb_mark_start.tp_col == vb->vb_mark_end.tp_col && 224 vb->vb_mark_start.tp_row == vb->vb_mark_end.tp_row) 225 return (0); 226 227 sc = vb->vb_mark_start.tp_col; 228 sr = vb->vb_mark_start.tp_row; 229 ec = vb->vb_mark_end.tp_col; 230 er = vb->vb_mark_end.tp_row; 231 232 /* 233 * Information about if the selection was made bottom-top or 234 * top-bottom is lost due to modulo arithmetics and needs to 235 * be recovered: 236 */ 237 sz = vb->vb_history_size; 238 tmp = (sz + er - sr) % sz; 239 row = vtbuf_wth(vb, row); 240 241 /* Swap start and end if start > end */ 242 if ((2 * tmp) > sz || (tmp == 0 && sc > ec)) { 243 tmp = sc; sc = ec; ec = tmp; 244 tmp = sr; sr = er; er = tmp; 245 } 246 247 if (vtbuf_in_this_range(POS_INDEX(sc, sr), POS_INDEX(col, row), 248 POS_INDEX(ec, er), POS_INDEX(0, sz))) 249 return (1); 250 #endif 251 252 return (0); 253 } 254 255 static inline void 256 vtbuf_dirty_locked(struct vt_buf *vb, const term_rect_t *area) 257 { 258 259 if (vb->vb_dirtyrect.tr_begin.tp_row > area->tr_begin.tp_row) 260 vb->vb_dirtyrect.tr_begin.tp_row = area->tr_begin.tp_row; 261 if (vb->vb_dirtyrect.tr_begin.tp_col > area->tr_begin.tp_col) 262 vb->vb_dirtyrect.tr_begin.tp_col = area->tr_begin.tp_col; 263 if (vb->vb_dirtyrect.tr_end.tp_row < area->tr_end.tp_row) 264 vb->vb_dirtyrect.tr_end.tp_row = area->tr_end.tp_row; 265 if (vb->vb_dirtyrect.tr_end.tp_col < area->tr_end.tp_col) 266 vb->vb_dirtyrect.tr_end.tp_col = area->tr_end.tp_col; 267 } 268 269 void 270 vtbuf_dirty(struct vt_buf *vb, const term_rect_t *area) 271 { 272 273 VTBUF_LOCK(vb); 274 vtbuf_dirty_locked(vb, area); 275 VTBUF_UNLOCK(vb); 276 } 277 278 static inline void 279 vtbuf_dirty_cell_locked(struct vt_buf *vb, const term_pos_t *p) 280 { 281 term_rect_t area; 282 283 area.tr_begin = *p; 284 area.tr_end.tp_row = p->tp_row + 1; 285 area.tr_end.tp_col = p->tp_col + 1; 286 vtbuf_dirty_locked(vb, &area); 287 } 288 289 static void 290 vtbuf_make_undirty(struct vt_buf *vb) 291 { 292 293 vb->vb_dirtyrect.tr_begin = vb->vb_scr_size; 294 vb->vb_dirtyrect.tr_end.tp_row = vb->vb_dirtyrect.tr_end.tp_col = 0; 295 } 296 297 void 298 vtbuf_undirty(struct vt_buf *vb, term_rect_t *r) 299 { 300 301 VTBUF_LOCK(vb); 302 *r = vb->vb_dirtyrect; 303 vtbuf_make_undirty(vb); 304 VTBUF_UNLOCK(vb); 305 } 306 307 void 308 vtbuf_copy(struct vt_buf *vb, const term_rect_t *r, const term_pos_t *p2) 309 { 310 const term_pos_t *p1 = &r->tr_begin; 311 term_rect_t area; 312 unsigned int rows, cols; 313 int pr, rdiff; 314 315 KASSERT(r->tr_begin.tp_row < vb->vb_scr_size.tp_row, 316 ("vtbuf_copy begin.tp_row %d must be less than screen width %d", 317 r->tr_begin.tp_row, vb->vb_scr_size.tp_row)); 318 KASSERT(r->tr_begin.tp_col < vb->vb_scr_size.tp_col, 319 ("vtbuf_copy begin.tp_col %d must be less than screen height %d", 320 r->tr_begin.tp_col, vb->vb_scr_size.tp_col)); 321 322 KASSERT(r->tr_end.tp_row <= vb->vb_scr_size.tp_row, 323 ("vtbuf_copy end.tp_row %d must be less than screen width %d", 324 r->tr_end.tp_row, vb->vb_scr_size.tp_row)); 325 KASSERT(r->tr_end.tp_col <= vb->vb_scr_size.tp_col, 326 ("vtbuf_copy end.tp_col %d must be less than screen height %d", 327 r->tr_end.tp_col, vb->vb_scr_size.tp_col)); 328 329 KASSERT(p2->tp_row < vb->vb_scr_size.tp_row, 330 ("vtbuf_copy tp_row %d must be less than screen width %d", 331 p2->tp_row, vb->vb_scr_size.tp_row)); 332 KASSERT(p2->tp_col < vb->vb_scr_size.tp_col, 333 ("vtbuf_copy tp_col %d must be less than screen height %d", 334 p2->tp_col, vb->vb_scr_size.tp_col)); 335 336 rows = r->tr_end.tp_row - r->tr_begin.tp_row; 337 rdiff = r->tr_begin.tp_row - p2->tp_row; 338 cols = r->tr_end.tp_col - r->tr_begin.tp_col; 339 if (r->tr_begin.tp_row > p2->tp_row && r->tr_begin.tp_col == 0 && 340 r->tr_end.tp_col == vb->vb_scr_size.tp_col && /* Full row. */ 341 (rows + rdiff) == vb->vb_scr_size.tp_row && /* Whole screen. */ 342 rdiff > 0) { /* Only forward direction. Do not eat history. */ 343 vthistory_addlines(vb, rdiff); 344 } else if (p2->tp_row < p1->tp_row) { 345 /* Handle overlapping copies of line segments. */ 346 /* Move data up. */ 347 for (pr = 0; pr < rows; pr++) 348 memmove( 349 &VTBUF_FIELD(vb, p2->tp_row + pr, p2->tp_col), 350 &VTBUF_FIELD(vb, p1->tp_row + pr, p1->tp_col), 351 cols * sizeof(term_char_t)); 352 } else { 353 /* Move data down. */ 354 for (pr = rows - 1; pr >= 0; pr--) 355 memmove( 356 &VTBUF_FIELD(vb, p2->tp_row + pr, p2->tp_col), 357 &VTBUF_FIELD(vb, p1->tp_row + pr, p1->tp_col), 358 cols * sizeof(term_char_t)); 359 } 360 361 area.tr_begin = *p2; 362 area.tr_end.tp_row = MIN(p2->tp_row + rows, vb->vb_scr_size.tp_row); 363 area.tr_end.tp_col = MIN(p2->tp_col + cols, vb->vb_scr_size.tp_col); 364 vtbuf_dirty(vb, &area); 365 } 366 367 static void 368 vtbuf_fill(struct vt_buf *vb, const term_rect_t *r, term_char_t c) 369 { 370 unsigned int pr, pc; 371 term_char_t *row; 372 373 for (pr = r->tr_begin.tp_row; pr < r->tr_end.tp_row; pr++) { 374 row = vb->vb_rows[(vb->vb_curroffset + pr) % 375 VTBUF_MAX_HEIGHT(vb)]; 376 for (pc = r->tr_begin.tp_col; pc < r->tr_end.tp_col; pc++) { 377 row[pc] = c; 378 } 379 } 380 } 381 382 void 383 vtbuf_fill_locked(struct vt_buf *vb, const term_rect_t *r, term_char_t c) 384 { 385 KASSERT(r->tr_begin.tp_row < vb->vb_scr_size.tp_row, 386 ("vtbuf_fill_locked begin.tp_row %d must be < screen height %d", 387 r->tr_begin.tp_row, vb->vb_scr_size.tp_row)); 388 KASSERT(r->tr_begin.tp_col < vb->vb_scr_size.tp_col, 389 ("vtbuf_fill_locked begin.tp_col %d must be < screen width %d", 390 r->tr_begin.tp_col, vb->vb_scr_size.tp_col)); 391 392 KASSERT(r->tr_end.tp_row <= vb->vb_scr_size.tp_row, 393 ("vtbuf_fill_locked end.tp_row %d must be <= screen height %d", 394 r->tr_end.tp_row, vb->vb_scr_size.tp_row)); 395 KASSERT(r->tr_end.tp_col <= vb->vb_scr_size.tp_col, 396 ("vtbuf_fill_locked end.tp_col %d must be <= screen width %d", 397 r->tr_end.tp_col, vb->vb_scr_size.tp_col)); 398 399 VTBUF_LOCK(vb); 400 vtbuf_fill(vb, r, c); 401 vtbuf_dirty_locked(vb, r); 402 VTBUF_UNLOCK(vb); 403 } 404 405 static void 406 vtbuf_init_rows(struct vt_buf *vb) 407 { 408 int r; 409 410 vb->vb_history_size = MAX(vb->vb_history_size, vb->vb_scr_size.tp_row); 411 412 for (r = 0; r < vb->vb_history_size; r++) 413 vb->vb_rows[r] = &vb->vb_buffer[r * vb->vb_scr_size.tp_col]; 414 } 415 416 void 417 vtbuf_init_early(struct vt_buf *vb) 418 { 419 term_rect_t rect; 420 421 vb->vb_flags |= VBF_CURSOR; 422 vb->vb_roffset = 0; 423 vb->vb_curroffset = 0; 424 vb->vb_mark_start.tp_row = 0; 425 vb->vb_mark_start.tp_col = 0; 426 vb->vb_mark_end.tp_row = 0; 427 vb->vb_mark_end.tp_col = 0; 428 429 vtbuf_init_rows(vb); 430 rect.tr_begin.tp_row = rect.tr_begin.tp_col = 0; 431 rect.tr_end.tp_col = vb->vb_scr_size.tp_col; 432 rect.tr_end.tp_row = vb->vb_history_size; 433 vtbuf_fill(vb, &rect, VTBUF_SPACE_CHAR(TERMINAL_NORM_ATTR)); 434 vtbuf_make_undirty(vb); 435 if ((vb->vb_flags & VBF_MTX_INIT) == 0) { 436 mtx_init(&vb->vb_lock, "vtbuf", NULL, MTX_SPIN); 437 vb->vb_flags |= VBF_MTX_INIT; 438 } 439 } 440 441 void 442 vtbuf_init(struct vt_buf *vb, const term_pos_t *p) 443 { 444 int sz; 445 446 vb->vb_scr_size = *p; 447 vb->vb_history_size = VBF_DEFAULT_HISTORY_SIZE; 448 449 if ((vb->vb_flags & VBF_STATIC) == 0) { 450 sz = vb->vb_history_size * p->tp_col * sizeof(term_char_t); 451 vb->vb_buffer = malloc(sz, M_VTBUF, M_WAITOK | M_ZERO); 452 453 sz = vb->vb_history_size * sizeof(term_char_t *); 454 vb->vb_rows = malloc(sz, M_VTBUF, M_WAITOK | M_ZERO); 455 } 456 457 vtbuf_init_early(vb); 458 } 459 460 void 461 vtbuf_sethistory_size(struct vt_buf *vb, int size) 462 { 463 term_pos_t p; 464 465 /* With same size */ 466 p.tp_row = vb->vb_scr_size.tp_row; 467 p.tp_col = vb->vb_scr_size.tp_col; 468 vtbuf_grow(vb, &p, size); 469 } 470 471 void 472 vtbuf_grow(struct vt_buf *vb, const term_pos_t *p, unsigned int history_size) 473 { 474 term_char_t *old, *new, **rows, **oldrows, **copyrows, *row, *oldrow; 475 int bufsize, rowssize, w, h, c, r, history_was_full; 476 unsigned int old_history_size; 477 term_rect_t rect; 478 479 history_size = MAX(history_size, p->tp_row); 480 481 /* Allocate new buffer. */ 482 bufsize = history_size * p->tp_col * sizeof(term_char_t); 483 new = malloc(bufsize, M_VTBUF, M_WAITOK | M_ZERO); 484 rowssize = history_size * sizeof(term_pos_t *); 485 rows = malloc(rowssize, M_VTBUF, M_WAITOK | M_ZERO); 486 487 /* Toggle it. */ 488 VTBUF_LOCK(vb); 489 old = vb->vb_flags & VBF_STATIC ? NULL : vb->vb_buffer; 490 oldrows = vb->vb_flags & VBF_STATIC ? NULL : vb->vb_rows; 491 copyrows = vb->vb_rows; 492 493 w = vb->vb_scr_size.tp_col; 494 h = vb->vb_scr_size.tp_row; 495 old_history_size = vb->vb_history_size; 496 history_was_full = vb->vb_flags & VBF_HISTORY_FULL; 497 498 vb->vb_history_size = history_size; 499 vb->vb_buffer = new; 500 vb->vb_rows = rows; 501 vb->vb_flags &= ~VBF_STATIC; 502 vb->vb_scr_size = *p; 503 vtbuf_init_rows(vb); 504 505 /* Copy history and fill extra space if needed. */ 506 if (history_size > old_history_size) { 507 /* 508 * Copy rows to the new buffer. The first row in the history 509 * is back to index 0, ie. the new buffer doesn't cycle. 510 * 511 * The rest of the new buffer is initialized with blank 512 * content. 513 */ 514 for (r = 0; r < old_history_size; r ++) { 515 row = rows[r]; 516 517 /* Compute the corresponding row in the old buffer. */ 518 if (history_was_full) 519 /* 520 * The buffer is full, the "top" row is 521 * the one just after the viewable area 522 * (curroffset + viewable height) in the 523 * cycling buffer. The corresponding row 524 * is computed from this top row. 525 */ 526 oldrow = copyrows[ 527 (vb->vb_curroffset + h + r) % 528 old_history_size]; 529 else 530 /* 531 * The buffer is not full, therefore, 532 * we didn't cycle already. The 533 * corresponding rows are the same in 534 * both buffers. 535 */ 536 oldrow = copyrows[r]; 537 538 memmove(row, oldrow, 539 MIN(p->tp_col, w) * sizeof(term_char_t)); 540 541 /* 542 * XXX VTBUF_SPACE_CHAR(TERMINAL_NORM_ATTR) will 543 * extended lines of kernel text using the wrong 544 * background color. 545 */ 546 for (c = MIN(p->tp_col, w); c < p->tp_col; c++) { 547 row[c] = VTBUF_SPACE_CHAR(TERMINAL_NORM_ATTR); 548 } 549 } 550 551 /* Fill remaining rows. */ 552 rect.tr_begin.tp_col = 0; 553 rect.tr_begin.tp_row = old_history_size; 554 rect.tr_end.tp_col = p->tp_col; 555 rect.tr_end.tp_row = p->tp_row; 556 vtbuf_fill(vb, &rect, VTBUF_SPACE_CHAR(TERMINAL_NORM_ATTR)); 557 558 vb->vb_flags &= ~VBF_HISTORY_FULL; 559 } else { 560 /* 561 * Copy rows to the new buffer. The first row in the history 562 * is back to index 0, ie. the new buffer doesn't cycle. 563 * 564 * (old_history_size - history_size) lines of history are 565 * dropped. 566 */ 567 for (r = 0; r < history_size; r ++) { 568 row = rows[r]; 569 570 /* 571 * Compute the corresponding row in the old buffer. 572 * 573 * See the equivalent if{} block above for an 574 * explanation. 575 */ 576 if (history_was_full) 577 oldrow = copyrows[ 578 (vb->vb_curroffset + h + r + 579 (old_history_size - history_size)) % 580 old_history_size]; 581 else 582 oldrow = copyrows[ 583 (r + (old_history_size - history_size)) % 584 old_history_size]; 585 586 memmove(row, oldrow, 587 MIN(p->tp_col, w) * sizeof(term_char_t)); 588 589 /* 590 * XXX VTBUF_SPACE_CHAR(TERMINAL_NORM_ATTR) will 591 * extended lines of kernel text using the wrong 592 * background color. 593 */ 594 for (c = MIN(p->tp_col, w); c < p->tp_col; c++) { 595 row[c] = VTBUF_SPACE_CHAR(TERMINAL_NORM_ATTR); 596 } 597 } 598 599 if (!history_was_full && 600 (vb->vb_curroffset + h) >= history_size) 601 vb->vb_flags |= VBF_HISTORY_FULL; 602 } 603 604 /* 605 * If the screen is already filled (there are non-visible lines 606 * above the current viewable area), adjust curroffset to the 607 * new viewable area. 608 */ 609 if (!history_was_full && vb->vb_curroffset > 0) { 610 vb->vb_curroffset = vb->vb_curroffset + h - p->tp_row; 611 if (vb->vb_curroffset < 0) 612 vb->vb_curroffset += vb->vb_history_size; 613 vb->vb_curroffset %= vb->vb_history_size; 614 vb->vb_roffset = vb->vb_curroffset; 615 } 616 617 /* Adjust cursor position. */ 618 if (vb->vb_cursor.tp_col > p->tp_col - 1) 619 /* 620 * Move cursor to the last column, in case its previous 621 * position is outside of the new screen area. 622 */ 623 vb->vb_cursor.tp_col = p->tp_col - 1; 624 625 if (vb->vb_curroffset > 0 || vb->vb_cursor.tp_row > p->tp_row - 1) 626 /* Move cursor to the last line on the screen. */ 627 vb->vb_cursor.tp_row = p->tp_row - 1; 628 629 vtbuf_make_undirty(vb); 630 VTBUF_UNLOCK(vb); 631 632 /* Deallocate old buffer. */ 633 free(old, M_VTBUF); 634 free(oldrows, M_VTBUF); 635 } 636 637 void 638 vtbuf_putchar(struct vt_buf *vb, const term_pos_t *p, term_char_t c) 639 { 640 term_char_t *row; 641 642 KASSERT(p->tp_row < vb->vb_scr_size.tp_row, 643 ("vtbuf_putchar tp_row %d must be less than screen width %d", 644 p->tp_row, vb->vb_scr_size.tp_row)); 645 KASSERT(p->tp_col < vb->vb_scr_size.tp_col, 646 ("vtbuf_putchar tp_col %d must be less than screen height %d", 647 p->tp_col, vb->vb_scr_size.tp_col)); 648 649 row = vb->vb_rows[(vb->vb_curroffset + p->tp_row) % 650 VTBUF_MAX_HEIGHT(vb)]; 651 if (row[p->tp_col] != c) { 652 VTBUF_LOCK(vb); 653 row[p->tp_col] = c; 654 vtbuf_dirty_cell_locked(vb, p); 655 VTBUF_UNLOCK(vb); 656 } 657 } 658 659 void 660 vtbuf_cursor_position(struct vt_buf *vb, const term_pos_t *p) 661 { 662 663 if (vb->vb_flags & VBF_CURSOR) { 664 VTBUF_LOCK(vb); 665 vtbuf_dirty_cell_locked(vb, &vb->vb_cursor); 666 vb->vb_cursor = *p; 667 vtbuf_dirty_cell_locked(vb, &vb->vb_cursor); 668 VTBUF_UNLOCK(vb); 669 } else { 670 vb->vb_cursor = *p; 671 } 672 } 673 674 #ifndef SC_NO_CUTPASTE 675 static void 676 vtbuf_flush_mark(struct vt_buf *vb) 677 { 678 term_rect_t area; 679 int s, e; 680 681 /* Notify renderer to update marked region. */ 682 if ((vb->vb_mark_start.tp_col != vb->vb_mark_end.tp_col) || 683 (vb->vb_mark_start.tp_row != vb->vb_mark_end.tp_row)) { 684 685 s = vtbuf_htw(vb, vb->vb_mark_start.tp_row); 686 e = vtbuf_htw(vb, vb->vb_mark_end.tp_row); 687 688 area.tr_begin.tp_col = 0; 689 area.tr_begin.tp_row = MIN(s, e); 690 691 area.tr_end.tp_col = vb->vb_scr_size.tp_col; 692 area.tr_end.tp_row = MAX(s, e) + 1; 693 694 vtbuf_dirty(vb, &area); 695 } 696 } 697 698 int 699 vtbuf_get_marked_len(struct vt_buf *vb) 700 { 701 int ei, si, sz; 702 term_pos_t s, e; 703 704 /* Swap according to window coordinates. */ 705 if (POS_INDEX(vtbuf_htw(vb, vb->vb_mark_start.tp_row), 706 vb->vb_mark_start.tp_col) > 707 POS_INDEX(vtbuf_htw(vb, vb->vb_mark_end.tp_row), 708 vb->vb_mark_end.tp_col)) { 709 POS_COPY(e, vb->vb_mark_start); 710 POS_COPY(s, vb->vb_mark_end); 711 } else { 712 POS_COPY(s, vb->vb_mark_start); 713 POS_COPY(e, vb->vb_mark_end); 714 } 715 716 si = s.tp_row * vb->vb_scr_size.tp_col + s.tp_col; 717 ei = e.tp_row * vb->vb_scr_size.tp_col + e.tp_col; 718 719 /* Number symbols and number of rows to inject \n */ 720 sz = ei - si + ((e.tp_row - s.tp_row) * 2); 721 722 return (sz * sizeof(term_char_t)); 723 } 724 725 void 726 vtbuf_extract_marked(struct vt_buf *vb, term_char_t *buf, int sz) 727 { 728 int i, r, c, cs, ce; 729 term_pos_t s, e; 730 731 /* Swap according to window coordinates. */ 732 if (POS_INDEX(vtbuf_htw(vb, vb->vb_mark_start.tp_row), 733 vb->vb_mark_start.tp_col) > 734 POS_INDEX(vtbuf_htw(vb, vb->vb_mark_end.tp_row), 735 vb->vb_mark_end.tp_col)) { 736 POS_COPY(e, vb->vb_mark_start); 737 POS_COPY(s, vb->vb_mark_end); 738 } else { 739 POS_COPY(s, vb->vb_mark_start); 740 POS_COPY(e, vb->vb_mark_end); 741 } 742 743 i = 0; 744 for (r = s.tp_row; r <= e.tp_row; r ++) { 745 cs = (r == s.tp_row)?s.tp_col:0; 746 ce = (r == e.tp_row)?e.tp_col:vb->vb_scr_size.tp_col; 747 for (c = cs; c < ce; c ++) { 748 buf[i++] = vb->vb_rows[r][c]; 749 } 750 /* Add new line for all rows, but not for last one. */ 751 if (r != e.tp_row) { 752 buf[i++] = '\r'; 753 buf[i++] = '\n'; 754 } 755 } 756 } 757 758 int 759 vtbuf_set_mark(struct vt_buf *vb, int type, int col, int row) 760 { 761 term_char_t *r; 762 int i; 763 764 switch (type) { 765 case VTB_MARK_END: /* B1 UP */ 766 if (vb->vb_mark_last != VTB_MARK_MOVE) 767 return (0); 768 /* FALLTHROUGH */ 769 case VTB_MARK_MOVE: 770 case VTB_MARK_EXTEND: 771 vtbuf_flush_mark(vb); /* Clean old mark. */ 772 vb->vb_mark_end.tp_col = col; 773 vb->vb_mark_end.tp_row = vtbuf_wth(vb, row); 774 break; 775 case VTB_MARK_START: 776 vtbuf_flush_mark(vb); /* Clean old mark. */ 777 vb->vb_mark_start.tp_col = col; 778 vb->vb_mark_start.tp_row = vtbuf_wth(vb, row); 779 /* Start again, so clear end point. */ 780 vb->vb_mark_end.tp_col = col; 781 vb->vb_mark_end.tp_row = vtbuf_wth(vb, row); 782 break; 783 case VTB_MARK_WORD: 784 vtbuf_flush_mark(vb); /* Clean old mark. */ 785 vb->vb_mark_start.tp_row = vb->vb_mark_end.tp_row = 786 vtbuf_wth(vb, row); 787 r = vb->vb_rows[vb->vb_mark_start.tp_row]; 788 for (i = col; i >= 0; i --) { 789 if (TCHAR_CHARACTER(r[i]) == ' ') { 790 vb->vb_mark_start.tp_col = i + 1; 791 break; 792 } 793 } 794 for (i = col; i < vb->vb_scr_size.tp_col; i ++) { 795 if (TCHAR_CHARACTER(r[i]) == ' ') { 796 vb->vb_mark_end.tp_col = i; 797 break; 798 } 799 } 800 if (vb->vb_mark_start.tp_col > vb->vb_mark_end.tp_col) 801 vb->vb_mark_start.tp_col = vb->vb_mark_end.tp_col; 802 break; 803 case VTB_MARK_ROW: 804 vtbuf_flush_mark(vb); /* Clean old mark. */ 805 vb->vb_mark_start.tp_col = 0; 806 vb->vb_mark_end.tp_col = vb->vb_scr_size.tp_col; 807 vb->vb_mark_start.tp_row = vb->vb_mark_end.tp_row = 808 vtbuf_wth(vb, row); 809 break; 810 case VTB_MARK_NONE: 811 vb->vb_mark_last = type; 812 /* FALLTHROUGH */ 813 default: 814 /* panic? */ 815 return (0); 816 } 817 818 vb->vb_mark_last = type; 819 /* Draw new marked region. */ 820 vtbuf_flush_mark(vb); 821 return (1); 822 } 823 #endif 824 825 void 826 vtbuf_cursor_visibility(struct vt_buf *vb, int yes) 827 { 828 int oflags, nflags; 829 830 VTBUF_LOCK(vb); 831 oflags = vb->vb_flags; 832 if (yes) 833 vb->vb_flags |= VBF_CURSOR; 834 else 835 vb->vb_flags &= ~VBF_CURSOR; 836 nflags = vb->vb_flags; 837 838 if (oflags != nflags) 839 vtbuf_dirty_cell_locked(vb, &vb->vb_cursor); 840 VTBUF_UNLOCK(vb); 841 } 842 843 void 844 vtbuf_scroll_mode(struct vt_buf *vb, int yes) 845 { 846 int oflags, nflags; 847 848 VTBUF_LOCK(vb); 849 oflags = vb->vb_flags; 850 if (yes) 851 vb->vb_flags |= VBF_SCROLL; 852 else 853 vb->vb_flags &= ~VBF_SCROLL; 854 nflags = vb->vb_flags; 855 856 if (oflags != nflags) 857 vtbuf_dirty_cell_locked(vb, &vb->vb_cursor); 858 VTBUF_UNLOCK(vb); 859 } 860 861