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