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