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