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