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