1219888Sed/*- 2257547Sray * Copyright (c) 2009, 2013 The FreeBSD Foundation 3219888Sed * All rights reserved. 4219888Sed * 5219888Sed * This software was developed by Ed Schouten under sponsorship from the 6219888Sed * FreeBSD Foundation. 7219888Sed * 8257547Sray * Portions of this software were developed by Oleksandr Rybalko 9257547Sray * under sponsorship from the FreeBSD Foundation. 10257547Sray * 11219888Sed * Redistribution and use in source and binary forms, with or without 12219888Sed * modification, are permitted provided that the following conditions 13219888Sed * are met: 14219888Sed * 1. Redistributions of source code must retain the above copyright 15219888Sed * notice, this list of conditions and the following disclaimer. 16219888Sed * 2. Redistributions in binary form must reproduce the above copyright 17219888Sed * notice, this list of conditions and the following disclaimer in the 18219888Sed * documentation and/or other materials provided with the distribution. 19219888Sed * 20219888Sed * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21219888Sed * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22219888Sed * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23219888Sed * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24219888Sed * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25219888Sed * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26219888Sed * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27219888Sed * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28219888Sed * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29219888Sed * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30219888Sed * SUCH DAMAGE. 31219888Sed */ 32219888Sed 33219888Sed#include <sys/cdefs.h> 34219888Sed__FBSDID("$FreeBSD: stable/11/sys/dev/vt/vt_buf.c 331722 2018-03-29 02:50:57Z eadler $"); 35219888Sed 36219888Sed#include <sys/param.h> 37219888Sed#include <sys/kernel.h> 38219888Sed#include <sys/lock.h> 39219888Sed#include <sys/malloc.h> 40219888Sed#include <sys/mutex.h> 41267978Smarius#include <sys/reboot.h> 42219888Sed#include <sys/systm.h> 43219888Sed 44219888Sed#include <dev/vt/vt.h> 45219888Sed 46219888Sedstatic MALLOC_DEFINE(M_VTBUF, "vtbuf", "vt buffer"); 47219888Sed 48219888Sed#define VTBUF_LOCK(vb) mtx_lock_spin(&(vb)->vb_lock) 49219888Sed#define VTBUF_UNLOCK(vb) mtx_unlock_spin(&(vb)->vb_lock) 50257971Sray 51258090Sray#define POS_INDEX(c, r) (((r) << 12) + (c)) 52258090Sray#define POS_COPY(d, s) do { \ 53258090Sray (d).tp_col = (s).tp_col; \ 54258090Sray (d).tp_row = (s).tp_row; \ 55258090Sray} while (0) 56257971Sray 57321199Semaste#ifndef SC_NO_CUTPASTE 58321199Semastestatic int vtbuf_htw(const struct vt_buf *vb, int row); 59321199Semastestatic int vtbuf_wth(const struct vt_buf *vb, int row); 60321199Semastestatic int vtbuf_in_this_range(int begin, int test, int end, int sz); 61321199Semaste#endif 62258090Sray 63256145Sray/* 64256145Sray * line4 65256145Sray * line5 <--- curroffset (terminal output to that line) 66256145Sray * line0 67256145Sray * line1 <--- roffset (history display from that point) 68256145Sray * line2 69256145Sray * line3 70256145Sray */ 71256145Srayint 72256145Srayvthistory_seek(struct vt_buf *vb, int offset, int whence) 73256145Sray{ 74257074Sray int diff, top, bottom, roffset; 75219888Sed 76256145Sray /* No scrolling if not enabled. */ 77256145Sray if ((vb->vb_flags & VBF_SCROLL) == 0) { 78256145Sray if (vb->vb_roffset != vb->vb_curroffset) { 79256145Sray vb->vb_roffset = vb->vb_curroffset; 80257074Sray return (0xffff); 81256145Sray } 82256145Sray return (0); /* No changes */ 83256145Sray } 84256970Sray 85271871Sdumbbell /* "top" may be a negative integer. */ 86271871Sdumbbell bottom = vb->vb_curroffset; 87271871Sdumbbell top = (vb->vb_flags & VBF_HISTORY_FULL) ? 88271871Sdumbbell bottom + vb->vb_scr_size.tp_row - vb->vb_history_size : 89271871Sdumbbell 0; 90271871Sdumbbell 91271899Sbz roffset = 0; /* Make gcc happy. */ 92256145Sray switch (whence) { 93256145Sray case VHS_SET: 94271871Sdumbbell if (offset < 0) 95271871Sdumbbell offset = 0; 96271871Sdumbbell roffset = top + offset; 97256145Sray break; 98256145Sray case VHS_CUR: 99271871Sdumbbell /* 100271871Sdumbbell * Operate on copy of offset value, since it temporary 101271871Sdumbbell * can be bigger than amount of rows in buffer. 102271871Sdumbbell */ 103271871Sdumbbell roffset = vb->vb_roffset; 104271871Sdumbbell if (roffset >= bottom + vb->vb_scr_size.tp_row) 105271871Sdumbbell roffset -= vb->vb_history_size; 106271871Sdumbbell 107256145Sray roffset += offset; 108271871Sdumbbell roffset = MAX(roffset, top); 109271871Sdumbbell roffset = MIN(roffset, bottom); 110271871Sdumbbell 111271871Sdumbbell if (roffset < 0) 112271871Sdumbbell roffset = vb->vb_history_size + roffset; 113271871Sdumbbell 114256145Sray break; 115256145Sray case VHS_END: 116256145Sray /* Go to current offset. */ 117271871Sdumbbell roffset = vb->vb_curroffset; 118256145Sray break; 119256145Sray } 120256145Sray 121271871Sdumbbell diff = vb->vb_roffset != roffset; 122271871Sdumbbell vb->vb_roffset = roffset; 123256145Sray 124271871Sdumbbell return (diff); 125256145Sray} 126256145Sray 127256145Srayvoid 128256145Srayvthistory_addlines(struct vt_buf *vb, int offset) 129256145Sray{ 130321199Semaste#ifndef SC_NO_CUTPASTE 131321199Semaste int cur, sz; 132321199Semaste#endif 133256145Sray 134256145Sray vb->vb_curroffset += offset; 135330641Seadler if (vb->vb_curroffset + vb->vb_scr_size.tp_row >= vb->vb_history_size) { 136271871Sdumbbell vb->vb_flags |= VBF_HISTORY_FULL; 137330641Seadler vb->vb_curroffset %= vb->vb_history_size; 138330641Seadler } 139256145Sray if ((vb->vb_flags & VBF_SCROLL) == 0) { 140256145Sray vb->vb_roffset = vb->vb_curroffset; 141256145Sray } 142321199Semaste 143321199Semaste#ifndef SC_NO_CUTPASTE 144321199Semaste sz = vb->vb_history_size; 145321199Semaste cur = vb->vb_roffset + vb->vb_scr_size.tp_row + sz - 1; 146321199Semaste if (vtbuf_in_this_range(cur, vb->vb_mark_start.tp_row, cur + offset, sz) || 147321199Semaste vtbuf_in_this_range(cur, vb->vb_mark_end.tp_row, cur + offset, sz)) { 148321199Semaste /* clear screen selection */ 149321199Semaste vb->vb_mark_start.tp_row = vb->vb_mark_end.tp_row; 150321199Semaste vb->vb_mark_start.tp_col = vb->vb_mark_end.tp_col; 151321199Semaste } 152321199Semaste#endif 153256145Sray} 154256145Sray 155256145Srayvoid 156256145Srayvthistory_getpos(const struct vt_buf *vb, unsigned int *offset) 157256145Sray{ 158256145Sray 159256145Sray *offset = vb->vb_roffset; 160256145Sray} 161256145Sray 162259129Sray#ifndef SC_NO_CUTPASTE /* Only mouse support use it now. */ 163258090Sray/* Translate history row to current view row number. */ 164258090Sraystatic int 165270288Sdumbbellvtbuf_htw(const struct vt_buf *vb, int row) 166258090Sray{ 167258090Sray 168258090Sray /* 169258090Sray * total 1000 rows. 170258090Sray * History offset roffset winrow 171258090Sray * 205 200 ((205 - 200 + 1000) % 1000) = 5 172258090Sray * 90 990 ((90 - 990 + 1000) % 1000) = 100 173258090Sray */ 174258090Sray return ((row - vb->vb_roffset + vb->vb_history_size) % 175258090Sray vb->vb_history_size); 176258090Sray} 177258090Sray 178321199Semaste/* Translate current view row number to history row. */ 179321199Semastestatic int 180321199Semastevtbuf_wth(const struct vt_buf *vb, int row) 181321199Semaste{ 182321199Semaste 183321199Semaste return ((vb->vb_roffset + row) % vb->vb_history_size); 184321199Semaste} 185321199Semaste 186321199Semaste/* 187321199Semaste * Test if an index in a circular buffer is within a range. 188321199Semaste * 189321199Semaste * begin - start index 190321199Semaste * end - end index 191321199Semaste * test - test index 192321199Semaste * sz - size of circular buffer when it turns over 193321199Semaste */ 194321199Semastestatic int 195321199Semastevtbuf_in_this_range(int begin, int test, int end, int sz) 196321199Semaste{ 197321199Semaste 198321199Semaste begin %= sz; 199321199Semaste end %= sz; 200321199Semaste 201321199Semaste /* check for inversion */ 202321199Semaste if (begin > end) 203321199Semaste return (test >= begin || test < end); 204321199Semaste else 205321199Semaste return (test >= begin && test < end); 206321199Semaste} 207321199Semaste#endif 208321199Semaste 209257971Srayint 210270288Sdumbbellvtbuf_iscursor(const struct vt_buf *vb, int row, int col) 211257971Sray{ 212321199Semaste#ifndef SC_NO_CUTPASTE 213321199Semaste int sc, sr, sz, ec, er, tmp; 214321199Semaste#endif 215258090Sray 216258090Sray if ((vb->vb_flags & (VBF_CURSOR|VBF_SCROLL)) == VBF_CURSOR && 217258090Sray (vb->vb_cursor.tp_row == row) && (vb->vb_cursor.tp_col == col)) 218257971Sray return (1); 219257971Sray 220321199Semaste#ifndef SC_NO_CUTPASTE 221258090Sray /* Mark cut/paste region. */ 222321199Semaste if (vb->vb_mark_start.tp_col == vb->vb_mark_end.tp_col && 223321199Semaste vb->vb_mark_start.tp_row == vb->vb_mark_end.tp_row) 224321199Semaste return (0); 225258090Sray 226258090Sray sc = vb->vb_mark_start.tp_col; 227321199Semaste sr = vb->vb_mark_start.tp_row; 228258090Sray ec = vb->vb_mark_end.tp_col; 229321199Semaste er = vb->vb_mark_end.tp_row; 230258090Sray 231321199Semaste /* 232321199Semaste * Information about if the selection was made bottom-top or 233321199Semaste * top-bottom is lost due to modulo arithmetics and needs to 234321199Semaste * be recovered: 235321199Semaste */ 236321199Semaste sz = vb->vb_history_size; 237321199Semaste tmp = (sz + er - sr) % sz; 238321199Semaste row = vtbuf_wth(vb, row); 239258090Sray 240321199Semaste /* Swap start and end if start > end */ 241321199Semaste if ((2 * tmp) > sz || (tmp == 0 && sc > ec)) { 242258090Sray tmp = sc; sc = ec; ec = tmp; 243258090Sray tmp = sr; sr = er; er = tmp; 244258090Sray } 245258090Sray 246321199Semaste if (vtbuf_in_this_range(POS_INDEX(sc, sr), POS_INDEX(col, row), 247321199Semaste POS_INDEX(ec, er), POS_INDEX(0, sz))) 248257971Sray return (1); 249321199Semaste#endif 250257971Sray 251257971Sray return (0); 252257971Sray} 253257971Sray 254219888Sedstatic inline void 255269780Sdumbbellvtbuf_dirty_locked(struct vt_buf *vb, const term_rect_t *area) 256219888Sed{ 257219888Sed 258219888Sed if (vb->vb_dirtyrect.tr_begin.tp_row > area->tr_begin.tp_row) 259219888Sed vb->vb_dirtyrect.tr_begin.tp_row = area->tr_begin.tp_row; 260219888Sed if (vb->vb_dirtyrect.tr_begin.tp_col > area->tr_begin.tp_col) 261219888Sed vb->vb_dirtyrect.tr_begin.tp_col = area->tr_begin.tp_col; 262219888Sed if (vb->vb_dirtyrect.tr_end.tp_row < area->tr_end.tp_row) 263219888Sed vb->vb_dirtyrect.tr_end.tp_row = area->tr_end.tp_row; 264219888Sed if (vb->vb_dirtyrect.tr_end.tp_col < area->tr_end.tp_col) 265219888Sed vb->vb_dirtyrect.tr_end.tp_col = area->tr_end.tp_col; 266269780Sdumbbell} 267269780Sdumbbell 268270342Sdumbbellvoid 269269780Sdumbbellvtbuf_dirty(struct vt_buf *vb, const term_rect_t *area) 270269780Sdumbbell{ 271269780Sdumbbell 272269780Sdumbbell VTBUF_LOCK(vb); 273269780Sdumbbell vtbuf_dirty_locked(vb, area); 274219888Sed VTBUF_UNLOCK(vb); 275219888Sed} 276219888Sed 277219888Sedstatic inline void 278269780Sdumbbellvtbuf_dirty_cell_locked(struct vt_buf *vb, const term_pos_t *p) 279219888Sed{ 280219888Sed term_rect_t area; 281219888Sed 282219888Sed area.tr_begin = *p; 283219888Sed area.tr_end.tp_row = p->tp_row + 1; 284219888Sed area.tr_end.tp_col = p->tp_col + 1; 285269780Sdumbbell vtbuf_dirty_locked(vb, &area); 286219888Sed} 287219888Sed 288219888Sedstatic void 289219888Sedvtbuf_make_undirty(struct vt_buf *vb) 290219888Sed{ 291219888Sed 292256145Sray vb->vb_dirtyrect.tr_begin = vb->vb_scr_size; 293219888Sed vb->vb_dirtyrect.tr_end.tp_row = vb->vb_dirtyrect.tr_end.tp_col = 0; 294219888Sed} 295219888Sed 296219888Sedvoid 297271868Sdumbbellvtbuf_undirty(struct vt_buf *vb, term_rect_t *r) 298219888Sed{ 299219888Sed 300219888Sed VTBUF_LOCK(vb); 301219888Sed *r = vb->vb_dirtyrect; 302219888Sed vtbuf_make_undirty(vb); 303219888Sed VTBUF_UNLOCK(vb); 304219888Sed} 305219888Sed 306219888Sedvoid 307219888Sedvtbuf_copy(struct vt_buf *vb, const term_rect_t *r, const term_pos_t *p2) 308219888Sed{ 309219888Sed const term_pos_t *p1 = &r->tr_begin; 310219888Sed term_rect_t area; 311219888Sed unsigned int rows, cols; 312256145Sray int pr, rdiff; 313219888Sed 314256145Sray KASSERT(r->tr_begin.tp_row < vb->vb_scr_size.tp_row, 315256145Sray ("vtbuf_copy begin.tp_row %d must be less than screen width %d", 316256145Sray r->tr_begin.tp_row, vb->vb_scr_size.tp_row)); 317256145Sray KASSERT(r->tr_begin.tp_col < vb->vb_scr_size.tp_col, 318256145Sray ("vtbuf_copy begin.tp_col %d must be less than screen height %d", 319256145Sray r->tr_begin.tp_col, vb->vb_scr_size.tp_col)); 320256145Sray 321256145Sray KASSERT(r->tr_end.tp_row <= vb->vb_scr_size.tp_row, 322256145Sray ("vtbuf_copy end.tp_row %d must be less than screen width %d", 323256145Sray r->tr_end.tp_row, vb->vb_scr_size.tp_row)); 324256145Sray KASSERT(r->tr_end.tp_col <= vb->vb_scr_size.tp_col, 325256145Sray ("vtbuf_copy end.tp_col %d must be less than screen height %d", 326256145Sray r->tr_end.tp_col, vb->vb_scr_size.tp_col)); 327256145Sray 328256145Sray KASSERT(p2->tp_row < vb->vb_scr_size.tp_row, 329256145Sray ("vtbuf_copy tp_row %d must be less than screen width %d", 330256145Sray p2->tp_row, vb->vb_scr_size.tp_row)); 331256145Sray KASSERT(p2->tp_col < vb->vb_scr_size.tp_col, 332256145Sray ("vtbuf_copy tp_col %d must be less than screen height %d", 333256145Sray p2->tp_col, vb->vb_scr_size.tp_col)); 334256145Sray 335219888Sed rows = r->tr_end.tp_row - r->tr_begin.tp_row; 336256145Sray rdiff = r->tr_begin.tp_row - p2->tp_row; 337219888Sed cols = r->tr_end.tp_col - r->tr_begin.tp_col; 338256145Sray if (r->tr_begin.tp_row > p2->tp_row && r->tr_begin.tp_col == 0 && 339256145Sray r->tr_end.tp_col == vb->vb_scr_size.tp_col && /* Full row. */ 340256145Sray (rows + rdiff) == vb->vb_scr_size.tp_row && /* Whole screen. */ 341271382Sray rdiff > 0) { /* Only forward direction. Do not eat history. */ 342256145Sray vthistory_addlines(vb, rdiff); 343256145Sray } else if (p2->tp_row < p1->tp_row) { 344256145Sray /* Handle overlapping copies of line segments. */ 345219888Sed /* Move data up. */ 346219888Sed for (pr = 0; pr < rows; pr++) 347219888Sed memmove( 348219888Sed &VTBUF_FIELD(vb, p2->tp_row + pr, p2->tp_col), 349219888Sed &VTBUF_FIELD(vb, p1->tp_row + pr, p1->tp_col), 350219888Sed cols * sizeof(term_char_t)); 351219888Sed } else { 352219888Sed /* Move data down. */ 353219888Sed for (pr = rows - 1; pr >= 0; pr--) 354219888Sed memmove( 355219888Sed &VTBUF_FIELD(vb, p2->tp_row + pr, p2->tp_col), 356219888Sed &VTBUF_FIELD(vb, p1->tp_row + pr, p1->tp_col), 357219888Sed cols * sizeof(term_char_t)); 358219888Sed } 359219888Sed 360219888Sed area.tr_begin = *p2; 361256145Sray area.tr_end.tp_row = MIN(p2->tp_row + rows, vb->vb_scr_size.tp_row); 362256145Sray area.tr_end.tp_col = MIN(p2->tp_col + cols, vb->vb_scr_size.tp_col); 363219888Sed vtbuf_dirty(vb, &area); 364219888Sed} 365219888Sed 366256145Sraystatic void 367219888Sedvtbuf_fill(struct vt_buf *vb, const term_rect_t *r, term_char_t c) 368219888Sed{ 369219888Sed unsigned int pr, pc; 370256145Sray term_char_t *row; 371219888Sed 372256145Sray for (pr = r->tr_begin.tp_row; pr < r->tr_end.tp_row; pr++) { 373256145Sray row = vb->vb_rows[(vb->vb_curroffset + pr) % 374256145Sray VTBUF_MAX_HEIGHT(vb)]; 375256145Sray for (pc = r->tr_begin.tp_col; pc < r->tr_end.tp_col; pc++) { 376256145Sray row[pc] = c; 377256145Sray } 378256145Sray } 379256145Sray} 380219888Sed 381256145Srayvoid 382256145Srayvtbuf_fill_locked(struct vt_buf *vb, const term_rect_t *r, term_char_t c) 383256145Sray{ 384256145Sray KASSERT(r->tr_begin.tp_row < vb->vb_scr_size.tp_row, 385266856Semaste ("vtbuf_fill_locked begin.tp_row %d must be < screen height %d", 386256145Sray r->tr_begin.tp_row, vb->vb_scr_size.tp_row)); 387256145Sray KASSERT(r->tr_begin.tp_col < vb->vb_scr_size.tp_col, 388266856Semaste ("vtbuf_fill_locked begin.tp_col %d must be < screen width %d", 389256145Sray r->tr_begin.tp_col, vb->vb_scr_size.tp_col)); 390256145Sray 391256145Sray KASSERT(r->tr_end.tp_row <= vb->vb_scr_size.tp_row, 392266856Semaste ("vtbuf_fill_locked end.tp_row %d must be <= screen height %d", 393256145Sray r->tr_end.tp_row, vb->vb_scr_size.tp_row)); 394256145Sray KASSERT(r->tr_end.tp_col <= vb->vb_scr_size.tp_col, 395266856Semaste ("vtbuf_fill_locked end.tp_col %d must be <= screen width %d", 396256145Sray r->tr_end.tp_col, vb->vb_scr_size.tp_col)); 397256145Sray 398256145Sray VTBUF_LOCK(vb); 399256145Sray vtbuf_fill(vb, r, c); 400269780Sdumbbell vtbuf_dirty_locked(vb, r); 401256145Sray VTBUF_UNLOCK(vb); 402219888Sed} 403219888Sed 404256145Sraystatic void 405256145Srayvtbuf_init_rows(struct vt_buf *vb) 406256145Sray{ 407256145Sray int r; 408256145Sray 409256145Sray vb->vb_history_size = MAX(vb->vb_history_size, vb->vb_scr_size.tp_row); 410256145Sray 411256145Sray for (r = 0; r < vb->vb_history_size; r++) 412267978Smarius vb->vb_rows[r] = &vb->vb_buffer[r * vb->vb_scr_size.tp_col]; 413256145Sray} 414256145Sray 415219888Sedvoid 416219888Sedvtbuf_init_early(struct vt_buf *vb) 417219888Sed{ 418267978Smarius term_rect_t rect; 419219888Sed 420219888Sed vb->vb_flags |= VBF_CURSOR; 421256145Sray vb->vb_roffset = 0; 422256145Sray vb->vb_curroffset = 0; 423258090Sray vb->vb_mark_start.tp_row = 0; 424258090Sray vb->vb_mark_start.tp_col = 0; 425258090Sray vb->vb_mark_end.tp_row = 0; 426258090Sray vb->vb_mark_end.tp_col = 0; 427256145Sray 428256145Sray vtbuf_init_rows(vb); 429267978Smarius rect.tr_begin.tp_row = rect.tr_begin.tp_col = 0; 430270667Sdumbbell rect.tr_end.tp_col = vb->vb_scr_size.tp_col; 431270667Sdumbbell rect.tr_end.tp_row = vb->vb_history_size; 432270667Sdumbbell vtbuf_fill(vb, &rect, VTBUF_SPACE_CHAR(TERMINAL_NORM_ATTR)); 433219888Sed vtbuf_make_undirty(vb); 434256145Sray if ((vb->vb_flags & VBF_MTX_INIT) == 0) { 435256145Sray mtx_init(&vb->vb_lock, "vtbuf", NULL, MTX_SPIN); 436256145Sray vb->vb_flags |= VBF_MTX_INIT; 437256145Sray } 438219888Sed} 439219888Sed 440219888Sedvoid 441219888Sedvtbuf_init(struct vt_buf *vb, const term_pos_t *p) 442219888Sed{ 443256145Sray int sz; 444219888Sed 445256145Sray vb->vb_scr_size = *p; 446256145Sray vb->vb_history_size = VBF_DEFAULT_HISTORY_SIZE; 447256145Sray 448256145Sray if ((vb->vb_flags & VBF_STATIC) == 0) { 449256145Sray sz = vb->vb_history_size * p->tp_col * sizeof(term_char_t); 450256894Sray vb->vb_buffer = malloc(sz, M_VTBUF, M_WAITOK | M_ZERO); 451256145Sray 452256145Sray sz = vb->vb_history_size * sizeof(term_char_t *); 453256894Sray vb->vb_rows = malloc(sz, M_VTBUF, M_WAITOK | M_ZERO); 454256145Sray } 455256145Sray 456219888Sed vtbuf_init_early(vb); 457219888Sed} 458219888Sed 459219888Sedvoid 460330641Seadlervtbuf_sethistory_size(struct vt_buf *vb, unsigned int size) 461219888Sed{ 462256145Sray term_pos_t p; 463219888Sed 464256145Sray /* With same size */ 465256145Sray p.tp_row = vb->vb_scr_size.tp_row; 466256145Sray p.tp_col = vb->vb_scr_size.tp_col; 467256145Sray vtbuf_grow(vb, &p, size); 468256145Sray} 469256145Sray 470256145Srayvoid 471270785Sdumbbellvtbuf_grow(struct vt_buf *vb, const term_pos_t *p, unsigned int history_size) 472256145Sray{ 473271871Sdumbbell term_char_t *old, *new, **rows, **oldrows, **copyrows, *row, *oldrow; 474330641Seadler unsigned int w, h, c, r, old_history_size; 475330641Seadler size_t bufsize, rowssize; 476330641Seadler int history_full; 477256145Sray 478256145Sray history_size = MAX(history_size, p->tp_row); 479256145Sray 480271871Sdumbbell /* Allocate new buffer. */ 481271871Sdumbbell bufsize = history_size * p->tp_col * sizeof(term_char_t); 482271871Sdumbbell new = malloc(bufsize, M_VTBUF, M_WAITOK | M_ZERO); 483271871Sdumbbell rowssize = history_size * sizeof(term_pos_t *); 484271871Sdumbbell rows = malloc(rowssize, M_VTBUF, M_WAITOK | M_ZERO); 485219888Sed 486271871Sdumbbell /* Toggle it. */ 487271871Sdumbbell VTBUF_LOCK(vb); 488271871Sdumbbell old = vb->vb_flags & VBF_STATIC ? NULL : vb->vb_buffer; 489271871Sdumbbell oldrows = vb->vb_flags & VBF_STATIC ? NULL : vb->vb_rows; 490271871Sdumbbell copyrows = vb->vb_rows; 491256145Sray 492271871Sdumbbell w = vb->vb_scr_size.tp_col; 493271871Sdumbbell h = vb->vb_scr_size.tp_row; 494271871Sdumbbell old_history_size = vb->vb_history_size; 495330641Seadler history_full = vb->vb_flags & VBF_HISTORY_FULL || 496330641Seadler vb->vb_curroffset + h >= history_size; 497256145Sray 498271871Sdumbbell vb->vb_history_size = history_size; 499271871Sdumbbell vb->vb_buffer = new; 500271871Sdumbbell vb->vb_rows = rows; 501271871Sdumbbell vb->vb_flags &= ~VBF_STATIC; 502271871Sdumbbell vb->vb_scr_size = *p; 503271871Sdumbbell vtbuf_init_rows(vb); 504271871Sdumbbell 505330641Seadler /* 506330641Seadler * Copy rows to the new buffer. The first row in the history 507330641Seadler * is back to index 0, ie. the new buffer doesn't cycle. 508330641Seadler */ 509271871Sdumbbell if (history_size > old_history_size) { 510271871Sdumbbell for (r = 0; r < old_history_size; r ++) { 511271871Sdumbbell row = rows[r]; 512271871Sdumbbell 513271871Sdumbbell /* Compute the corresponding row in the old buffer. */ 514330641Seadler if (history_full) 515271871Sdumbbell /* 516271871Sdumbbell * The buffer is full, the "top" row is 517271871Sdumbbell * the one just after the viewable area 518271871Sdumbbell * (curroffset + viewable height) in the 519271871Sdumbbell * cycling buffer. The corresponding row 520271871Sdumbbell * is computed from this top row. 521271871Sdumbbell */ 522271871Sdumbbell oldrow = copyrows[ 523271871Sdumbbell (vb->vb_curroffset + h + r) % 524271871Sdumbbell old_history_size]; 525271871Sdumbbell else 526271871Sdumbbell /* 527271871Sdumbbell * The buffer is not full, therefore, 528271871Sdumbbell * we didn't cycle already. The 529271871Sdumbbell * corresponding rows are the same in 530271871Sdumbbell * both buffers. 531271871Sdumbbell */ 532271871Sdumbbell oldrow = copyrows[r]; 533271871Sdumbbell 534271871Sdumbbell memmove(row, oldrow, 535271871Sdumbbell MIN(p->tp_col, w) * sizeof(term_char_t)); 536271871Sdumbbell 537271871Sdumbbell /* 538271871Sdumbbell * XXX VTBUF_SPACE_CHAR(TERMINAL_NORM_ATTR) will 539271871Sdumbbell * extended lines of kernel text using the wrong 540271871Sdumbbell * background color. 541271871Sdumbbell */ 542271871Sdumbbell for (c = MIN(p->tp_col, w); c < p->tp_col; c++) { 543271871Sdumbbell row[c] = VTBUF_SPACE_CHAR(TERMINAL_NORM_ATTR); 544271871Sdumbbell } 545271871Sdumbbell } 546271871Sdumbbell 547271871Sdumbbell /* Fill remaining rows. */ 548330641Seadler for (r = old_history_size; r < history_size; r++) { 549330641Seadler row = rows[r]; 550330641Seadler for (c = MIN(p->tp_col, w); c < p->tp_col; c++) { 551330641Seadler row[c] = VTBUF_SPACE_CHAR(TERMINAL_NORM_ATTR); 552330641Seadler } 553330641Seadler } 554271871Sdumbbell 555271871Sdumbbell vb->vb_flags &= ~VBF_HISTORY_FULL; 556330641Seadler 557330641Seadler /* 558330641Seadler * If the screen is already filled (there are non-visible lines 559330641Seadler * above the current viewable area), adjust curroffset to the 560330641Seadler * new viewable area. 561330641Seadler * 562330641Seadler * If the old buffer was full, set curroffset to the 563330641Seadler * <h>th most recent line of history in the new, non-cycled 564330641Seadler * buffer. Otherwise, it didn't cycle, so the old curroffset 565330641Seadler * is the same in the new buffer. 566330641Seadler */ 567330641Seadler if (history_full) 568330641Seadler vb->vb_curroffset = old_history_size - h; 569271871Sdumbbell } else { 570271871Sdumbbell /* 571271871Sdumbbell * (old_history_size - history_size) lines of history are 572271871Sdumbbell * dropped. 573271871Sdumbbell */ 574256145Sray for (r = 0; r < history_size; r ++) { 575271871Sdumbbell row = rows[r]; 576271871Sdumbbell 577267978Smarius /* 578271871Sdumbbell * Compute the corresponding row in the old buffer. 579271871Sdumbbell * 580271871Sdumbbell * See the equivalent if{} block above for an 581271871Sdumbbell * explanation. 582271871Sdumbbell */ 583330641Seadler if (history_full) 584271871Sdumbbell oldrow = copyrows[ 585271871Sdumbbell (vb->vb_curroffset + h + r + 586271871Sdumbbell (old_history_size - history_size)) % 587271871Sdumbbell old_history_size]; 588271871Sdumbbell else 589330641Seadler oldrow = copyrows[r]; 590271871Sdumbbell 591271871Sdumbbell memmove(row, oldrow, 592271871Sdumbbell MIN(p->tp_col, w) * sizeof(term_char_t)); 593271871Sdumbbell 594271871Sdumbbell /* 595267978Smarius * XXX VTBUF_SPACE_CHAR(TERMINAL_NORM_ATTR) will 596267978Smarius * extended lines of kernel text using the wrong 597267978Smarius * background color. 598267978Smarius */ 599271871Sdumbbell for (c = MIN(p->tp_col, w); c < p->tp_col; c++) { 600271871Sdumbbell row[c] = VTBUF_SPACE_CHAR(TERMINAL_NORM_ATTR); 601256145Sray } 602256145Sray } 603271871Sdumbbell 604330641Seadler if (history_full) { 605330641Seadler vb->vb_curroffset = history_size - h; 606271871Sdumbbell vb->vb_flags |= VBF_HISTORY_FULL; 607330641Seadler } 608219888Sed } 609271871Sdumbbell 610330641Seadler vb->vb_roffset = vb->vb_curroffset; 611271871Sdumbbell 612273932Sdumbbell /* Adjust cursor position. */ 613273932Sdumbbell if (vb->vb_cursor.tp_col > p->tp_col - 1) 614273932Sdumbbell /* 615273932Sdumbbell * Move cursor to the last column, in case its previous 616273932Sdumbbell * position is outside of the new screen area. 617273932Sdumbbell */ 618273932Sdumbbell vb->vb_cursor.tp_col = p->tp_col - 1; 619273932Sdumbbell 620273932Sdumbbell if (vb->vb_curroffset > 0 || vb->vb_cursor.tp_row > p->tp_row - 1) 621273932Sdumbbell /* Move cursor to the last line on the screen. */ 622273932Sdumbbell vb->vb_cursor.tp_row = p->tp_row - 1; 623273932Sdumbbell 624271871Sdumbbell VTBUF_UNLOCK(vb); 625271871Sdumbbell 626271871Sdumbbell /* Deallocate old buffer. */ 627271871Sdumbbell free(old, M_VTBUF); 628271871Sdumbbell free(oldrows, M_VTBUF); 629219888Sed} 630219888Sed 631219888Sedvoid 632219888Sedvtbuf_putchar(struct vt_buf *vb, const term_pos_t *p, term_char_t c) 633219888Sed{ 634256145Sray term_char_t *row; 635219888Sed 636256145Sray KASSERT(p->tp_row < vb->vb_scr_size.tp_row, 637256145Sray ("vtbuf_putchar tp_row %d must be less than screen width %d", 638256145Sray p->tp_row, vb->vb_scr_size.tp_row)); 639256145Sray KASSERT(p->tp_col < vb->vb_scr_size.tp_col, 640256145Sray ("vtbuf_putchar tp_col %d must be less than screen height %d", 641256145Sray p->tp_col, vb->vb_scr_size.tp_col)); 642256145Sray 643256145Sray row = vb->vb_rows[(vb->vb_curroffset + p->tp_row) % 644256145Sray VTBUF_MAX_HEIGHT(vb)]; 645256145Sray if (row[p->tp_col] != c) { 646256145Sray VTBUF_LOCK(vb); 647256145Sray row[p->tp_col] = c; 648269780Sdumbbell vtbuf_dirty_cell_locked(vb, p); 649256145Sray VTBUF_UNLOCK(vb); 650219888Sed } 651219888Sed} 652219888Sed 653219888Sedvoid 654219888Sedvtbuf_cursor_position(struct vt_buf *vb, const term_pos_t *p) 655219888Sed{ 656219888Sed 657219888Sed if (vb->vb_flags & VBF_CURSOR) { 658269780Sdumbbell VTBUF_LOCK(vb); 659269780Sdumbbell vtbuf_dirty_cell_locked(vb, &vb->vb_cursor); 660219888Sed vb->vb_cursor = *p; 661269780Sdumbbell vtbuf_dirty_cell_locked(vb, &vb->vb_cursor); 662269780Sdumbbell VTBUF_UNLOCK(vb); 663219888Sed } else { 664219888Sed vb->vb_cursor = *p; 665219888Sed } 666219888Sed} 667219888Sed 668259129Sray#ifndef SC_NO_CUTPASTE 669258090Sraystatic void 670258090Srayvtbuf_flush_mark(struct vt_buf *vb) 671258090Sray{ 672258090Sray term_rect_t area; 673258090Sray int s, e; 674258090Sray 675258090Sray /* Notify renderer to update marked region. */ 676321199Semaste if ((vb->vb_mark_start.tp_col != vb->vb_mark_end.tp_col) || 677321199Semaste (vb->vb_mark_start.tp_row != vb->vb_mark_end.tp_row)) { 678258090Sray 679258090Sray s = vtbuf_htw(vb, vb->vb_mark_start.tp_row); 680258090Sray e = vtbuf_htw(vb, vb->vb_mark_end.tp_row); 681258090Sray 682258090Sray area.tr_begin.tp_col = 0; 683258090Sray area.tr_begin.tp_row = MIN(s, e); 684258090Sray 685258090Sray area.tr_end.tp_col = vb->vb_scr_size.tp_col; 686258090Sray area.tr_end.tp_row = MAX(s, e) + 1; 687258090Sray 688258090Sray vtbuf_dirty(vb, &area); 689258090Sray } 690258090Sray} 691258090Sray 692258090Srayint 693258090Srayvtbuf_get_marked_len(struct vt_buf *vb) 694258090Sray{ 695258090Sray int ei, si, sz; 696258090Sray term_pos_t s, e; 697258090Sray 698258090Sray /* Swap according to window coordinates. */ 699258134Sray if (POS_INDEX(vtbuf_htw(vb, vb->vb_mark_start.tp_row), 700258134Sray vb->vb_mark_start.tp_col) > 701258134Sray POS_INDEX(vtbuf_htw(vb, vb->vb_mark_end.tp_row), 702258134Sray vb->vb_mark_end.tp_col)) { 703258090Sray POS_COPY(e, vb->vb_mark_start); 704258090Sray POS_COPY(s, vb->vb_mark_end); 705258090Sray } else { 706258090Sray POS_COPY(s, vb->vb_mark_start); 707258090Sray POS_COPY(e, vb->vb_mark_end); 708258090Sray } 709258090Sray 710258090Sray si = s.tp_row * vb->vb_scr_size.tp_col + s.tp_col; 711258090Sray ei = e.tp_row * vb->vb_scr_size.tp_col + e.tp_col; 712258090Sray 713258090Sray /* Number symbols and number of rows to inject \n */ 714271466Sray sz = ei - si + ((e.tp_row - s.tp_row) * 2); 715258090Sray 716258090Sray return (sz * sizeof(term_char_t)); 717258090Sray} 718258090Sray 719257971Srayvoid 720258090Srayvtbuf_extract_marked(struct vt_buf *vb, term_char_t *buf, int sz) 721258090Sray{ 722258090Sray int i, r, c, cs, ce; 723258090Sray term_pos_t s, e; 724258090Sray 725258090Sray /* Swap according to window coordinates. */ 726258134Sray if (POS_INDEX(vtbuf_htw(vb, vb->vb_mark_start.tp_row), 727258134Sray vb->vb_mark_start.tp_col) > 728258134Sray POS_INDEX(vtbuf_htw(vb, vb->vb_mark_end.tp_row), 729258134Sray vb->vb_mark_end.tp_col)) { 730258090Sray POS_COPY(e, vb->vb_mark_start); 731258090Sray POS_COPY(s, vb->vb_mark_end); 732258090Sray } else { 733258090Sray POS_COPY(s, vb->vb_mark_start); 734258090Sray POS_COPY(e, vb->vb_mark_end); 735258090Sray } 736258090Sray 737258090Sray i = 0; 738258090Sray for (r = s.tp_row; r <= e.tp_row; r ++) { 739258090Sray cs = (r == s.tp_row)?s.tp_col:0; 740258090Sray ce = (r == e.tp_row)?e.tp_col:vb->vb_scr_size.tp_col; 741258090Sray for (c = cs; c < ce; c ++) { 742258090Sray buf[i++] = vb->vb_rows[r][c]; 743258090Sray } 744258093Sray /* Add new line for all rows, but not for last one. */ 745258093Sray if (r != e.tp_row) { 746258093Sray buf[i++] = '\r'; 747258093Sray buf[i++] = '\n'; 748258093Sray } 749258090Sray } 750258090Sray} 751258090Sray 752258090Srayint 753257971Srayvtbuf_set_mark(struct vt_buf *vb, int type, int col, int row) 754257971Sray{ 755258136Sray term_char_t *r; 756258136Sray int i; 757257971Sray 758257971Sray switch (type) { 759258130Sray case VTB_MARK_END: /* B1 UP */ 760258130Sray if (vb->vb_mark_last != VTB_MARK_MOVE) 761258130Sray return (0); 762258130Sray /* FALLTHROUGH */ 763258130Sray case VTB_MARK_MOVE: 764257971Sray case VTB_MARK_EXTEND: 765258090Sray vtbuf_flush_mark(vb); /* Clean old mark. */ 766257971Sray vb->vb_mark_end.tp_col = col; 767258090Sray vb->vb_mark_end.tp_row = vtbuf_wth(vb, row); 768257971Sray break; 769257971Sray case VTB_MARK_START: 770258090Sray vtbuf_flush_mark(vb); /* Clean old mark. */ 771257971Sray vb->vb_mark_start.tp_col = col; 772258090Sray vb->vb_mark_start.tp_row = vtbuf_wth(vb, row); 773257971Sray /* Start again, so clear end point. */ 774258090Sray vb->vb_mark_end.tp_col = col; 775258090Sray vb->vb_mark_end.tp_row = vtbuf_wth(vb, row); 776257971Sray break; 777257971Sray case VTB_MARK_WORD: 778258090Sray vtbuf_flush_mark(vb); /* Clean old mark. */ 779258090Sray vb->vb_mark_start.tp_row = vb->vb_mark_end.tp_row = 780258090Sray vtbuf_wth(vb, row); 781258136Sray r = vb->vb_rows[vb->vb_mark_start.tp_row]; 782258136Sray for (i = col; i >= 0; i --) { 783258136Sray if (TCHAR_CHARACTER(r[i]) == ' ') { 784258136Sray vb->vb_mark_start.tp_col = i + 1; 785258136Sray break; 786258136Sray } 787258136Sray } 788258136Sray for (i = col; i < vb->vb_scr_size.tp_col; i ++) { 789258136Sray if (TCHAR_CHARACTER(r[i]) == ' ') { 790258136Sray vb->vb_mark_end.tp_col = i; 791258136Sray break; 792258136Sray } 793258136Sray } 794258136Sray if (vb->vb_mark_start.tp_col > vb->vb_mark_end.tp_col) 795258136Sray vb->vb_mark_start.tp_col = vb->vb_mark_end.tp_col; 796257971Sray break; 797257971Sray case VTB_MARK_ROW: 798258090Sray vtbuf_flush_mark(vb); /* Clean old mark. */ 799257971Sray vb->vb_mark_start.tp_col = 0; 800257971Sray vb->vb_mark_end.tp_col = vb->vb_scr_size.tp_col; 801258090Sray vb->vb_mark_start.tp_row = vb->vb_mark_end.tp_row = 802258090Sray vtbuf_wth(vb, row); 803257971Sray break; 804258090Sray case VTB_MARK_NONE: 805258130Sray vb->vb_mark_last = type; 806258130Sray /* FALLTHROUGH */ 807258090Sray default: 808258090Sray /* panic? */ 809258090Sray return (0); 810257971Sray } 811258111Sray 812258130Sray vb->vb_mark_last = type; 813258111Sray /* Draw new marked region. */ 814258111Sray vtbuf_flush_mark(vb); 815258111Sray return (1); 816257971Sray} 817259129Sray#endif 818257971Sray 819257971Srayvoid 820219888Sedvtbuf_cursor_visibility(struct vt_buf *vb, int yes) 821219888Sed{ 822219888Sed int oflags, nflags; 823219888Sed 824219888Sed VTBUF_LOCK(vb); 825219888Sed oflags = vb->vb_flags; 826219888Sed if (yes) 827219888Sed vb->vb_flags |= VBF_CURSOR; 828219888Sed else 829219888Sed vb->vb_flags &= ~VBF_CURSOR; 830219888Sed nflags = vb->vb_flags; 831219888Sed 832219888Sed if (oflags != nflags) 833269780Sdumbbell vtbuf_dirty_cell_locked(vb, &vb->vb_cursor); 834269780Sdumbbell VTBUF_UNLOCK(vb); 835219888Sed} 836258090Sray 837258090Srayvoid 838258090Srayvtbuf_scroll_mode(struct vt_buf *vb, int yes) 839258090Sray{ 840258090Sray int oflags, nflags; 841258090Sray 842258090Sray VTBUF_LOCK(vb); 843258090Sray oflags = vb->vb_flags; 844258090Sray if (yes) 845258090Sray vb->vb_flags |= VBF_SCROLL; 846258090Sray else 847258090Sray vb->vb_flags &= ~VBF_SCROLL; 848258090Sray nflags = vb->vb_flags; 849258090Sray 850258090Sray if (oflags != nflags) 851269780Sdumbbell vtbuf_dirty_cell_locked(vb, &vb->vb_cursor); 852269780Sdumbbell VTBUF_UNLOCK(vb); 853258090Sray} 854258090Sray 855