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/10/sys/dev/vt/vt_buf.c 321200 2017-07-19 13:32:08Z emaste $"); 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> 41268037Smarius#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 57321200Semaste#ifndef SC_NO_CUTPASTE 58321200Semastestatic int vtbuf_htw(const struct vt_buf *vb, int row); 59321200Semastestatic int vtbuf_wth(const struct vt_buf *vb, int row); 60321200Semastestatic int vtbuf_in_this_range(int begin, int test, int end, int sz); 61321200Semaste#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 85271973Sdumbbell /* "top" may be a negative integer. */ 86271973Sdumbbell bottom = vb->vb_curroffset; 87271973Sdumbbell top = (vb->vb_flags & VBF_HISTORY_FULL) ? 88271973Sdumbbell bottom + vb->vb_scr_size.tp_row - vb->vb_history_size : 89271973Sdumbbell 0; 90271973Sdumbbell 91271973Sdumbbell roffset = 0; /* Make gcc happy. */ 92256145Sray switch (whence) { 93256145Sray case VHS_SET: 94271973Sdumbbell if (offset < 0) 95271973Sdumbbell offset = 0; 96271973Sdumbbell roffset = top + offset; 97256145Sray break; 98256145Sray case VHS_CUR: 99271973Sdumbbell /* 100271973Sdumbbell * Operate on copy of offset value, since it temporary 101271973Sdumbbell * can be bigger than amount of rows in buffer. 102271973Sdumbbell */ 103271973Sdumbbell roffset = vb->vb_roffset; 104271973Sdumbbell if (roffset >= bottom + vb->vb_scr_size.tp_row) 105271973Sdumbbell roffset -= vb->vb_history_size; 106271973Sdumbbell 107256145Sray roffset += offset; 108271973Sdumbbell roffset = MAX(roffset, top); 109271973Sdumbbell roffset = MIN(roffset, bottom); 110271973Sdumbbell 111271973Sdumbbell if (roffset < 0) 112271973Sdumbbell roffset = vb->vb_history_size + roffset; 113271973Sdumbbell 114256145Sray break; 115256145Sray case VHS_END: 116256145Sray /* Go to current offset. */ 117271973Sdumbbell roffset = vb->vb_curroffset; 118256145Sray break; 119256145Sray } 120256145Sray 121271973Sdumbbell diff = vb->vb_roffset != roffset; 122271973Sdumbbell vb->vb_roffset = roffset; 123256145Sray 124271973Sdumbbell return (diff); 125256145Sray} 126256145Sray 127256145Srayvoid 128256145Srayvthistory_addlines(struct vt_buf *vb, int offset) 129256145Sray{ 130321200Semaste#ifndef SC_NO_CUTPASTE 131321200Semaste int cur, sz; 132321200Semaste#endif 133256145Sray 134256145Sray vb->vb_curroffset += offset; 135256145Sray if (vb->vb_curroffset < 0) 136256145Sray vb->vb_curroffset = 0; 137271973Sdumbbell if (vb->vb_curroffset + vb->vb_scr_size.tp_row >= vb->vb_history_size) 138271973Sdumbbell vb->vb_flags |= VBF_HISTORY_FULL; 139256145Sray vb->vb_curroffset %= vb->vb_history_size; 140256145Sray if ((vb->vb_flags & VBF_SCROLL) == 0) { 141256145Sray vb->vb_roffset = vb->vb_curroffset; 142256145Sray } 143321200Semaste 144321200Semaste#ifndef SC_NO_CUTPASTE 145321200Semaste sz = vb->vb_history_size; 146321200Semaste cur = vb->vb_roffset + vb->vb_scr_size.tp_row + sz - 1; 147321200Semaste if (vtbuf_in_this_range(cur, vb->vb_mark_start.tp_row, cur + offset, sz) || 148321200Semaste vtbuf_in_this_range(cur, vb->vb_mark_end.tp_row, cur + offset, sz)) { 149321200Semaste /* clear screen selection */ 150321200Semaste vb->vb_mark_start.tp_row = vb->vb_mark_end.tp_row; 151321200Semaste vb->vb_mark_start.tp_col = vb->vb_mark_end.tp_col; 152321200Semaste } 153321200Semaste#endif 154256145Sray} 155256145Sray 156256145Srayvoid 157256145Srayvthistory_getpos(const struct vt_buf *vb, unsigned int *offset) 158256145Sray{ 159256145Sray 160256145Sray *offset = vb->vb_roffset; 161256145Sray} 162256145Sray 163262861Sjhb#ifndef SC_NO_CUTPASTE /* Only mouse support use it now. */ 164258090Sray/* Translate history row to current view row number. */ 165258090Sraystatic int 166271024Semastevtbuf_htw(const struct vt_buf *vb, int row) 167258090Sray{ 168258090Sray 169258090Sray /* 170258090Sray * total 1000 rows. 171258090Sray * History offset roffset winrow 172258090Sray * 205 200 ((205 - 200 + 1000) % 1000) = 5 173258090Sray * 90 990 ((90 - 990 + 1000) % 1000) = 100 174258090Sray */ 175258090Sray return ((row - vb->vb_roffset + vb->vb_history_size) % 176258090Sray vb->vb_history_size); 177258090Sray} 178258090Sray 179321200Semaste/* Translate current view row number to history row. */ 180321200Semastestatic int 181321200Semastevtbuf_wth(const struct vt_buf *vb, int row) 182321200Semaste{ 183321200Semaste 184321200Semaste return ((vb->vb_roffset + row) % vb->vb_history_size); 185321200Semaste} 186321200Semaste 187321200Semaste/* 188321200Semaste * Test if an index in a circular buffer is within a range. 189321200Semaste * 190321200Semaste * begin - start index 191321200Semaste * end - end index 192321200Semaste * test - test index 193321200Semaste * sz - size of circular buffer when it turns over 194321200Semaste */ 195321200Semastestatic int 196321200Semastevtbuf_in_this_range(int begin, int test, int end, int sz) 197321200Semaste{ 198321200Semaste 199321200Semaste begin %= sz; 200321200Semaste end %= sz; 201321200Semaste 202321200Semaste /* check for inversion */ 203321200Semaste if (begin > end) 204321200Semaste return (test >= begin || test < end); 205321200Semaste else 206321200Semaste return (test >= begin && test < end); 207321200Semaste} 208321200Semaste#endif 209321200Semaste 210257971Srayint 211271024Semastevtbuf_iscursor(const struct vt_buf *vb, int row, int col) 212257971Sray{ 213321200Semaste#ifndef SC_NO_CUTPASTE 214321200Semaste int sc, sr, sz, ec, er, tmp; 215321200Semaste#endif 216258090Sray 217258090Sray if ((vb->vb_flags & (VBF_CURSOR|VBF_SCROLL)) == VBF_CURSOR && 218258090Sray (vb->vb_cursor.tp_row == row) && (vb->vb_cursor.tp_col == col)) 219257971Sray return (1); 220257971Sray 221321200Semaste#ifndef SC_NO_CUTPASTE 222258090Sray /* Mark cut/paste region. */ 223321200Semaste if (vb->vb_mark_start.tp_col == vb->vb_mark_end.tp_col && 224321200Semaste vb->vb_mark_start.tp_row == vb->vb_mark_end.tp_row) 225321200Semaste return (0); 226258090Sray 227258090Sray sc = vb->vb_mark_start.tp_col; 228321200Semaste sr = vb->vb_mark_start.tp_row; 229258090Sray ec = vb->vb_mark_end.tp_col; 230321200Semaste er = vb->vb_mark_end.tp_row; 231258090Sray 232321200Semaste /* 233321200Semaste * Information about if the selection was made bottom-top or 234321200Semaste * top-bottom is lost due to modulo arithmetics and needs to 235321200Semaste * be recovered: 236321200Semaste */ 237321200Semaste sz = vb->vb_history_size; 238321200Semaste tmp = (sz + er - sr) % sz; 239321200Semaste row = vtbuf_wth(vb, row); 240258090Sray 241321200Semaste /* Swap start and end if start > end */ 242321200Semaste if ((2 * tmp) > sz || (tmp == 0 && sc > ec)) { 243258090Sray tmp = sc; sc = ec; ec = tmp; 244258090Sray tmp = sr; sr = er; er = tmp; 245258090Sray } 246258090Sray 247321200Semaste if (vtbuf_in_this_range(POS_INDEX(sc, sr), POS_INDEX(col, row), 248321200Semaste POS_INDEX(ec, er), POS_INDEX(0, sz))) 249257971Sray return (1); 250321200Semaste#endif 251257971Sray 252257971Sray return (0); 253257971Sray} 254257971Sray 255219888Sedstatic inline void 256270182Sdumbbellvtbuf_dirty_locked(struct vt_buf *vb, const term_rect_t *area) 257219888Sed{ 258219888Sed 259219888Sed if (vb->vb_dirtyrect.tr_begin.tp_row > area->tr_begin.tp_row) 260219888Sed vb->vb_dirtyrect.tr_begin.tp_row = area->tr_begin.tp_row; 261219888Sed if (vb->vb_dirtyrect.tr_begin.tp_col > area->tr_begin.tp_col) 262219888Sed vb->vb_dirtyrect.tr_begin.tp_col = area->tr_begin.tp_col; 263219888Sed if (vb->vb_dirtyrect.tr_end.tp_row < area->tr_end.tp_row) 264219888Sed vb->vb_dirtyrect.tr_end.tp_row = area->tr_end.tp_row; 265219888Sed if (vb->vb_dirtyrect.tr_end.tp_col < area->tr_end.tp_col) 266219888Sed vb->vb_dirtyrect.tr_end.tp_col = area->tr_end.tp_col; 267270182Sdumbbell} 268270182Sdumbbell 269271128Semastevoid 270270182Sdumbbellvtbuf_dirty(struct vt_buf *vb, const term_rect_t *area) 271270182Sdumbbell{ 272270182Sdumbbell 273270182Sdumbbell VTBUF_LOCK(vb); 274270182Sdumbbell vtbuf_dirty_locked(vb, area); 275219888Sed VTBUF_UNLOCK(vb); 276219888Sed} 277219888Sed 278219888Sedstatic inline void 279270182Sdumbbellvtbuf_dirty_cell_locked(struct vt_buf *vb, const term_pos_t *p) 280219888Sed{ 281219888Sed term_rect_t area; 282219888Sed 283219888Sed area.tr_begin = *p; 284219888Sed area.tr_end.tp_row = p->tp_row + 1; 285219888Sed area.tr_end.tp_col = p->tp_col + 1; 286270182Sdumbbell vtbuf_dirty_locked(vb, &area); 287219888Sed} 288219888Sed 289219888Sedstatic void 290219888Sedvtbuf_make_undirty(struct vt_buf *vb) 291219888Sed{ 292219888Sed 293256145Sray vb->vb_dirtyrect.tr_begin = vb->vb_scr_size; 294219888Sed vb->vb_dirtyrect.tr_end.tp_row = vb->vb_dirtyrect.tr_end.tp_col = 0; 295219888Sed} 296219888Sed 297219888Sedvoid 298271973Sdumbbellvtbuf_undirty(struct vt_buf *vb, term_rect_t *r) 299219888Sed{ 300219888Sed 301219888Sed VTBUF_LOCK(vb); 302219888Sed *r = vb->vb_dirtyrect; 303219888Sed vtbuf_make_undirty(vb); 304219888Sed VTBUF_UNLOCK(vb); 305219888Sed} 306219888Sed 307219888Sedvoid 308219888Sedvtbuf_copy(struct vt_buf *vb, const term_rect_t *r, const term_pos_t *p2) 309219888Sed{ 310219888Sed const term_pos_t *p1 = &r->tr_begin; 311219888Sed term_rect_t area; 312219888Sed unsigned int rows, cols; 313256145Sray int pr, rdiff; 314219888Sed 315256145Sray KASSERT(r->tr_begin.tp_row < vb->vb_scr_size.tp_row, 316256145Sray ("vtbuf_copy begin.tp_row %d must be less than screen width %d", 317256145Sray r->tr_begin.tp_row, vb->vb_scr_size.tp_row)); 318256145Sray KASSERT(r->tr_begin.tp_col < vb->vb_scr_size.tp_col, 319256145Sray ("vtbuf_copy begin.tp_col %d must be less than screen height %d", 320256145Sray r->tr_begin.tp_col, vb->vb_scr_size.tp_col)); 321256145Sray 322256145Sray KASSERT(r->tr_end.tp_row <= vb->vb_scr_size.tp_row, 323256145Sray ("vtbuf_copy end.tp_row %d must be less than screen width %d", 324256145Sray r->tr_end.tp_row, vb->vb_scr_size.tp_row)); 325256145Sray KASSERT(r->tr_end.tp_col <= vb->vb_scr_size.tp_col, 326256145Sray ("vtbuf_copy end.tp_col %d must be less than screen height %d", 327256145Sray r->tr_end.tp_col, vb->vb_scr_size.tp_col)); 328256145Sray 329256145Sray KASSERT(p2->tp_row < vb->vb_scr_size.tp_row, 330256145Sray ("vtbuf_copy tp_row %d must be less than screen width %d", 331256145Sray p2->tp_row, vb->vb_scr_size.tp_row)); 332256145Sray KASSERT(p2->tp_col < vb->vb_scr_size.tp_col, 333256145Sray ("vtbuf_copy tp_col %d must be less than screen height %d", 334256145Sray p2->tp_col, vb->vb_scr_size.tp_col)); 335256145Sray 336219888Sed rows = r->tr_end.tp_row - r->tr_begin.tp_row; 337256145Sray rdiff = r->tr_begin.tp_row - p2->tp_row; 338219888Sed cols = r->tr_end.tp_col - r->tr_begin.tp_col; 339256145Sray if (r->tr_begin.tp_row > p2->tp_row && r->tr_begin.tp_col == 0 && 340256145Sray r->tr_end.tp_col == vb->vb_scr_size.tp_col && /* Full row. */ 341256145Sray (rows + rdiff) == vb->vb_scr_size.tp_row && /* Whole screen. */ 342271952Sray rdiff > 0) { /* Only forward direction. Do not eat history. */ 343256145Sray vthistory_addlines(vb, rdiff); 344256145Sray } else if (p2->tp_row < p1->tp_row) { 345256145Sray /* Handle overlapping copies of line segments. */ 346219888Sed /* Move data up. */ 347219888Sed for (pr = 0; pr < rows; pr++) 348219888Sed memmove( 349219888Sed &VTBUF_FIELD(vb, p2->tp_row + pr, p2->tp_col), 350219888Sed &VTBUF_FIELD(vb, p1->tp_row + pr, p1->tp_col), 351219888Sed cols * sizeof(term_char_t)); 352219888Sed } else { 353219888Sed /* Move data down. */ 354219888Sed for (pr = rows - 1; pr >= 0; pr--) 355219888Sed memmove( 356219888Sed &VTBUF_FIELD(vb, p2->tp_row + pr, p2->tp_col), 357219888Sed &VTBUF_FIELD(vb, p1->tp_row + pr, p1->tp_col), 358219888Sed cols * sizeof(term_char_t)); 359219888Sed } 360219888Sed 361219888Sed area.tr_begin = *p2; 362256145Sray area.tr_end.tp_row = MIN(p2->tp_row + rows, vb->vb_scr_size.tp_row); 363256145Sray area.tr_end.tp_col = MIN(p2->tp_col + cols, vb->vb_scr_size.tp_col); 364219888Sed vtbuf_dirty(vb, &area); 365219888Sed} 366219888Sed 367256145Sraystatic void 368219888Sedvtbuf_fill(struct vt_buf *vb, const term_rect_t *r, term_char_t c) 369219888Sed{ 370219888Sed unsigned int pr, pc; 371256145Sray term_char_t *row; 372219888Sed 373256145Sray for (pr = r->tr_begin.tp_row; pr < r->tr_end.tp_row; pr++) { 374256145Sray row = vb->vb_rows[(vb->vb_curroffset + pr) % 375256145Sray VTBUF_MAX_HEIGHT(vb)]; 376256145Sray for (pc = r->tr_begin.tp_col; pc < r->tr_end.tp_col; pc++) { 377256145Sray row[pc] = c; 378256145Sray } 379256145Sray } 380256145Sray} 381219888Sed 382256145Srayvoid 383256145Srayvtbuf_fill_locked(struct vt_buf *vb, const term_rect_t *r, term_char_t c) 384256145Sray{ 385256145Sray KASSERT(r->tr_begin.tp_row < vb->vb_scr_size.tp_row, 386267538Sray ("vtbuf_fill_locked begin.tp_row %d must be < screen height %d", 387256145Sray r->tr_begin.tp_row, vb->vb_scr_size.tp_row)); 388256145Sray KASSERT(r->tr_begin.tp_col < vb->vb_scr_size.tp_col, 389267538Sray ("vtbuf_fill_locked begin.tp_col %d must be < screen width %d", 390256145Sray r->tr_begin.tp_col, vb->vb_scr_size.tp_col)); 391256145Sray 392256145Sray KASSERT(r->tr_end.tp_row <= vb->vb_scr_size.tp_row, 393267538Sray ("vtbuf_fill_locked end.tp_row %d must be <= screen height %d", 394256145Sray r->tr_end.tp_row, vb->vb_scr_size.tp_row)); 395256145Sray KASSERT(r->tr_end.tp_col <= vb->vb_scr_size.tp_col, 396267538Sray ("vtbuf_fill_locked end.tp_col %d must be <= screen width %d", 397256145Sray r->tr_end.tp_col, vb->vb_scr_size.tp_col)); 398256145Sray 399256145Sray VTBUF_LOCK(vb); 400256145Sray vtbuf_fill(vb, r, c); 401270182Sdumbbell vtbuf_dirty_locked(vb, r); 402256145Sray VTBUF_UNLOCK(vb); 403219888Sed} 404219888Sed 405256145Sraystatic void 406256145Srayvtbuf_init_rows(struct vt_buf *vb) 407256145Sray{ 408256145Sray int r; 409256145Sray 410256145Sray vb->vb_history_size = MAX(vb->vb_history_size, vb->vb_scr_size.tp_row); 411256145Sray 412256145Sray for (r = 0; r < vb->vb_history_size; r++) 413268037Smarius vb->vb_rows[r] = &vb->vb_buffer[r * vb->vb_scr_size.tp_col]; 414256145Sray} 415256145Sray 416219888Sedvoid 417219888Sedvtbuf_init_early(struct vt_buf *vb) 418219888Sed{ 419268037Smarius term_rect_t rect; 420219888Sed 421219888Sed vb->vb_flags |= VBF_CURSOR; 422256145Sray vb->vb_roffset = 0; 423256145Sray vb->vb_curroffset = 0; 424258090Sray vb->vb_mark_start.tp_row = 0; 425258090Sray vb->vb_mark_start.tp_col = 0; 426258090Sray vb->vb_mark_end.tp_row = 0; 427258090Sray vb->vb_mark_end.tp_col = 0; 428256145Sray 429256145Sray vtbuf_init_rows(vb); 430268037Smarius rect.tr_begin.tp_row = rect.tr_begin.tp_col = 0; 431271128Semaste rect.tr_end.tp_col = vb->vb_scr_size.tp_col; 432271128Semaste rect.tr_end.tp_row = vb->vb_history_size; 433271128Semaste vtbuf_fill(vb, &rect, VTBUF_SPACE_CHAR(TERMINAL_NORM_ATTR)); 434219888Sed vtbuf_make_undirty(vb); 435256145Sray if ((vb->vb_flags & VBF_MTX_INIT) == 0) { 436256145Sray mtx_init(&vb->vb_lock, "vtbuf", NULL, MTX_SPIN); 437256145Sray vb->vb_flags |= VBF_MTX_INIT; 438256145Sray } 439219888Sed} 440219888Sed 441219888Sedvoid 442219888Sedvtbuf_init(struct vt_buf *vb, const term_pos_t *p) 443219888Sed{ 444256145Sray int sz; 445219888Sed 446256145Sray vb->vb_scr_size = *p; 447256145Sray vb->vb_history_size = VBF_DEFAULT_HISTORY_SIZE; 448256145Sray 449256145Sray if ((vb->vb_flags & VBF_STATIC) == 0) { 450256145Sray sz = vb->vb_history_size * p->tp_col * sizeof(term_char_t); 451256894Sray vb->vb_buffer = malloc(sz, M_VTBUF, M_WAITOK | M_ZERO); 452256145Sray 453256145Sray sz = vb->vb_history_size * sizeof(term_char_t *); 454256894Sray vb->vb_rows = malloc(sz, M_VTBUF, M_WAITOK | M_ZERO); 455256145Sray } 456256145Sray 457219888Sed vtbuf_init_early(vb); 458219888Sed} 459219888Sed 460219888Sedvoid 461256145Srayvtbuf_sethistory_size(struct vt_buf *vb, int size) 462219888Sed{ 463256145Sray term_pos_t p; 464219888Sed 465256145Sray /* With same size */ 466256145Sray p.tp_row = vb->vb_scr_size.tp_row; 467256145Sray p.tp_col = vb->vb_scr_size.tp_col; 468256145Sray vtbuf_grow(vb, &p, size); 469256145Sray} 470256145Sray 471256145Srayvoid 472271128Semastevtbuf_grow(struct vt_buf *vb, const term_pos_t *p, unsigned int history_size) 473256145Sray{ 474271973Sdumbbell term_char_t *old, *new, **rows, **oldrows, **copyrows, *row, *oldrow; 475271973Sdumbbell int bufsize, rowssize, w, h, c, r, history_was_full; 476271973Sdumbbell unsigned int old_history_size; 477256145Sray term_rect_t rect; 478256145Sray 479256145Sray history_size = MAX(history_size, p->tp_row); 480256145Sray 481271973Sdumbbell /* Allocate new buffer. */ 482271973Sdumbbell bufsize = history_size * p->tp_col * sizeof(term_char_t); 483271973Sdumbbell new = malloc(bufsize, M_VTBUF, M_WAITOK | M_ZERO); 484271973Sdumbbell rowssize = history_size * sizeof(term_pos_t *); 485271973Sdumbbell rows = malloc(rowssize, M_VTBUF, M_WAITOK | M_ZERO); 486219888Sed 487271973Sdumbbell /* Toggle it. */ 488271973Sdumbbell VTBUF_LOCK(vb); 489271973Sdumbbell old = vb->vb_flags & VBF_STATIC ? NULL : vb->vb_buffer; 490271973Sdumbbell oldrows = vb->vb_flags & VBF_STATIC ? NULL : vb->vb_rows; 491271973Sdumbbell copyrows = vb->vb_rows; 492256145Sray 493271973Sdumbbell w = vb->vb_scr_size.tp_col; 494271973Sdumbbell h = vb->vb_scr_size.tp_row; 495271973Sdumbbell old_history_size = vb->vb_history_size; 496271973Sdumbbell history_was_full = vb->vb_flags & VBF_HISTORY_FULL; 497256145Sray 498271973Sdumbbell vb->vb_history_size = history_size; 499271973Sdumbbell vb->vb_buffer = new; 500271973Sdumbbell vb->vb_rows = rows; 501271973Sdumbbell vb->vb_flags &= ~VBF_STATIC; 502271973Sdumbbell vb->vb_scr_size = *p; 503271973Sdumbbell vtbuf_init_rows(vb); 504271973Sdumbbell 505271973Sdumbbell /* Copy history and fill extra space if needed. */ 506271973Sdumbbell if (history_size > old_history_size) { 507271973Sdumbbell /* 508271973Sdumbbell * Copy rows to the new buffer. The first row in the history 509271973Sdumbbell * is back to index 0, ie. the new buffer doesn't cycle. 510271973Sdumbbell * 511271973Sdumbbell * The rest of the new buffer is initialized with blank 512271973Sdumbbell * content. 513271973Sdumbbell */ 514271973Sdumbbell for (r = 0; r < old_history_size; r ++) { 515271973Sdumbbell row = rows[r]; 516271973Sdumbbell 517271973Sdumbbell /* Compute the corresponding row in the old buffer. */ 518271973Sdumbbell if (history_was_full) 519271973Sdumbbell /* 520271973Sdumbbell * The buffer is full, the "top" row is 521271973Sdumbbell * the one just after the viewable area 522271973Sdumbbell * (curroffset + viewable height) in the 523271973Sdumbbell * cycling buffer. The corresponding row 524271973Sdumbbell * is computed from this top row. 525271973Sdumbbell */ 526271973Sdumbbell oldrow = copyrows[ 527271973Sdumbbell (vb->vb_curroffset + h + r) % 528271973Sdumbbell old_history_size]; 529271973Sdumbbell else 530271973Sdumbbell /* 531271973Sdumbbell * The buffer is not full, therefore, 532271973Sdumbbell * we didn't cycle already. The 533271973Sdumbbell * corresponding rows are the same in 534271973Sdumbbell * both buffers. 535271973Sdumbbell */ 536271973Sdumbbell oldrow = copyrows[r]; 537271973Sdumbbell 538271973Sdumbbell memmove(row, oldrow, 539271973Sdumbbell MIN(p->tp_col, w) * sizeof(term_char_t)); 540271973Sdumbbell 541271973Sdumbbell /* 542271973Sdumbbell * XXX VTBUF_SPACE_CHAR(TERMINAL_NORM_ATTR) will 543271973Sdumbbell * extended lines of kernel text using the wrong 544271973Sdumbbell * background color. 545271973Sdumbbell */ 546271973Sdumbbell for (c = MIN(p->tp_col, w); c < p->tp_col; c++) { 547271973Sdumbbell row[c] = VTBUF_SPACE_CHAR(TERMINAL_NORM_ATTR); 548271973Sdumbbell } 549271973Sdumbbell } 550271973Sdumbbell 551271973Sdumbbell /* Fill remaining rows. */ 552271973Sdumbbell rect.tr_begin.tp_col = 0; 553271973Sdumbbell rect.tr_begin.tp_row = old_history_size; 554271973Sdumbbell rect.tr_end.tp_col = p->tp_col; 555271973Sdumbbell rect.tr_end.tp_row = p->tp_row; 556271973Sdumbbell vtbuf_fill(vb, &rect, VTBUF_SPACE_CHAR(TERMINAL_NORM_ATTR)); 557271973Sdumbbell 558271973Sdumbbell vb->vb_flags &= ~VBF_HISTORY_FULL; 559271973Sdumbbell } else { 560271973Sdumbbell /* 561271973Sdumbbell * Copy rows to the new buffer. The first row in the history 562271973Sdumbbell * is back to index 0, ie. the new buffer doesn't cycle. 563271973Sdumbbell * 564271973Sdumbbell * (old_history_size - history_size) lines of history are 565271973Sdumbbell * dropped. 566271973Sdumbbell */ 567256145Sray for (r = 0; r < history_size; r ++) { 568271973Sdumbbell row = rows[r]; 569271973Sdumbbell 570268037Smarius /* 571271973Sdumbbell * Compute the corresponding row in the old buffer. 572271973Sdumbbell * 573271973Sdumbbell * See the equivalent if{} block above for an 574271973Sdumbbell * explanation. 575271973Sdumbbell */ 576271973Sdumbbell if (history_was_full) 577271973Sdumbbell oldrow = copyrows[ 578271973Sdumbbell (vb->vb_curroffset + h + r + 579271973Sdumbbell (old_history_size - history_size)) % 580271973Sdumbbell old_history_size]; 581271973Sdumbbell else 582271973Sdumbbell oldrow = copyrows[ 583271973Sdumbbell (r + (old_history_size - history_size)) % 584271973Sdumbbell old_history_size]; 585271973Sdumbbell 586271973Sdumbbell memmove(row, oldrow, 587271973Sdumbbell MIN(p->tp_col, w) * sizeof(term_char_t)); 588271973Sdumbbell 589271973Sdumbbell /* 590268037Smarius * XXX VTBUF_SPACE_CHAR(TERMINAL_NORM_ATTR) will 591268037Smarius * extended lines of kernel text using the wrong 592268037Smarius * background color. 593268037Smarius */ 594271973Sdumbbell for (c = MIN(p->tp_col, w); c < p->tp_col; c++) { 595271973Sdumbbell row[c] = VTBUF_SPACE_CHAR(TERMINAL_NORM_ATTR); 596256145Sray } 597256145Sray } 598271973Sdumbbell 599271973Sdumbbell if (!history_was_full && 600271973Sdumbbell (vb->vb_curroffset + h) >= history_size) 601271973Sdumbbell vb->vb_flags |= VBF_HISTORY_FULL; 602219888Sed } 603271973Sdumbbell 604271973Sdumbbell /* 605271973Sdumbbell * If the screen is already filled (there are non-visible lines 606271973Sdumbbell * above the current viewable area), adjust curroffset to the 607271973Sdumbbell * new viewable area. 608271973Sdumbbell */ 609271973Sdumbbell if (!history_was_full && vb->vb_curroffset > 0) { 610271973Sdumbbell vb->vb_curroffset = vb->vb_curroffset + h - p->tp_row; 611271973Sdumbbell if (vb->vb_curroffset < 0) 612271973Sdumbbell vb->vb_curroffset += vb->vb_history_size; 613271973Sdumbbell vb->vb_curroffset %= vb->vb_history_size; 614271973Sdumbbell vb->vb_roffset = vb->vb_curroffset; 615271973Sdumbbell } 616271973Sdumbbell 617274860Sdumbbell /* Adjust cursor position. */ 618274860Sdumbbell if (vb->vb_cursor.tp_col > p->tp_col - 1) 619274860Sdumbbell /* 620274860Sdumbbell * Move cursor to the last column, in case its previous 621274860Sdumbbell * position is outside of the new screen area. 622274860Sdumbbell */ 623274860Sdumbbell vb->vb_cursor.tp_col = p->tp_col - 1; 624274860Sdumbbell 625274860Sdumbbell if (vb->vb_curroffset > 0 || vb->vb_cursor.tp_row > p->tp_row - 1) 626274860Sdumbbell /* Move cursor to the last line on the screen. */ 627274860Sdumbbell vb->vb_cursor.tp_row = p->tp_row - 1; 628274860Sdumbbell 629271973Sdumbbell vtbuf_make_undirty(vb); 630271973Sdumbbell VTBUF_UNLOCK(vb); 631271973Sdumbbell 632271973Sdumbbell /* Deallocate old buffer. */ 633271973Sdumbbell free(old, M_VTBUF); 634271973Sdumbbell free(oldrows, M_VTBUF); 635219888Sed} 636219888Sed 637219888Sedvoid 638219888Sedvtbuf_putchar(struct vt_buf *vb, const term_pos_t *p, term_char_t c) 639219888Sed{ 640256145Sray term_char_t *row; 641219888Sed 642256145Sray KASSERT(p->tp_row < vb->vb_scr_size.tp_row, 643256145Sray ("vtbuf_putchar tp_row %d must be less than screen width %d", 644256145Sray p->tp_row, vb->vb_scr_size.tp_row)); 645256145Sray KASSERT(p->tp_col < vb->vb_scr_size.tp_col, 646256145Sray ("vtbuf_putchar tp_col %d must be less than screen height %d", 647256145Sray p->tp_col, vb->vb_scr_size.tp_col)); 648256145Sray 649256145Sray row = vb->vb_rows[(vb->vb_curroffset + p->tp_row) % 650256145Sray VTBUF_MAX_HEIGHT(vb)]; 651256145Sray if (row[p->tp_col] != c) { 652256145Sray VTBUF_LOCK(vb); 653256145Sray row[p->tp_col] = c; 654270182Sdumbbell vtbuf_dirty_cell_locked(vb, p); 655256145Sray VTBUF_UNLOCK(vb); 656219888Sed } 657219888Sed} 658219888Sed 659219888Sedvoid 660219888Sedvtbuf_cursor_position(struct vt_buf *vb, const term_pos_t *p) 661219888Sed{ 662219888Sed 663219888Sed if (vb->vb_flags & VBF_CURSOR) { 664270182Sdumbbell VTBUF_LOCK(vb); 665270182Sdumbbell vtbuf_dirty_cell_locked(vb, &vb->vb_cursor); 666219888Sed vb->vb_cursor = *p; 667270182Sdumbbell vtbuf_dirty_cell_locked(vb, &vb->vb_cursor); 668270182Sdumbbell VTBUF_UNLOCK(vb); 669219888Sed } else { 670219888Sed vb->vb_cursor = *p; 671219888Sed } 672219888Sed} 673219888Sed 674262861Sjhb#ifndef SC_NO_CUTPASTE 675258090Sraystatic void 676258090Srayvtbuf_flush_mark(struct vt_buf *vb) 677258090Sray{ 678258090Sray term_rect_t area; 679258090Sray int s, e; 680258090Sray 681258090Sray /* Notify renderer to update marked region. */ 682321200Semaste if ((vb->vb_mark_start.tp_col != vb->vb_mark_end.tp_col) || 683321200Semaste (vb->vb_mark_start.tp_row != vb->vb_mark_end.tp_row)) { 684258090Sray 685258090Sray s = vtbuf_htw(vb, vb->vb_mark_start.tp_row); 686258090Sray e = vtbuf_htw(vb, vb->vb_mark_end.tp_row); 687258090Sray 688258090Sray area.tr_begin.tp_col = 0; 689258090Sray area.tr_begin.tp_row = MIN(s, e); 690258090Sray 691258090Sray area.tr_end.tp_col = vb->vb_scr_size.tp_col; 692258090Sray area.tr_end.tp_row = MAX(s, e) + 1; 693258090Sray 694258090Sray vtbuf_dirty(vb, &area); 695258090Sray } 696258090Sray} 697258090Sray 698258090Srayint 699258090Srayvtbuf_get_marked_len(struct vt_buf *vb) 700258090Sray{ 701258090Sray int ei, si, sz; 702258090Sray term_pos_t s, e; 703258090Sray 704258090Sray /* Swap according to window coordinates. */ 705258134Sray if (POS_INDEX(vtbuf_htw(vb, vb->vb_mark_start.tp_row), 706258134Sray vb->vb_mark_start.tp_col) > 707258134Sray POS_INDEX(vtbuf_htw(vb, vb->vb_mark_end.tp_row), 708258134Sray vb->vb_mark_end.tp_col)) { 709258090Sray POS_COPY(e, vb->vb_mark_start); 710258090Sray POS_COPY(s, vb->vb_mark_end); 711258090Sray } else { 712258090Sray POS_COPY(s, vb->vb_mark_start); 713258090Sray POS_COPY(e, vb->vb_mark_end); 714258090Sray } 715258090Sray 716258090Sray si = s.tp_row * vb->vb_scr_size.tp_col + s.tp_col; 717258090Sray ei = e.tp_row * vb->vb_scr_size.tp_col + e.tp_col; 718258090Sray 719258090Sray /* Number symbols and number of rows to inject \n */ 720271952Sray sz = ei - si + ((e.tp_row - s.tp_row) * 2); 721258090Sray 722258090Sray return (sz * sizeof(term_char_t)); 723258090Sray} 724258090Sray 725257971Srayvoid 726258090Srayvtbuf_extract_marked(struct vt_buf *vb, term_char_t *buf, int sz) 727258090Sray{ 728258090Sray int i, r, c, cs, ce; 729258090Sray term_pos_t s, e; 730258090Sray 731258090Sray /* Swap according to window coordinates. */ 732258134Sray if (POS_INDEX(vtbuf_htw(vb, vb->vb_mark_start.tp_row), 733258134Sray vb->vb_mark_start.tp_col) > 734258134Sray POS_INDEX(vtbuf_htw(vb, vb->vb_mark_end.tp_row), 735258134Sray vb->vb_mark_end.tp_col)) { 736258090Sray POS_COPY(e, vb->vb_mark_start); 737258090Sray POS_COPY(s, vb->vb_mark_end); 738258090Sray } else { 739258090Sray POS_COPY(s, vb->vb_mark_start); 740258090Sray POS_COPY(e, vb->vb_mark_end); 741258090Sray } 742258090Sray 743258090Sray i = 0; 744258090Sray for (r = s.tp_row; r <= e.tp_row; r ++) { 745258090Sray cs = (r == s.tp_row)?s.tp_col:0; 746258090Sray ce = (r == e.tp_row)?e.tp_col:vb->vb_scr_size.tp_col; 747258090Sray for (c = cs; c < ce; c ++) { 748258090Sray buf[i++] = vb->vb_rows[r][c]; 749258090Sray } 750258093Sray /* Add new line for all rows, but not for last one. */ 751258093Sray if (r != e.tp_row) { 752258093Sray buf[i++] = '\r'; 753258093Sray buf[i++] = '\n'; 754258093Sray } 755258090Sray } 756258090Sray} 757258090Sray 758258090Srayint 759257971Srayvtbuf_set_mark(struct vt_buf *vb, int type, int col, int row) 760257971Sray{ 761258136Sray term_char_t *r; 762258136Sray int i; 763257971Sray 764257971Sray switch (type) { 765258130Sray case VTB_MARK_END: /* B1 UP */ 766258130Sray if (vb->vb_mark_last != VTB_MARK_MOVE) 767258130Sray return (0); 768258130Sray /* FALLTHROUGH */ 769258130Sray case VTB_MARK_MOVE: 770257971Sray case VTB_MARK_EXTEND: 771258090Sray vtbuf_flush_mark(vb); /* Clean old mark. */ 772257971Sray vb->vb_mark_end.tp_col = col; 773258090Sray vb->vb_mark_end.tp_row = vtbuf_wth(vb, row); 774257971Sray break; 775257971Sray case VTB_MARK_START: 776258090Sray vtbuf_flush_mark(vb); /* Clean old mark. */ 777257971Sray vb->vb_mark_start.tp_col = col; 778258090Sray vb->vb_mark_start.tp_row = vtbuf_wth(vb, row); 779257971Sray /* Start again, so clear end point. */ 780258090Sray vb->vb_mark_end.tp_col = col; 781258090Sray vb->vb_mark_end.tp_row = vtbuf_wth(vb, row); 782257971Sray break; 783257971Sray case VTB_MARK_WORD: 784258090Sray vtbuf_flush_mark(vb); /* Clean old mark. */ 785258090Sray vb->vb_mark_start.tp_row = vb->vb_mark_end.tp_row = 786258090Sray vtbuf_wth(vb, row); 787258136Sray r = vb->vb_rows[vb->vb_mark_start.tp_row]; 788258136Sray for (i = col; i >= 0; i --) { 789258136Sray if (TCHAR_CHARACTER(r[i]) == ' ') { 790258136Sray vb->vb_mark_start.tp_col = i + 1; 791258136Sray break; 792258136Sray } 793258136Sray } 794258136Sray for (i = col; i < vb->vb_scr_size.tp_col; i ++) { 795258136Sray if (TCHAR_CHARACTER(r[i]) == ' ') { 796258136Sray vb->vb_mark_end.tp_col = i; 797258136Sray break; 798258136Sray } 799258136Sray } 800258136Sray if (vb->vb_mark_start.tp_col > vb->vb_mark_end.tp_col) 801258136Sray vb->vb_mark_start.tp_col = vb->vb_mark_end.tp_col; 802257971Sray break; 803257971Sray case VTB_MARK_ROW: 804258090Sray vtbuf_flush_mark(vb); /* Clean old mark. */ 805257971Sray vb->vb_mark_start.tp_col = 0; 806257971Sray vb->vb_mark_end.tp_col = vb->vb_scr_size.tp_col; 807258090Sray vb->vb_mark_start.tp_row = vb->vb_mark_end.tp_row = 808258090Sray vtbuf_wth(vb, row); 809257971Sray break; 810258090Sray case VTB_MARK_NONE: 811258130Sray vb->vb_mark_last = type; 812258130Sray /* FALLTHROUGH */ 813258090Sray default: 814258090Sray /* panic? */ 815258090Sray return (0); 816257971Sray } 817258111Sray 818258130Sray vb->vb_mark_last = type; 819258111Sray /* Draw new marked region. */ 820258111Sray vtbuf_flush_mark(vb); 821258111Sray return (1); 822257971Sray} 823262861Sjhb#endif 824257971Sray 825257971Srayvoid 826219888Sedvtbuf_cursor_visibility(struct vt_buf *vb, int yes) 827219888Sed{ 828219888Sed int oflags, nflags; 829219888Sed 830219888Sed VTBUF_LOCK(vb); 831219888Sed oflags = vb->vb_flags; 832219888Sed if (yes) 833219888Sed vb->vb_flags |= VBF_CURSOR; 834219888Sed else 835219888Sed vb->vb_flags &= ~VBF_CURSOR; 836219888Sed nflags = vb->vb_flags; 837219888Sed 838219888Sed if (oflags != nflags) 839270182Sdumbbell vtbuf_dirty_cell_locked(vb, &vb->vb_cursor); 840270182Sdumbbell VTBUF_UNLOCK(vb); 841219888Sed} 842258090Sray 843258090Srayvoid 844258090Srayvtbuf_scroll_mode(struct vt_buf *vb, int yes) 845258090Sray{ 846258090Sray int oflags, nflags; 847258090Sray 848258090Sray VTBUF_LOCK(vb); 849258090Sray oflags = vb->vb_flags; 850258090Sray if (yes) 851258090Sray vb->vb_flags |= VBF_SCROLL; 852258090Sray else 853258090Sray vb->vb_flags &= ~VBF_SCROLL; 854258090Sray nflags = vb->vb_flags; 855258090Sray 856258090Sray if (oflags != nflags) 857270182Sdumbbell vtbuf_dirty_cell_locked(vb, &vb->vb_cursor); 858270182Sdumbbell VTBUF_UNLOCK(vb); 859258090Sray} 860258090Sray 861