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