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