1/*-
2 * Copyright (c) 2009, 2013 The FreeBSD Foundation
3 * All rights reserved.
4 *
5 * This software was developed by Ed Schouten under sponsorship from the
6 * FreeBSD Foundation.
7 *
8 * Portions of this software were developed by Oleksandr Rybalko
9 * under sponsorship from the FreeBSD Foundation.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in the
18 *    documentation and/or other materials provided with the distribution.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33#include <sys/cdefs.h>
34__FBSDID("$FreeBSD: stable/10/sys/dev/vt/vt_buf.c 321200 2017-07-19 13:32:08Z emaste $");
35
36#include <sys/param.h>
37#include <sys/kernel.h>
38#include <sys/lock.h>
39#include <sys/malloc.h>
40#include <sys/mutex.h>
41#include <sys/reboot.h>
42#include <sys/systm.h>
43
44#include <dev/vt/vt.h>
45
46static MALLOC_DEFINE(M_VTBUF, "vtbuf", "vt buffer");
47
48#define	VTBUF_LOCK(vb)		mtx_lock_spin(&(vb)->vb_lock)
49#define	VTBUF_UNLOCK(vb)	mtx_unlock_spin(&(vb)->vb_lock)
50
51#define POS_INDEX(c, r) (((r) << 12) + (c))
52#define	POS_COPY(d, s)	do {	\
53	(d).tp_col = (s).tp_col;	\
54	(d).tp_row = (s).tp_row;	\
55} while (0)
56
57#ifndef SC_NO_CUTPASTE
58static int vtbuf_htw(const struct vt_buf *vb, int row);
59static int vtbuf_wth(const struct vt_buf *vb, int row);
60static int vtbuf_in_this_range(int begin, int test, int end, int sz);
61#endif
62
63/*
64 * line4
65 * line5 <--- curroffset (terminal output to that line)
66 * line0
67 * line1                  <--- roffset (history display from that point)
68 * line2
69 * line3
70 */
71int
72vthistory_seek(struct vt_buf *vb, int offset, int whence)
73{
74	int diff, top, bottom, roffset;
75
76	/* No scrolling if not enabled. */
77	if ((vb->vb_flags & VBF_SCROLL) == 0) {
78		if (vb->vb_roffset != vb->vb_curroffset) {
79			vb->vb_roffset = vb->vb_curroffset;
80			return (0xffff);
81		}
82		return (0); /* No changes */
83	}
84
85	/* "top" may be a negative integer. */
86	bottom = vb->vb_curroffset;
87	top = (vb->vb_flags & VBF_HISTORY_FULL) ?
88	    bottom + vb->vb_scr_size.tp_row - vb->vb_history_size :
89	    0;
90
91	roffset = 0; /* Make gcc happy. */
92	switch (whence) {
93	case VHS_SET:
94		if (offset < 0)
95			offset = 0;
96		roffset = top + offset;
97		break;
98	case VHS_CUR:
99		/*
100		 * Operate on copy of offset value, since it temporary
101		 * can be bigger than amount of rows in buffer.
102		 */
103		roffset = vb->vb_roffset;
104		if (roffset >= bottom + vb->vb_scr_size.tp_row)
105			roffset -= vb->vb_history_size;
106
107		roffset += offset;
108		roffset = MAX(roffset, top);
109		roffset = MIN(roffset, bottom);
110
111		if (roffset < 0)
112			roffset = vb->vb_history_size + roffset;
113
114		break;
115	case VHS_END:
116		/* Go to current offset. */
117		roffset = vb->vb_curroffset;
118		break;
119	}
120
121	diff = vb->vb_roffset != roffset;
122	vb->vb_roffset = roffset;
123
124	return (diff);
125}
126
127void
128vthistory_addlines(struct vt_buf *vb, int offset)
129{
130#ifndef SC_NO_CUTPASTE
131	int cur, sz;
132#endif
133
134	vb->vb_curroffset += offset;
135	if (vb->vb_curroffset < 0)
136		vb->vb_curroffset = 0;
137	if (vb->vb_curroffset + vb->vb_scr_size.tp_row >= vb->vb_history_size)
138		vb->vb_flags |= VBF_HISTORY_FULL;
139	vb->vb_curroffset %= vb->vb_history_size;
140	if ((vb->vb_flags & VBF_SCROLL) == 0) {
141		vb->vb_roffset = vb->vb_curroffset;
142	}
143
144#ifndef SC_NO_CUTPASTE
145	sz = vb->vb_history_size;
146	cur = vb->vb_roffset + vb->vb_scr_size.tp_row + sz - 1;
147	if (vtbuf_in_this_range(cur, vb->vb_mark_start.tp_row, cur + offset, sz) ||
148	    vtbuf_in_this_range(cur, vb->vb_mark_end.tp_row, cur + offset, sz)) {
149		/* clear screen selection */
150		vb->vb_mark_start.tp_row = vb->vb_mark_end.tp_row;
151		vb->vb_mark_start.tp_col = vb->vb_mark_end.tp_col;
152	}
153#endif
154}
155
156void
157vthistory_getpos(const struct vt_buf *vb, unsigned int *offset)
158{
159
160	*offset = vb->vb_roffset;
161}
162
163#ifndef SC_NO_CUTPASTE	/* Only mouse support use it now. */
164/* Translate history row to current view row number. */
165static int
166vtbuf_htw(const struct vt_buf *vb, int row)
167{
168
169	/*
170	 * total 1000 rows.
171	 * History offset	roffset	winrow
172	 *	205		200	((205 - 200 + 1000) % 1000) = 5
173	 *	90		990	((90 - 990 + 1000) % 1000) = 100
174	 */
175	return ((row - vb->vb_roffset + vb->vb_history_size) %
176	    vb->vb_history_size);
177}
178
179/* Translate current view row number to history row. */
180static int
181vtbuf_wth(const struct vt_buf *vb, int row)
182{
183
184	return ((vb->vb_roffset + row) % vb->vb_history_size);
185}
186
187/*
188 * Test if an index in a circular buffer is within a range.
189 *
190 * begin - start index
191 * end - end index
192 * test - test index
193 * sz - size of circular buffer when it turns over
194 */
195static int
196vtbuf_in_this_range(int begin, int test, int end, int sz)
197{
198
199	begin %= sz;
200	end %= sz;
201
202	/* check for inversion */
203	if (begin > end)
204		return (test >= begin || test < end);
205	else
206		return (test >= begin && test < end);
207}
208#endif
209
210int
211vtbuf_iscursor(const struct vt_buf *vb, int row, int col)
212{
213#ifndef SC_NO_CUTPASTE
214	int sc, sr, sz, ec, er, tmp;
215#endif
216
217	if ((vb->vb_flags & (VBF_CURSOR|VBF_SCROLL)) == VBF_CURSOR &&
218	    (vb->vb_cursor.tp_row == row) && (vb->vb_cursor.tp_col == col))
219		return (1);
220
221#ifndef SC_NO_CUTPASTE
222	/* Mark cut/paste region. */
223	if (vb->vb_mark_start.tp_col == vb->vb_mark_end.tp_col &&
224	    vb->vb_mark_start.tp_row == vb->vb_mark_end.tp_row)
225		return (0);
226
227	sc = vb->vb_mark_start.tp_col;
228	sr = vb->vb_mark_start.tp_row;
229	ec = vb->vb_mark_end.tp_col;
230	er = vb->vb_mark_end.tp_row;
231
232	/*
233	 * Information about if the selection was made bottom-top or
234	 * top-bottom is lost due to modulo arithmetics and needs to
235	 * be recovered:
236	 */
237	sz = vb->vb_history_size;
238	tmp = (sz + er - sr) % sz;
239	row = vtbuf_wth(vb, row);
240
241	/* Swap start and end if start > end */
242	if ((2 * tmp) > sz || (tmp == 0 && sc > ec)) {
243		tmp = sc; sc = ec; ec = tmp;
244		tmp = sr; sr = er; er = tmp;
245	}
246
247	if (vtbuf_in_this_range(POS_INDEX(sc, sr), POS_INDEX(col, row),
248	    POS_INDEX(ec, er), POS_INDEX(0, sz)))
249		return (1);
250#endif
251
252	return (0);
253}
254
255static inline void
256vtbuf_dirty_locked(struct vt_buf *vb, const term_rect_t *area)
257{
258
259	if (vb->vb_dirtyrect.tr_begin.tp_row > area->tr_begin.tp_row)
260		vb->vb_dirtyrect.tr_begin.tp_row = area->tr_begin.tp_row;
261	if (vb->vb_dirtyrect.tr_begin.tp_col > area->tr_begin.tp_col)
262		vb->vb_dirtyrect.tr_begin.tp_col = area->tr_begin.tp_col;
263	if (vb->vb_dirtyrect.tr_end.tp_row < area->tr_end.tp_row)
264		vb->vb_dirtyrect.tr_end.tp_row = area->tr_end.tp_row;
265	if (vb->vb_dirtyrect.tr_end.tp_col < area->tr_end.tp_col)
266		vb->vb_dirtyrect.tr_end.tp_col = area->tr_end.tp_col;
267}
268
269void
270vtbuf_dirty(struct vt_buf *vb, const term_rect_t *area)
271{
272
273	VTBUF_LOCK(vb);
274	vtbuf_dirty_locked(vb, area);
275	VTBUF_UNLOCK(vb);
276}
277
278static inline void
279vtbuf_dirty_cell_locked(struct vt_buf *vb, const term_pos_t *p)
280{
281	term_rect_t area;
282
283	area.tr_begin = *p;
284	area.tr_end.tp_row = p->tp_row + 1;
285	area.tr_end.tp_col = p->tp_col + 1;
286	vtbuf_dirty_locked(vb, &area);
287}
288
289static void
290vtbuf_make_undirty(struct vt_buf *vb)
291{
292
293	vb->vb_dirtyrect.tr_begin = vb->vb_scr_size;
294	vb->vb_dirtyrect.tr_end.tp_row = vb->vb_dirtyrect.tr_end.tp_col = 0;
295}
296
297void
298vtbuf_undirty(struct vt_buf *vb, term_rect_t *r)
299{
300
301	VTBUF_LOCK(vb);
302	*r = vb->vb_dirtyrect;
303	vtbuf_make_undirty(vb);
304	VTBUF_UNLOCK(vb);
305}
306
307void
308vtbuf_copy(struct vt_buf *vb, const term_rect_t *r, const term_pos_t *p2)
309{
310	const term_pos_t *p1 = &r->tr_begin;
311	term_rect_t area;
312	unsigned int rows, cols;
313	int pr, rdiff;
314
315	KASSERT(r->tr_begin.tp_row < vb->vb_scr_size.tp_row,
316	    ("vtbuf_copy begin.tp_row %d must be less than screen width %d",
317		r->tr_begin.tp_row, vb->vb_scr_size.tp_row));
318	KASSERT(r->tr_begin.tp_col < vb->vb_scr_size.tp_col,
319	    ("vtbuf_copy begin.tp_col %d must be less than screen height %d",
320		r->tr_begin.tp_col, vb->vb_scr_size.tp_col));
321
322	KASSERT(r->tr_end.tp_row <= vb->vb_scr_size.tp_row,
323	    ("vtbuf_copy end.tp_row %d must be less than screen width %d",
324		r->tr_end.tp_row, vb->vb_scr_size.tp_row));
325	KASSERT(r->tr_end.tp_col <= vb->vb_scr_size.tp_col,
326	    ("vtbuf_copy end.tp_col %d must be less than screen height %d",
327		r->tr_end.tp_col, vb->vb_scr_size.tp_col));
328
329	KASSERT(p2->tp_row < vb->vb_scr_size.tp_row,
330	    ("vtbuf_copy tp_row %d must be less than screen width %d",
331		p2->tp_row, vb->vb_scr_size.tp_row));
332	KASSERT(p2->tp_col < vb->vb_scr_size.tp_col,
333	    ("vtbuf_copy tp_col %d must be less than screen height %d",
334		p2->tp_col, vb->vb_scr_size.tp_col));
335
336	rows = r->tr_end.tp_row - r->tr_begin.tp_row;
337	rdiff = r->tr_begin.tp_row - p2->tp_row;
338	cols = r->tr_end.tp_col - r->tr_begin.tp_col;
339	if (r->tr_begin.tp_row > p2->tp_row && r->tr_begin.tp_col == 0 &&
340	    r->tr_end.tp_col == vb->vb_scr_size.tp_col && /* Full row. */
341	    (rows + rdiff) == vb->vb_scr_size.tp_row && /* Whole screen. */
342	    rdiff > 0) { /* Only forward direction. Do not eat history. */
343		vthistory_addlines(vb, rdiff);
344	} else if (p2->tp_row < p1->tp_row) {
345		/* Handle overlapping copies of line segments. */
346		/* Move data up. */
347		for (pr = 0; pr < rows; pr++)
348			memmove(
349			    &VTBUF_FIELD(vb, p2->tp_row + pr, p2->tp_col),
350			    &VTBUF_FIELD(vb, p1->tp_row + pr, p1->tp_col),
351			    cols * sizeof(term_char_t));
352	} else {
353		/* Move data down. */
354		for (pr = rows - 1; pr >= 0; pr--)
355			memmove(
356			    &VTBUF_FIELD(vb, p2->tp_row + pr, p2->tp_col),
357			    &VTBUF_FIELD(vb, p1->tp_row + pr, p1->tp_col),
358			    cols * sizeof(term_char_t));
359	}
360
361	area.tr_begin = *p2;
362	area.tr_end.tp_row = MIN(p2->tp_row + rows, vb->vb_scr_size.tp_row);
363	area.tr_end.tp_col = MIN(p2->tp_col + cols, vb->vb_scr_size.tp_col);
364	vtbuf_dirty(vb, &area);
365}
366
367static void
368vtbuf_fill(struct vt_buf *vb, const term_rect_t *r, term_char_t c)
369{
370	unsigned int pr, pc;
371	term_char_t *row;
372
373	for (pr = r->tr_begin.tp_row; pr < r->tr_end.tp_row; pr++) {
374		row = vb->vb_rows[(vb->vb_curroffset + pr) %
375		    VTBUF_MAX_HEIGHT(vb)];
376		for (pc = r->tr_begin.tp_col; pc < r->tr_end.tp_col; pc++) {
377			row[pc] = c;
378		}
379	}
380}
381
382void
383vtbuf_fill_locked(struct vt_buf *vb, const term_rect_t *r, term_char_t c)
384{
385	KASSERT(r->tr_begin.tp_row < vb->vb_scr_size.tp_row,
386	    ("vtbuf_fill_locked begin.tp_row %d must be < screen height %d",
387		r->tr_begin.tp_row, vb->vb_scr_size.tp_row));
388	KASSERT(r->tr_begin.tp_col < vb->vb_scr_size.tp_col,
389	    ("vtbuf_fill_locked begin.tp_col %d must be < screen width %d",
390		r->tr_begin.tp_col, vb->vb_scr_size.tp_col));
391
392	KASSERT(r->tr_end.tp_row <= vb->vb_scr_size.tp_row,
393	    ("vtbuf_fill_locked end.tp_row %d must be <= screen height %d",
394		r->tr_end.tp_row, vb->vb_scr_size.tp_row));
395	KASSERT(r->tr_end.tp_col <= vb->vb_scr_size.tp_col,
396	    ("vtbuf_fill_locked end.tp_col %d must be <= screen width %d",
397		r->tr_end.tp_col, vb->vb_scr_size.tp_col));
398
399	VTBUF_LOCK(vb);
400	vtbuf_fill(vb, r, c);
401	vtbuf_dirty_locked(vb, r);
402	VTBUF_UNLOCK(vb);
403}
404
405static void
406vtbuf_init_rows(struct vt_buf *vb)
407{
408	int r;
409
410	vb->vb_history_size = MAX(vb->vb_history_size, vb->vb_scr_size.tp_row);
411
412	for (r = 0; r < vb->vb_history_size; r++)
413		vb->vb_rows[r] = &vb->vb_buffer[r * vb->vb_scr_size.tp_col];
414}
415
416void
417vtbuf_init_early(struct vt_buf *vb)
418{
419	term_rect_t rect;
420
421	vb->vb_flags |= VBF_CURSOR;
422	vb->vb_roffset = 0;
423	vb->vb_curroffset = 0;
424	vb->vb_mark_start.tp_row = 0;
425	vb->vb_mark_start.tp_col = 0;
426	vb->vb_mark_end.tp_row = 0;
427	vb->vb_mark_end.tp_col = 0;
428
429	vtbuf_init_rows(vb);
430	rect.tr_begin.tp_row = rect.tr_begin.tp_col = 0;
431	rect.tr_end.tp_col = vb->vb_scr_size.tp_col;
432	rect.tr_end.tp_row = vb->vb_history_size;
433	vtbuf_fill(vb, &rect, VTBUF_SPACE_CHAR(TERMINAL_NORM_ATTR));
434	vtbuf_make_undirty(vb);
435	if ((vb->vb_flags & VBF_MTX_INIT) == 0) {
436		mtx_init(&vb->vb_lock, "vtbuf", NULL, MTX_SPIN);
437		vb->vb_flags |= VBF_MTX_INIT;
438	}
439}
440
441void
442vtbuf_init(struct vt_buf *vb, const term_pos_t *p)
443{
444	int sz;
445
446	vb->vb_scr_size = *p;
447	vb->vb_history_size = VBF_DEFAULT_HISTORY_SIZE;
448
449	if ((vb->vb_flags & VBF_STATIC) == 0) {
450		sz = vb->vb_history_size * p->tp_col * sizeof(term_char_t);
451		vb->vb_buffer = malloc(sz, M_VTBUF, M_WAITOK | M_ZERO);
452
453		sz = vb->vb_history_size * sizeof(term_char_t *);
454		vb->vb_rows = malloc(sz, M_VTBUF, M_WAITOK | M_ZERO);
455	}
456
457	vtbuf_init_early(vb);
458}
459
460void
461vtbuf_sethistory_size(struct vt_buf *vb, int size)
462{
463	term_pos_t p;
464
465	/* With same size */
466	p.tp_row = vb->vb_scr_size.tp_row;
467	p.tp_col = vb->vb_scr_size.tp_col;
468	vtbuf_grow(vb, &p, size);
469}
470
471void
472vtbuf_grow(struct vt_buf *vb, const term_pos_t *p, unsigned int history_size)
473{
474	term_char_t *old, *new, **rows, **oldrows, **copyrows, *row, *oldrow;
475	int bufsize, rowssize, w, h, c, r, history_was_full;
476	unsigned int old_history_size;
477	term_rect_t rect;
478
479	history_size = MAX(history_size, p->tp_row);
480
481	/* Allocate new buffer. */
482	bufsize = history_size * p->tp_col * sizeof(term_char_t);
483	new = malloc(bufsize, M_VTBUF, M_WAITOK | M_ZERO);
484	rowssize = history_size * sizeof(term_pos_t *);
485	rows = malloc(rowssize, M_VTBUF, M_WAITOK | M_ZERO);
486
487	/* Toggle it. */
488	VTBUF_LOCK(vb);
489	old = vb->vb_flags & VBF_STATIC ? NULL : vb->vb_buffer;
490	oldrows = vb->vb_flags & VBF_STATIC ? NULL : vb->vb_rows;
491	copyrows = vb->vb_rows;
492
493	w = vb->vb_scr_size.tp_col;
494	h = vb->vb_scr_size.tp_row;
495	old_history_size = vb->vb_history_size;
496	history_was_full = vb->vb_flags & VBF_HISTORY_FULL;
497
498	vb->vb_history_size = history_size;
499	vb->vb_buffer = new;
500	vb->vb_rows = rows;
501	vb->vb_flags &= ~VBF_STATIC;
502	vb->vb_scr_size = *p;
503	vtbuf_init_rows(vb);
504
505	/* Copy history and fill extra space if needed. */
506	if (history_size > old_history_size) {
507		/*
508		 * Copy rows to the new buffer. The first row in the history
509		 * is back to index 0, ie. the new buffer doesn't cycle.
510		 *
511		 * The rest of the new buffer is initialized with blank
512		 * content.
513		 */
514		for (r = 0; r < old_history_size; r ++) {
515			row = rows[r];
516
517			/* Compute the corresponding row in the old buffer. */
518			if (history_was_full)
519				/*
520				 * The buffer is full, the "top" row is
521				 * the one just after the viewable area
522				 * (curroffset + viewable height) in the
523				 * cycling buffer. The corresponding row
524				 * is computed from this top row.
525				 */
526				oldrow = copyrows[
527				    (vb->vb_curroffset + h + r) %
528				    old_history_size];
529			else
530				/*
531				 * The buffer is not full, therefore,
532				 * we didn't cycle already. The
533				 * corresponding rows are the same in
534				 * both buffers.
535				 */
536				oldrow = copyrows[r];
537
538			memmove(row, oldrow,
539			    MIN(p->tp_col, w) * sizeof(term_char_t));
540
541			/*
542			 * XXX VTBUF_SPACE_CHAR(TERMINAL_NORM_ATTR) will
543			 * extended lines of kernel text using the wrong
544			 * background color.
545			 */
546			for (c = MIN(p->tp_col, w); c < p->tp_col; c++) {
547				row[c] = VTBUF_SPACE_CHAR(TERMINAL_NORM_ATTR);
548			}
549		}
550
551		/* Fill remaining rows. */
552		rect.tr_begin.tp_col = 0;
553		rect.tr_begin.tp_row = old_history_size;
554		rect.tr_end.tp_col = p->tp_col;
555		rect.tr_end.tp_row = p->tp_row;
556		vtbuf_fill(vb, &rect, VTBUF_SPACE_CHAR(TERMINAL_NORM_ATTR));
557
558		vb->vb_flags &= ~VBF_HISTORY_FULL;
559	} else {
560		/*
561		 * Copy rows to the new buffer. The first row in the history
562		 * is back to index 0, ie. the new buffer doesn't cycle.
563		 *
564		 * (old_history_size - history_size) lines of history are
565		 * dropped.
566		 */
567		for (r = 0; r < history_size; r ++) {
568			row = rows[r];
569
570			/*
571			 * Compute the corresponding row in the old buffer.
572			 *
573			 * See the equivalent if{} block above for an
574			 * explanation.
575			 */
576			if (history_was_full)
577				oldrow = copyrows[
578				    (vb->vb_curroffset + h + r +
579				     (old_history_size - history_size)) %
580				    old_history_size];
581			else
582				oldrow = copyrows[
583				    (r + (old_history_size - history_size)) %
584				    old_history_size];
585
586			memmove(row, oldrow,
587			    MIN(p->tp_col, w) * sizeof(term_char_t));
588
589			/*
590			 * XXX VTBUF_SPACE_CHAR(TERMINAL_NORM_ATTR) will
591			 * extended lines of kernel text using the wrong
592			 * background color.
593			 */
594			for (c = MIN(p->tp_col, w); c < p->tp_col; c++) {
595				row[c] = VTBUF_SPACE_CHAR(TERMINAL_NORM_ATTR);
596			}
597		}
598
599		if (!history_was_full &&
600		    (vb->vb_curroffset + h) >= history_size)
601			vb->vb_flags |= VBF_HISTORY_FULL;
602	}
603
604	/*
605	 * If the screen is already filled (there are non-visible lines
606	 * above the current viewable area), adjust curroffset to the
607	 * new viewable area.
608	 */
609	if (!history_was_full && vb->vb_curroffset > 0) {
610		vb->vb_curroffset = vb->vb_curroffset + h - p->tp_row;
611		if (vb->vb_curroffset < 0)
612			vb->vb_curroffset += vb->vb_history_size;
613		vb->vb_curroffset %= vb->vb_history_size;
614		vb->vb_roffset = vb->vb_curroffset;
615	}
616
617	/* Adjust cursor position. */
618	if (vb->vb_cursor.tp_col > p->tp_col - 1)
619		/*
620		 * Move cursor to the last column, in case its previous
621		 * position is outside of the new screen area.
622		 */
623		vb->vb_cursor.tp_col = p->tp_col - 1;
624
625	if (vb->vb_curroffset > 0 || vb->vb_cursor.tp_row > p->tp_row - 1)
626		/* Move cursor to the last line on the screen. */
627		vb->vb_cursor.tp_row = p->tp_row - 1;
628
629	vtbuf_make_undirty(vb);
630	VTBUF_UNLOCK(vb);
631
632	/* Deallocate old buffer. */
633	free(old, M_VTBUF);
634	free(oldrows, M_VTBUF);
635}
636
637void
638vtbuf_putchar(struct vt_buf *vb, const term_pos_t *p, term_char_t c)
639{
640	term_char_t *row;
641
642	KASSERT(p->tp_row < vb->vb_scr_size.tp_row,
643	    ("vtbuf_putchar tp_row %d must be less than screen width %d",
644		p->tp_row, vb->vb_scr_size.tp_row));
645	KASSERT(p->tp_col < vb->vb_scr_size.tp_col,
646	    ("vtbuf_putchar tp_col %d must be less than screen height %d",
647		p->tp_col, vb->vb_scr_size.tp_col));
648
649	row = vb->vb_rows[(vb->vb_curroffset + p->tp_row) %
650	    VTBUF_MAX_HEIGHT(vb)];
651	if (row[p->tp_col] != c) {
652		VTBUF_LOCK(vb);
653		row[p->tp_col] = c;
654		vtbuf_dirty_cell_locked(vb, p);
655		VTBUF_UNLOCK(vb);
656	}
657}
658
659void
660vtbuf_cursor_position(struct vt_buf *vb, const term_pos_t *p)
661{
662
663	if (vb->vb_flags & VBF_CURSOR) {
664		VTBUF_LOCK(vb);
665		vtbuf_dirty_cell_locked(vb, &vb->vb_cursor);
666		vb->vb_cursor = *p;
667		vtbuf_dirty_cell_locked(vb, &vb->vb_cursor);
668		VTBUF_UNLOCK(vb);
669	} else {
670		vb->vb_cursor = *p;
671	}
672}
673
674#ifndef SC_NO_CUTPASTE
675static void
676vtbuf_flush_mark(struct vt_buf *vb)
677{
678	term_rect_t area;
679	int s, e;
680
681	/* Notify renderer to update marked region. */
682	if ((vb->vb_mark_start.tp_col != vb->vb_mark_end.tp_col) ||
683	    (vb->vb_mark_start.tp_row != vb->vb_mark_end.tp_row)) {
684
685		s = vtbuf_htw(vb, vb->vb_mark_start.tp_row);
686		e = vtbuf_htw(vb, vb->vb_mark_end.tp_row);
687
688		area.tr_begin.tp_col = 0;
689		area.tr_begin.tp_row = MIN(s, e);
690
691		area.tr_end.tp_col = vb->vb_scr_size.tp_col;
692		area.tr_end.tp_row = MAX(s, e) + 1;
693
694		vtbuf_dirty(vb, &area);
695	}
696}
697
698int
699vtbuf_get_marked_len(struct vt_buf *vb)
700{
701	int ei, si, sz;
702	term_pos_t s, e;
703
704	/* Swap according to window coordinates. */
705	if (POS_INDEX(vtbuf_htw(vb, vb->vb_mark_start.tp_row),
706	    vb->vb_mark_start.tp_col) >
707	    POS_INDEX(vtbuf_htw(vb, vb->vb_mark_end.tp_row),
708	    vb->vb_mark_end.tp_col)) {
709		POS_COPY(e, vb->vb_mark_start);
710		POS_COPY(s, vb->vb_mark_end);
711	} else {
712		POS_COPY(s, vb->vb_mark_start);
713		POS_COPY(e, vb->vb_mark_end);
714	}
715
716	si = s.tp_row * vb->vb_scr_size.tp_col + s.tp_col;
717	ei = e.tp_row * vb->vb_scr_size.tp_col + e.tp_col;
718
719	/* Number symbols and number of rows to inject \n */
720	sz = ei - si + ((e.tp_row - s.tp_row) * 2);
721
722	return (sz * sizeof(term_char_t));
723}
724
725void
726vtbuf_extract_marked(struct vt_buf *vb, term_char_t *buf, int sz)
727{
728	int i, r, c, cs, ce;
729	term_pos_t s, e;
730
731	/* Swap according to window coordinates. */
732	if (POS_INDEX(vtbuf_htw(vb, vb->vb_mark_start.tp_row),
733	    vb->vb_mark_start.tp_col) >
734	    POS_INDEX(vtbuf_htw(vb, vb->vb_mark_end.tp_row),
735	    vb->vb_mark_end.tp_col)) {
736		POS_COPY(e, vb->vb_mark_start);
737		POS_COPY(s, vb->vb_mark_end);
738	} else {
739		POS_COPY(s, vb->vb_mark_start);
740		POS_COPY(e, vb->vb_mark_end);
741	}
742
743	i = 0;
744	for (r = s.tp_row; r <= e.tp_row; r ++) {
745		cs = (r == s.tp_row)?s.tp_col:0;
746		ce = (r == e.tp_row)?e.tp_col:vb->vb_scr_size.tp_col;
747		for (c = cs; c < ce; c ++) {
748			buf[i++] = vb->vb_rows[r][c];
749		}
750		/* Add new line for all rows, but not for last one. */
751		if (r != e.tp_row) {
752			buf[i++] = '\r';
753			buf[i++] = '\n';
754		}
755	}
756}
757
758int
759vtbuf_set_mark(struct vt_buf *vb, int type, int col, int row)
760{
761	term_char_t *r;
762	int i;
763
764	switch (type) {
765	case VTB_MARK_END:	/* B1 UP */
766		if (vb->vb_mark_last != VTB_MARK_MOVE)
767			return (0);
768		/* FALLTHROUGH */
769	case VTB_MARK_MOVE:
770	case VTB_MARK_EXTEND:
771		vtbuf_flush_mark(vb); /* Clean old mark. */
772		vb->vb_mark_end.tp_col = col;
773		vb->vb_mark_end.tp_row = vtbuf_wth(vb, row);
774		break;
775	case VTB_MARK_START:
776		vtbuf_flush_mark(vb); /* Clean old mark. */
777		vb->vb_mark_start.tp_col = col;
778		vb->vb_mark_start.tp_row = vtbuf_wth(vb, row);
779		/* Start again, so clear end point. */
780		vb->vb_mark_end.tp_col = col;
781		vb->vb_mark_end.tp_row = vtbuf_wth(vb, row);
782		break;
783	case VTB_MARK_WORD:
784		vtbuf_flush_mark(vb); /* Clean old mark. */
785		vb->vb_mark_start.tp_row = vb->vb_mark_end.tp_row =
786		    vtbuf_wth(vb, row);
787		r = vb->vb_rows[vb->vb_mark_start.tp_row];
788		for (i = col; i >= 0; i --) {
789			if (TCHAR_CHARACTER(r[i]) == ' ') {
790				vb->vb_mark_start.tp_col = i + 1;
791				break;
792			}
793		}
794		for (i = col; i < vb->vb_scr_size.tp_col; i ++) {
795			if (TCHAR_CHARACTER(r[i]) == ' ') {
796				vb->vb_mark_end.tp_col = i;
797				break;
798			}
799		}
800		if (vb->vb_mark_start.tp_col > vb->vb_mark_end.tp_col)
801			vb->vb_mark_start.tp_col = vb->vb_mark_end.tp_col;
802		break;
803	case VTB_MARK_ROW:
804		vtbuf_flush_mark(vb); /* Clean old mark. */
805		vb->vb_mark_start.tp_col = 0;
806		vb->vb_mark_end.tp_col = vb->vb_scr_size.tp_col;
807		vb->vb_mark_start.tp_row = vb->vb_mark_end.tp_row =
808		    vtbuf_wth(vb, row);
809		break;
810	case VTB_MARK_NONE:
811		vb->vb_mark_last = type;
812		/* FALLTHROUGH */
813	default:
814		/* panic? */
815		return (0);
816	}
817
818	vb->vb_mark_last = type;
819	/* Draw new marked region. */
820	vtbuf_flush_mark(vb);
821	return (1);
822}
823#endif
824
825void
826vtbuf_cursor_visibility(struct vt_buf *vb, int yes)
827{
828	int oflags, nflags;
829
830	VTBUF_LOCK(vb);
831	oflags = vb->vb_flags;
832	if (yes)
833		vb->vb_flags |= VBF_CURSOR;
834	else
835		vb->vb_flags &= ~VBF_CURSOR;
836	nflags = vb->vb_flags;
837
838	if (oflags != nflags)
839		vtbuf_dirty_cell_locked(vb, &vb->vb_cursor);
840	VTBUF_UNLOCK(vb);
841}
842
843void
844vtbuf_scroll_mode(struct vt_buf *vb, int yes)
845{
846	int oflags, nflags;
847
848	VTBUF_LOCK(vb);
849	oflags = vb->vb_flags;
850	if (yes)
851		vb->vb_flags |= VBF_SCROLL;
852	else
853		vb->vb_flags &= ~VBF_SCROLL;
854	nflags = vb->vb_flags;
855
856	if (oflags != nflags)
857		vtbuf_dirty_cell_locked(vb, &vb->vb_cursor);
858	VTBUF_UNLOCK(vb);
859}
860
861