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$");
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
57258090Sray
58256145Sray/*
59256145Sray * line4
60256145Sray * line5 <--- curroffset (terminal output to that line)
61256145Sray * line0
62256145Sray * line1                  <--- roffset (history display from that point)
63256145Sray * line2
64256145Sray * line3
65256145Sray */
66256145Srayint
67256145Srayvthistory_seek(struct vt_buf *vb, int offset, int whence)
68256145Sray{
69257074Sray	int diff, top, bottom, roffset;
70219888Sed
71256145Sray	/* No scrolling if not enabled. */
72256145Sray	if ((vb->vb_flags & VBF_SCROLL) == 0) {
73256145Sray		if (vb->vb_roffset != vb->vb_curroffset) {
74256145Sray			vb->vb_roffset = vb->vb_curroffset;
75257074Sray			return (0xffff);
76256145Sray		}
77256145Sray		return (0); /* No changes */
78256145Sray	}
79256970Sray
80271973Sdumbbell	/* "top" may be a negative integer. */
81271973Sdumbbell	bottom = vb->vb_curroffset;
82271973Sdumbbell	top = (vb->vb_flags & VBF_HISTORY_FULL) ?
83271973Sdumbbell	    bottom + vb->vb_scr_size.tp_row - vb->vb_history_size :
84271973Sdumbbell	    0;
85271973Sdumbbell
86271973Sdumbbell	roffset = 0; /* Make gcc happy. */
87256145Sray	switch (whence) {
88256145Sray	case VHS_SET:
89271973Sdumbbell		if (offset < 0)
90271973Sdumbbell			offset = 0;
91271973Sdumbbell		roffset = top + offset;
92256145Sray		break;
93256145Sray	case VHS_CUR:
94271973Sdumbbell		/*
95271973Sdumbbell		 * Operate on copy of offset value, since it temporary
96271973Sdumbbell		 * can be bigger than amount of rows in buffer.
97271973Sdumbbell		 */
98271973Sdumbbell		roffset = vb->vb_roffset;
99271973Sdumbbell		if (roffset >= bottom + vb->vb_scr_size.tp_row)
100271973Sdumbbell			roffset -= vb->vb_history_size;
101271973Sdumbbell
102256145Sray		roffset += offset;
103271973Sdumbbell		roffset = MAX(roffset, top);
104271973Sdumbbell		roffset = MIN(roffset, bottom);
105271973Sdumbbell
106271973Sdumbbell		if (roffset < 0)
107271973Sdumbbell			roffset = vb->vb_history_size + roffset;
108271973Sdumbbell
109256145Sray		break;
110256145Sray	case VHS_END:
111256145Sray		/* Go to current offset. */
112271973Sdumbbell		roffset = vb->vb_curroffset;
113256145Sray		break;
114256145Sray	}
115256145Sray
116271973Sdumbbell	diff = vb->vb_roffset != roffset;
117271973Sdumbbell	vb->vb_roffset = roffset;
118256145Sray
119271973Sdumbbell	return (diff);
120256145Sray}
121256145Sray
122256145Srayvoid
123256145Srayvthistory_addlines(struct vt_buf *vb, int offset)
124256145Sray{
125256145Sray
126256145Sray	vb->vb_curroffset += offset;
127256145Sray	if (vb->vb_curroffset < 0)
128256145Sray		vb->vb_curroffset = 0;
129271973Sdumbbell	if (vb->vb_curroffset + vb->vb_scr_size.tp_row >= vb->vb_history_size)
130271973Sdumbbell		vb->vb_flags |= VBF_HISTORY_FULL;
131256145Sray	vb->vb_curroffset %= vb->vb_history_size;
132256145Sray	if ((vb->vb_flags & VBF_SCROLL) == 0) {
133256145Sray		vb->vb_roffset = vb->vb_curroffset;
134256145Sray	}
135256145Sray}
136256145Sray
137256145Srayvoid
138256145Srayvthistory_getpos(const struct vt_buf *vb, unsigned int *offset)
139256145Sray{
140256145Sray
141256145Sray	*offset = vb->vb_roffset;
142256145Sray}
143256145Sray
144262861Sjhb#ifndef SC_NO_CUTPASTE	/* Only mouse support use it now. */
145258090Sray/* Translate current view row number to history row. */
146258090Sraystatic int
147258090Srayvtbuf_wth(struct vt_buf *vb, int row)
148258090Sray{
149258090Sray
150258090Sray	return ((vb->vb_roffset + row) % vb->vb_history_size);
151258090Sray}
152262861Sjhb#endif
153258090Sray
154258090Sray/* Translate history row to current view row number. */
155258090Sraystatic int
156271024Semastevtbuf_htw(const struct vt_buf *vb, int row)
157258090Sray{
158258090Sray
159258090Sray	/*
160258090Sray	 * total 1000 rows.
161258090Sray	 * History offset	roffset	winrow
162258090Sray	 *	205		200	((205 - 200 + 1000) % 1000) = 5
163258090Sray	 *	90		990	((90 - 990 + 1000) % 1000) = 100
164258090Sray	 */
165258090Sray	return ((row - vb->vb_roffset + vb->vb_history_size) %
166258090Sray	    vb->vb_history_size);
167258090Sray}
168258090Sray
169257971Srayint
170271024Semastevtbuf_iscursor(const struct vt_buf *vb, int row, int col)
171257971Sray{
172258090Sray	int sc, sr, ec, er, tmp;
173258090Sray
174258090Sray	if ((vb->vb_flags & (VBF_CURSOR|VBF_SCROLL)) == VBF_CURSOR &&
175258090Sray	    (vb->vb_cursor.tp_row == row) && (vb->vb_cursor.tp_col == col))
176257971Sray		return (1);
177257971Sray
178258090Sray	/* Mark cut/paste region. */
179258090Sray
180258090Sray	/*
181258090Sray	 * Luckily screen view is not like circular buffer, so we will
182258090Sray	 * calculate in screen coordinates.  Translate first.
183258090Sray	 */
184258090Sray	sc = vb->vb_mark_start.tp_col;
185258090Sray	sr = vtbuf_htw(vb, vb->vb_mark_start.tp_row);
186258090Sray	ec = vb->vb_mark_end.tp_col;
187258090Sray	er = vtbuf_htw(vb, vb->vb_mark_end.tp_row);
188258090Sray
189258090Sray
190258090Sray	/* Swap start and end if start > end. */
191258090Sray	if (POS_INDEX(sc, sr) > POS_INDEX(ec, er)) {
192258090Sray		tmp = sc; sc = ec; ec = tmp;
193258090Sray		tmp = sr; sr = er; er = tmp;
194258090Sray	}
195258090Sray
196258090Sray	if ((POS_INDEX(sc, sr) <= POS_INDEX(col, row)) &&
197258090Sray	    (POS_INDEX(col, row) < POS_INDEX(ec, er)))
198257971Sray		return (1);
199257971Sray
200257971Sray	return (0);
201257971Sray}
202257971Sray
203219888Sedstatic inline void
204270182Sdumbbellvtbuf_dirty_locked(struct vt_buf *vb, const term_rect_t *area)
205219888Sed{
206219888Sed
207219888Sed	if (vb->vb_dirtyrect.tr_begin.tp_row > area->tr_begin.tp_row)
208219888Sed		vb->vb_dirtyrect.tr_begin.tp_row = area->tr_begin.tp_row;
209219888Sed	if (vb->vb_dirtyrect.tr_begin.tp_col > area->tr_begin.tp_col)
210219888Sed		vb->vb_dirtyrect.tr_begin.tp_col = area->tr_begin.tp_col;
211219888Sed	if (vb->vb_dirtyrect.tr_end.tp_row < area->tr_end.tp_row)
212219888Sed		vb->vb_dirtyrect.tr_end.tp_row = area->tr_end.tp_row;
213219888Sed	if (vb->vb_dirtyrect.tr_end.tp_col < area->tr_end.tp_col)
214219888Sed		vb->vb_dirtyrect.tr_end.tp_col = area->tr_end.tp_col;
215270182Sdumbbell}
216270182Sdumbbell
217271128Semastevoid
218270182Sdumbbellvtbuf_dirty(struct vt_buf *vb, const term_rect_t *area)
219270182Sdumbbell{
220270182Sdumbbell
221270182Sdumbbell	VTBUF_LOCK(vb);
222270182Sdumbbell	vtbuf_dirty_locked(vb, area);
223219888Sed	VTBUF_UNLOCK(vb);
224219888Sed}
225219888Sed
226219888Sedstatic inline void
227270182Sdumbbellvtbuf_dirty_cell_locked(struct vt_buf *vb, const term_pos_t *p)
228219888Sed{
229219888Sed	term_rect_t area;
230219888Sed
231219888Sed	area.tr_begin = *p;
232219888Sed	area.tr_end.tp_row = p->tp_row + 1;
233219888Sed	area.tr_end.tp_col = p->tp_col + 1;
234270182Sdumbbell	vtbuf_dirty_locked(vb, &area);
235219888Sed}
236219888Sed
237219888Sedstatic void
238219888Sedvtbuf_make_undirty(struct vt_buf *vb)
239219888Sed{
240219888Sed
241256145Sray	vb->vb_dirtyrect.tr_begin = vb->vb_scr_size;
242219888Sed	vb->vb_dirtyrect.tr_end.tp_row = vb->vb_dirtyrect.tr_end.tp_col = 0;
243219888Sed}
244219888Sed
245219888Sedvoid
246271973Sdumbbellvtbuf_undirty(struct vt_buf *vb, term_rect_t *r)
247219888Sed{
248219888Sed
249219888Sed	VTBUF_LOCK(vb);
250219888Sed	*r = vb->vb_dirtyrect;
251219888Sed	vtbuf_make_undirty(vb);
252219888Sed	VTBUF_UNLOCK(vb);
253219888Sed}
254219888Sed
255219888Sedvoid
256219888Sedvtbuf_copy(struct vt_buf *vb, const term_rect_t *r, const term_pos_t *p2)
257219888Sed{
258219888Sed	const term_pos_t *p1 = &r->tr_begin;
259219888Sed	term_rect_t area;
260219888Sed	unsigned int rows, cols;
261256145Sray	int pr, rdiff;
262219888Sed
263256145Sray	KASSERT(r->tr_begin.tp_row < vb->vb_scr_size.tp_row,
264256145Sray	    ("vtbuf_copy begin.tp_row %d must be less than screen width %d",
265256145Sray		r->tr_begin.tp_row, vb->vb_scr_size.tp_row));
266256145Sray	KASSERT(r->tr_begin.tp_col < vb->vb_scr_size.tp_col,
267256145Sray	    ("vtbuf_copy begin.tp_col %d must be less than screen height %d",
268256145Sray		r->tr_begin.tp_col, vb->vb_scr_size.tp_col));
269256145Sray
270256145Sray	KASSERT(r->tr_end.tp_row <= vb->vb_scr_size.tp_row,
271256145Sray	    ("vtbuf_copy end.tp_row %d must be less than screen width %d",
272256145Sray		r->tr_end.tp_row, vb->vb_scr_size.tp_row));
273256145Sray	KASSERT(r->tr_end.tp_col <= vb->vb_scr_size.tp_col,
274256145Sray	    ("vtbuf_copy end.tp_col %d must be less than screen height %d",
275256145Sray		r->tr_end.tp_col, vb->vb_scr_size.tp_col));
276256145Sray
277256145Sray	KASSERT(p2->tp_row < vb->vb_scr_size.tp_row,
278256145Sray	    ("vtbuf_copy tp_row %d must be less than screen width %d",
279256145Sray		p2->tp_row, vb->vb_scr_size.tp_row));
280256145Sray	KASSERT(p2->tp_col < vb->vb_scr_size.tp_col,
281256145Sray	    ("vtbuf_copy tp_col %d must be less than screen height %d",
282256145Sray		p2->tp_col, vb->vb_scr_size.tp_col));
283256145Sray
284219888Sed	rows = r->tr_end.tp_row - r->tr_begin.tp_row;
285256145Sray	rdiff = r->tr_begin.tp_row - p2->tp_row;
286219888Sed	cols = r->tr_end.tp_col - r->tr_begin.tp_col;
287256145Sray	if (r->tr_begin.tp_row > p2->tp_row && r->tr_begin.tp_col == 0 &&
288256145Sray	    r->tr_end.tp_col == vb->vb_scr_size.tp_col && /* Full row. */
289256145Sray	    (rows + rdiff) == vb->vb_scr_size.tp_row && /* Whole screen. */
290271952Sray	    rdiff > 0) { /* Only forward direction. Do not eat history. */
291256145Sray		vthistory_addlines(vb, rdiff);
292256145Sray	} else if (p2->tp_row < p1->tp_row) {
293256145Sray		/* Handle overlapping copies of line segments. */
294219888Sed		/* Move data up. */
295219888Sed		for (pr = 0; pr < rows; pr++)
296219888Sed			memmove(
297219888Sed			    &VTBUF_FIELD(vb, p2->tp_row + pr, p2->tp_col),
298219888Sed			    &VTBUF_FIELD(vb, p1->tp_row + pr, p1->tp_col),
299219888Sed			    cols * sizeof(term_char_t));
300219888Sed	} else {
301219888Sed		/* Move data down. */
302219888Sed		for (pr = rows - 1; pr >= 0; pr--)
303219888Sed			memmove(
304219888Sed			    &VTBUF_FIELD(vb, p2->tp_row + pr, p2->tp_col),
305219888Sed			    &VTBUF_FIELD(vb, p1->tp_row + pr, p1->tp_col),
306219888Sed			    cols * sizeof(term_char_t));
307219888Sed	}
308219888Sed
309219888Sed	area.tr_begin = *p2;
310256145Sray	area.tr_end.tp_row = MIN(p2->tp_row + rows, vb->vb_scr_size.tp_row);
311256145Sray	area.tr_end.tp_col = MIN(p2->tp_col + cols, vb->vb_scr_size.tp_col);
312219888Sed	vtbuf_dirty(vb, &area);
313219888Sed}
314219888Sed
315256145Sraystatic void
316219888Sedvtbuf_fill(struct vt_buf *vb, const term_rect_t *r, term_char_t c)
317219888Sed{
318219888Sed	unsigned int pr, pc;
319256145Sray	term_char_t *row;
320219888Sed
321256145Sray	for (pr = r->tr_begin.tp_row; pr < r->tr_end.tp_row; pr++) {
322256145Sray		row = vb->vb_rows[(vb->vb_curroffset + pr) %
323256145Sray		    VTBUF_MAX_HEIGHT(vb)];
324256145Sray		for (pc = r->tr_begin.tp_col; pc < r->tr_end.tp_col; pc++) {
325256145Sray			row[pc] = c;
326256145Sray		}
327256145Sray	}
328256145Sray}
329219888Sed
330256145Srayvoid
331256145Srayvtbuf_fill_locked(struct vt_buf *vb, const term_rect_t *r, term_char_t c)
332256145Sray{
333256145Sray	KASSERT(r->tr_begin.tp_row < vb->vb_scr_size.tp_row,
334267538Sray	    ("vtbuf_fill_locked begin.tp_row %d must be < screen height %d",
335256145Sray		r->tr_begin.tp_row, vb->vb_scr_size.tp_row));
336256145Sray	KASSERT(r->tr_begin.tp_col < vb->vb_scr_size.tp_col,
337267538Sray	    ("vtbuf_fill_locked begin.tp_col %d must be < screen width %d",
338256145Sray		r->tr_begin.tp_col, vb->vb_scr_size.tp_col));
339256145Sray
340256145Sray	KASSERT(r->tr_end.tp_row <= vb->vb_scr_size.tp_row,
341267538Sray	    ("vtbuf_fill_locked end.tp_row %d must be <= screen height %d",
342256145Sray		r->tr_end.tp_row, vb->vb_scr_size.tp_row));
343256145Sray	KASSERT(r->tr_end.tp_col <= vb->vb_scr_size.tp_col,
344267538Sray	    ("vtbuf_fill_locked end.tp_col %d must be <= screen width %d",
345256145Sray		r->tr_end.tp_col, vb->vb_scr_size.tp_col));
346256145Sray
347256145Sray	VTBUF_LOCK(vb);
348256145Sray	vtbuf_fill(vb, r, c);
349270182Sdumbbell	vtbuf_dirty_locked(vb, r);
350256145Sray	VTBUF_UNLOCK(vb);
351219888Sed}
352219888Sed
353256145Sraystatic void
354256145Srayvtbuf_init_rows(struct vt_buf *vb)
355256145Sray{
356256145Sray	int r;
357256145Sray
358256145Sray	vb->vb_history_size = MAX(vb->vb_history_size, vb->vb_scr_size.tp_row);
359256145Sray
360256145Sray	for (r = 0; r < vb->vb_history_size; r++)
361268037Smarius		vb->vb_rows[r] = &vb->vb_buffer[r * vb->vb_scr_size.tp_col];
362256145Sray}
363256145Sray
364219888Sedvoid
365219888Sedvtbuf_init_early(struct vt_buf *vb)
366219888Sed{
367268037Smarius	term_rect_t rect;
368219888Sed
369219888Sed	vb->vb_flags |= VBF_CURSOR;
370256145Sray	vb->vb_roffset = 0;
371256145Sray	vb->vb_curroffset = 0;
372258090Sray	vb->vb_mark_start.tp_row = 0;
373258090Sray	vb->vb_mark_start.tp_col = 0;
374258090Sray	vb->vb_mark_end.tp_row = 0;
375258090Sray	vb->vb_mark_end.tp_col = 0;
376256145Sray
377256145Sray	vtbuf_init_rows(vb);
378268037Smarius	rect.tr_begin.tp_row = rect.tr_begin.tp_col = 0;
379271128Semaste	rect.tr_end.tp_col = vb->vb_scr_size.tp_col;
380271128Semaste	rect.tr_end.tp_row = vb->vb_history_size;
381271128Semaste	vtbuf_fill(vb, &rect, VTBUF_SPACE_CHAR(TERMINAL_NORM_ATTR));
382219888Sed	vtbuf_make_undirty(vb);
383256145Sray	if ((vb->vb_flags & VBF_MTX_INIT) == 0) {
384256145Sray		mtx_init(&vb->vb_lock, "vtbuf", NULL, MTX_SPIN);
385256145Sray		vb->vb_flags |= VBF_MTX_INIT;
386256145Sray	}
387219888Sed}
388219888Sed
389219888Sedvoid
390219888Sedvtbuf_init(struct vt_buf *vb, const term_pos_t *p)
391219888Sed{
392256145Sray	int sz;
393219888Sed
394256145Sray	vb->vb_scr_size = *p;
395256145Sray	vb->vb_history_size = VBF_DEFAULT_HISTORY_SIZE;
396256145Sray
397256145Sray	if ((vb->vb_flags & VBF_STATIC) == 0) {
398256145Sray		sz = vb->vb_history_size * p->tp_col * sizeof(term_char_t);
399256894Sray		vb->vb_buffer = malloc(sz, M_VTBUF, M_WAITOK | M_ZERO);
400256145Sray
401256145Sray		sz = vb->vb_history_size * sizeof(term_char_t *);
402256894Sray		vb->vb_rows = malloc(sz, M_VTBUF, M_WAITOK | M_ZERO);
403256145Sray	}
404256145Sray
405219888Sed	vtbuf_init_early(vb);
406219888Sed}
407219888Sed
408219888Sedvoid
409256145Srayvtbuf_sethistory_size(struct vt_buf *vb, int size)
410219888Sed{
411256145Sray	term_pos_t p;
412219888Sed
413256145Sray	/* With same size */
414256145Sray	p.tp_row = vb->vb_scr_size.tp_row;
415256145Sray	p.tp_col = vb->vb_scr_size.tp_col;
416256145Sray	vtbuf_grow(vb, &p, size);
417256145Sray}
418256145Sray
419256145Srayvoid
420271128Semastevtbuf_grow(struct vt_buf *vb, const term_pos_t *p, unsigned int history_size)
421256145Sray{
422271973Sdumbbell	term_char_t *old, *new, **rows, **oldrows, **copyrows, *row, *oldrow;
423271973Sdumbbell	int bufsize, rowssize, w, h, c, r, history_was_full;
424271973Sdumbbell	unsigned int old_history_size;
425256145Sray	term_rect_t rect;
426256145Sray
427256145Sray	history_size = MAX(history_size, p->tp_row);
428256145Sray
429271973Sdumbbell	/* Allocate new buffer. */
430271973Sdumbbell	bufsize = history_size * p->tp_col * sizeof(term_char_t);
431271973Sdumbbell	new = malloc(bufsize, M_VTBUF, M_WAITOK | M_ZERO);
432271973Sdumbbell	rowssize = history_size * sizeof(term_pos_t *);
433271973Sdumbbell	rows = malloc(rowssize, M_VTBUF, M_WAITOK | M_ZERO);
434219888Sed
435271973Sdumbbell	/* Toggle it. */
436271973Sdumbbell	VTBUF_LOCK(vb);
437271973Sdumbbell	old = vb->vb_flags & VBF_STATIC ? NULL : vb->vb_buffer;
438271973Sdumbbell	oldrows = vb->vb_flags & VBF_STATIC ? NULL : vb->vb_rows;
439271973Sdumbbell	copyrows = vb->vb_rows;
440256145Sray
441271973Sdumbbell	w = vb->vb_scr_size.tp_col;
442271973Sdumbbell	h = vb->vb_scr_size.tp_row;
443271973Sdumbbell	old_history_size = vb->vb_history_size;
444271973Sdumbbell	history_was_full = vb->vb_flags & VBF_HISTORY_FULL;
445256145Sray
446271973Sdumbbell	vb->vb_history_size = history_size;
447271973Sdumbbell	vb->vb_buffer = new;
448271973Sdumbbell	vb->vb_rows = rows;
449271973Sdumbbell	vb->vb_flags &= ~VBF_STATIC;
450271973Sdumbbell	vb->vb_scr_size = *p;
451271973Sdumbbell	vtbuf_init_rows(vb);
452271973Sdumbbell
453271973Sdumbbell	/* Copy history and fill extra space if needed. */
454271973Sdumbbell	if (history_size > old_history_size) {
455271973Sdumbbell		/*
456271973Sdumbbell		 * Copy rows to the new buffer. The first row in the history
457271973Sdumbbell		 * is back to index 0, ie. the new buffer doesn't cycle.
458271973Sdumbbell		 *
459271973Sdumbbell		 * The rest of the new buffer is initialized with blank
460271973Sdumbbell		 * content.
461271973Sdumbbell		 */
462271973Sdumbbell		for (r = 0; r < old_history_size; r ++) {
463271973Sdumbbell			row = rows[r];
464271973Sdumbbell
465271973Sdumbbell			/* Compute the corresponding row in the old buffer. */
466271973Sdumbbell			if (history_was_full)
467271973Sdumbbell				/*
468271973Sdumbbell				 * The buffer is full, the "top" row is
469271973Sdumbbell				 * the one just after the viewable area
470271973Sdumbbell				 * (curroffset + viewable height) in the
471271973Sdumbbell				 * cycling buffer. The corresponding row
472271973Sdumbbell				 * is computed from this top row.
473271973Sdumbbell				 */
474271973Sdumbbell				oldrow = copyrows[
475271973Sdumbbell				    (vb->vb_curroffset + h + r) %
476271973Sdumbbell				    old_history_size];
477271973Sdumbbell			else
478271973Sdumbbell				/*
479271973Sdumbbell				 * The buffer is not full, therefore,
480271973Sdumbbell				 * we didn't cycle already. The
481271973Sdumbbell				 * corresponding rows are the same in
482271973Sdumbbell				 * both buffers.
483271973Sdumbbell				 */
484271973Sdumbbell				oldrow = copyrows[r];
485271973Sdumbbell
486271973Sdumbbell			memmove(row, oldrow,
487271973Sdumbbell			    MIN(p->tp_col, w) * sizeof(term_char_t));
488271973Sdumbbell
489271973Sdumbbell			/*
490271973Sdumbbell			 * XXX VTBUF_SPACE_CHAR(TERMINAL_NORM_ATTR) will
491271973Sdumbbell			 * extended lines of kernel text using the wrong
492271973Sdumbbell			 * background color.
493271973Sdumbbell			 */
494271973Sdumbbell			for (c = MIN(p->tp_col, w); c < p->tp_col; c++) {
495271973Sdumbbell				row[c] = VTBUF_SPACE_CHAR(TERMINAL_NORM_ATTR);
496271973Sdumbbell			}
497271973Sdumbbell		}
498271973Sdumbbell
499271973Sdumbbell		/* Fill remaining rows. */
500271973Sdumbbell		rect.tr_begin.tp_col = 0;
501271973Sdumbbell		rect.tr_begin.tp_row = old_history_size;
502271973Sdumbbell		rect.tr_end.tp_col = p->tp_col;
503271973Sdumbbell		rect.tr_end.tp_row = p->tp_row;
504271973Sdumbbell		vtbuf_fill(vb, &rect, VTBUF_SPACE_CHAR(TERMINAL_NORM_ATTR));
505271973Sdumbbell
506271973Sdumbbell		vb->vb_flags &= ~VBF_HISTORY_FULL;
507271973Sdumbbell	} else {
508271973Sdumbbell		/*
509271973Sdumbbell		 * Copy rows to the new buffer. The first row in the history
510271973Sdumbbell		 * is back to index 0, ie. the new buffer doesn't cycle.
511271973Sdumbbell		 *
512271973Sdumbbell		 * (old_history_size - history_size) lines of history are
513271973Sdumbbell		 * dropped.
514271973Sdumbbell		 */
515256145Sray		for (r = 0; r < history_size; r ++) {
516271973Sdumbbell			row = rows[r];
517271973Sdumbbell
518268037Smarius			/*
519271973Sdumbbell			 * Compute the corresponding row in the old buffer.
520271973Sdumbbell			 *
521271973Sdumbbell			 * See the equivalent if{} block above for an
522271973Sdumbbell			 * explanation.
523271973Sdumbbell			 */
524271973Sdumbbell			if (history_was_full)
525271973Sdumbbell				oldrow = copyrows[
526271973Sdumbbell				    (vb->vb_curroffset + h + r +
527271973Sdumbbell				     (old_history_size - history_size)) %
528271973Sdumbbell				    old_history_size];
529271973Sdumbbell			else
530271973Sdumbbell				oldrow = copyrows[
531271973Sdumbbell				    (r + (old_history_size - history_size)) %
532271973Sdumbbell				    old_history_size];
533271973Sdumbbell
534271973Sdumbbell			memmove(row, oldrow,
535271973Sdumbbell			    MIN(p->tp_col, w) * sizeof(term_char_t));
536271973Sdumbbell
537271973Sdumbbell			/*
538268037Smarius			 * XXX VTBUF_SPACE_CHAR(TERMINAL_NORM_ATTR) will
539268037Smarius			 * extended lines of kernel text using the wrong
540268037Smarius			 * background color.
541268037Smarius			 */
542271973Sdumbbell			for (c = MIN(p->tp_col, w); c < p->tp_col; c++) {
543271973Sdumbbell				row[c] = VTBUF_SPACE_CHAR(TERMINAL_NORM_ATTR);
544256145Sray			}
545256145Sray		}
546271973Sdumbbell
547271973Sdumbbell		if (!history_was_full &&
548271973Sdumbbell		    (vb->vb_curroffset + h) >= history_size)
549271973Sdumbbell			vb->vb_flags |= VBF_HISTORY_FULL;
550219888Sed	}
551271973Sdumbbell
552271973Sdumbbell	/*
553271973Sdumbbell	 * If the screen is already filled (there are non-visible lines
554271973Sdumbbell	 * above the current viewable area), adjust curroffset to the
555271973Sdumbbell	 * new viewable area.
556271973Sdumbbell	 */
557271973Sdumbbell	if (!history_was_full && vb->vb_curroffset > 0) {
558271973Sdumbbell		vb->vb_curroffset = vb->vb_curroffset + h - p->tp_row;
559271973Sdumbbell		if (vb->vb_curroffset < 0)
560271973Sdumbbell			vb->vb_curroffset += vb->vb_history_size;
561271973Sdumbbell		vb->vb_curroffset %= vb->vb_history_size;
562271973Sdumbbell		vb->vb_roffset = vb->vb_curroffset;
563271973Sdumbbell	}
564271973Sdumbbell
565271973Sdumbbell	vtbuf_make_undirty(vb);
566271973Sdumbbell	VTBUF_UNLOCK(vb);
567271973Sdumbbell
568271973Sdumbbell	/* Deallocate old buffer. */
569271973Sdumbbell	free(old, M_VTBUF);
570271973Sdumbbell	free(oldrows, M_VTBUF);
571219888Sed}
572219888Sed
573219888Sedvoid
574219888Sedvtbuf_putchar(struct vt_buf *vb, const term_pos_t *p, term_char_t c)
575219888Sed{
576256145Sray	term_char_t *row;
577219888Sed
578256145Sray	KASSERT(p->tp_row < vb->vb_scr_size.tp_row,
579256145Sray	    ("vtbuf_putchar tp_row %d must be less than screen width %d",
580256145Sray		p->tp_row, vb->vb_scr_size.tp_row));
581256145Sray	KASSERT(p->tp_col < vb->vb_scr_size.tp_col,
582256145Sray	    ("vtbuf_putchar tp_col %d must be less than screen height %d",
583256145Sray		p->tp_col, vb->vb_scr_size.tp_col));
584256145Sray
585256145Sray	row = vb->vb_rows[(vb->vb_curroffset + p->tp_row) %
586256145Sray	    VTBUF_MAX_HEIGHT(vb)];
587256145Sray	if (row[p->tp_col] != c) {
588256145Sray		VTBUF_LOCK(vb);
589256145Sray		row[p->tp_col] = c;
590270182Sdumbbell		vtbuf_dirty_cell_locked(vb, p);
591256145Sray		VTBUF_UNLOCK(vb);
592219888Sed	}
593219888Sed}
594219888Sed
595219888Sedvoid
596219888Sedvtbuf_cursor_position(struct vt_buf *vb, const term_pos_t *p)
597219888Sed{
598219888Sed
599219888Sed	if (vb->vb_flags & VBF_CURSOR) {
600270182Sdumbbell		VTBUF_LOCK(vb);
601270182Sdumbbell		vtbuf_dirty_cell_locked(vb, &vb->vb_cursor);
602219888Sed		vb->vb_cursor = *p;
603270182Sdumbbell		vtbuf_dirty_cell_locked(vb, &vb->vb_cursor);
604270182Sdumbbell		VTBUF_UNLOCK(vb);
605219888Sed	} else {
606219888Sed		vb->vb_cursor = *p;
607219888Sed	}
608219888Sed}
609219888Sed
610262861Sjhb#ifndef SC_NO_CUTPASTE
611258090Sraystatic void
612258090Srayvtbuf_flush_mark(struct vt_buf *vb)
613258090Sray{
614258090Sray	term_rect_t area;
615258090Sray	int s, e;
616258090Sray
617258090Sray	/* Notify renderer to update marked region. */
618258090Sray	if (vb->vb_mark_start.tp_col || vb->vb_mark_end.tp_col ||
619258090Sray	    vb->vb_mark_start.tp_row || vb->vb_mark_end.tp_row) {
620258090Sray
621258090Sray		s = vtbuf_htw(vb, vb->vb_mark_start.tp_row);
622258090Sray		e = vtbuf_htw(vb, vb->vb_mark_end.tp_row);
623258090Sray
624258090Sray		area.tr_begin.tp_col = 0;
625258090Sray		area.tr_begin.tp_row = MIN(s, e);
626258090Sray
627258090Sray		area.tr_end.tp_col = vb->vb_scr_size.tp_col;
628258090Sray		area.tr_end.tp_row = MAX(s, e) + 1;
629258090Sray
630258090Sray		vtbuf_dirty(vb, &area);
631258090Sray	}
632258090Sray}
633258090Sray
634258090Srayint
635258090Srayvtbuf_get_marked_len(struct vt_buf *vb)
636258090Sray{
637258090Sray	int ei, si, sz;
638258090Sray	term_pos_t s, e;
639258090Sray
640258090Sray	/* Swap according to window coordinates. */
641258134Sray	if (POS_INDEX(vtbuf_htw(vb, vb->vb_mark_start.tp_row),
642258134Sray	    vb->vb_mark_start.tp_col) >
643258134Sray	    POS_INDEX(vtbuf_htw(vb, vb->vb_mark_end.tp_row),
644258134Sray	    vb->vb_mark_end.tp_col)) {
645258090Sray		POS_COPY(e, vb->vb_mark_start);
646258090Sray		POS_COPY(s, vb->vb_mark_end);
647258090Sray	} else {
648258090Sray		POS_COPY(s, vb->vb_mark_start);
649258090Sray		POS_COPY(e, vb->vb_mark_end);
650258090Sray	}
651258090Sray
652258090Sray	si = s.tp_row * vb->vb_scr_size.tp_col + s.tp_col;
653258090Sray	ei = e.tp_row * vb->vb_scr_size.tp_col + e.tp_col;
654258090Sray
655258090Sray	/* Number symbols and number of rows to inject \n */
656271952Sray	sz = ei - si + ((e.tp_row - s.tp_row) * 2);
657258090Sray
658258090Sray	return (sz * sizeof(term_char_t));
659258090Sray}
660258090Sray
661257971Srayvoid
662258090Srayvtbuf_extract_marked(struct vt_buf *vb, term_char_t *buf, int sz)
663258090Sray{
664258090Sray	int i, r, c, cs, ce;
665258090Sray	term_pos_t s, e;
666258090Sray
667258090Sray	/* Swap according to window coordinates. */
668258134Sray	if (POS_INDEX(vtbuf_htw(vb, vb->vb_mark_start.tp_row),
669258134Sray	    vb->vb_mark_start.tp_col) >
670258134Sray	    POS_INDEX(vtbuf_htw(vb, vb->vb_mark_end.tp_row),
671258134Sray	    vb->vb_mark_end.tp_col)) {
672258090Sray		POS_COPY(e, vb->vb_mark_start);
673258090Sray		POS_COPY(s, vb->vb_mark_end);
674258090Sray	} else {
675258090Sray		POS_COPY(s, vb->vb_mark_start);
676258090Sray		POS_COPY(e, vb->vb_mark_end);
677258090Sray	}
678258090Sray
679258090Sray	i = 0;
680258090Sray	for (r = s.tp_row; r <= e.tp_row; r ++) {
681258090Sray		cs = (r == s.tp_row)?s.tp_col:0;
682258090Sray		ce = (r == e.tp_row)?e.tp_col:vb->vb_scr_size.tp_col;
683258090Sray		for (c = cs; c < ce; c ++) {
684258090Sray			buf[i++] = vb->vb_rows[r][c];
685258090Sray		}
686258093Sray		/* Add new line for all rows, but not for last one. */
687258093Sray		if (r != e.tp_row) {
688258093Sray			buf[i++] = '\r';
689258093Sray			buf[i++] = '\n';
690258093Sray		}
691258090Sray	}
692258090Sray}
693258090Sray
694258090Srayint
695257971Srayvtbuf_set_mark(struct vt_buf *vb, int type, int col, int row)
696257971Sray{
697258136Sray	term_char_t *r;
698258136Sray	int i;
699257971Sray
700257971Sray	switch (type) {
701258130Sray	case VTB_MARK_END:	/* B1 UP */
702258130Sray		if (vb->vb_mark_last != VTB_MARK_MOVE)
703258130Sray			return (0);
704258130Sray		/* FALLTHROUGH */
705258130Sray	case VTB_MARK_MOVE:
706257971Sray	case VTB_MARK_EXTEND:
707258090Sray		vtbuf_flush_mark(vb); /* Clean old mark. */
708257971Sray		vb->vb_mark_end.tp_col = col;
709258090Sray		vb->vb_mark_end.tp_row = vtbuf_wth(vb, row);
710257971Sray		break;
711257971Sray	case VTB_MARK_START:
712258090Sray		vtbuf_flush_mark(vb); /* Clean old mark. */
713257971Sray		vb->vb_mark_start.tp_col = col;
714258090Sray		vb->vb_mark_start.tp_row = vtbuf_wth(vb, row);
715257971Sray		/* Start again, so clear end point. */
716258090Sray		vb->vb_mark_end.tp_col = col;
717258090Sray		vb->vb_mark_end.tp_row = vtbuf_wth(vb, row);
718257971Sray		break;
719257971Sray	case VTB_MARK_WORD:
720258090Sray		vtbuf_flush_mark(vb); /* Clean old mark. */
721258090Sray		vb->vb_mark_start.tp_row = vb->vb_mark_end.tp_row =
722258090Sray		    vtbuf_wth(vb, row);
723258136Sray		r = vb->vb_rows[vb->vb_mark_start.tp_row];
724258136Sray		for (i = col; i >= 0; i --) {
725258136Sray			if (TCHAR_CHARACTER(r[i]) == ' ') {
726258136Sray				vb->vb_mark_start.tp_col = i + 1;
727258136Sray				break;
728258136Sray			}
729258136Sray		}
730258136Sray		for (i = col; i < vb->vb_scr_size.tp_col; i ++) {
731258136Sray			if (TCHAR_CHARACTER(r[i]) == ' ') {
732258136Sray				vb->vb_mark_end.tp_col = i;
733258136Sray				break;
734258136Sray			}
735258136Sray		}
736258136Sray		if (vb->vb_mark_start.tp_col > vb->vb_mark_end.tp_col)
737258136Sray			vb->vb_mark_start.tp_col = vb->vb_mark_end.tp_col;
738257971Sray		break;
739257971Sray	case VTB_MARK_ROW:
740258090Sray		vtbuf_flush_mark(vb); /* Clean old mark. */
741257971Sray		vb->vb_mark_start.tp_col = 0;
742257971Sray		vb->vb_mark_end.tp_col = vb->vb_scr_size.tp_col;
743258090Sray		vb->vb_mark_start.tp_row = vb->vb_mark_end.tp_row =
744258090Sray		    vtbuf_wth(vb, row);
745257971Sray		break;
746258090Sray	case VTB_MARK_NONE:
747258130Sray		vb->vb_mark_last = type;
748258130Sray		/* FALLTHROUGH */
749258090Sray	default:
750258090Sray		/* panic? */
751258090Sray		return (0);
752257971Sray	}
753258111Sray
754258130Sray	vb->vb_mark_last = type;
755258111Sray	/* Draw new marked region. */
756258111Sray	vtbuf_flush_mark(vb);
757258111Sray	return (1);
758257971Sray}
759262861Sjhb#endif
760257971Sray
761257971Srayvoid
762219888Sedvtbuf_cursor_visibility(struct vt_buf *vb, int yes)
763219888Sed{
764219888Sed	int oflags, nflags;
765219888Sed
766219888Sed	VTBUF_LOCK(vb);
767219888Sed	oflags = vb->vb_flags;
768219888Sed	if (yes)
769219888Sed		vb->vb_flags |= VBF_CURSOR;
770219888Sed	else
771219888Sed		vb->vb_flags &= ~VBF_CURSOR;
772219888Sed	nflags = vb->vb_flags;
773219888Sed
774219888Sed	if (oflags != nflags)
775270182Sdumbbell		vtbuf_dirty_cell_locked(vb, &vb->vb_cursor);
776270182Sdumbbell	VTBUF_UNLOCK(vb);
777219888Sed}
778258090Sray
779258090Srayvoid
780258090Srayvtbuf_scroll_mode(struct vt_buf *vb, int yes)
781258090Sray{
782258090Sray	int oflags, nflags;
783258090Sray
784258090Sray	VTBUF_LOCK(vb);
785258090Sray	oflags = vb->vb_flags;
786258090Sray	if (yes)
787258090Sray		vb->vb_flags |= VBF_SCROLL;
788258090Sray	else
789258090Sray		vb->vb_flags &= ~VBF_SCROLL;
790258090Sray	nflags = vb->vb_flags;
791258090Sray
792258090Sray	if (oflags != nflags)
793270182Sdumbbell		vtbuf_dirty_cell_locked(vb, &vb->vb_cursor);
794270182Sdumbbell	VTBUF_UNLOCK(vb);
795258090Sray}
796258090Sray
797