vt_buf.c revision 219888
1219888Sed/*-
2219888Sed * Copyright (c) 2009 The FreeBSD Foundation
3219888Sed * All rights reserved.
4219888Sed *
5219888Sed * This software was developed by Ed Schouten under sponsorship from the
6219888Sed * FreeBSD Foundation.
7219888Sed *
8219888Sed * Redistribution and use in source and binary forms, with or without
9219888Sed * modification, are permitted provided that the following conditions
10219888Sed * are met:
11219888Sed * 1. Redistributions of source code must retain the above copyright
12219888Sed *    notice, this list of conditions and the following disclaimer.
13219888Sed * 2. Redistributions in binary form must reproduce the above copyright
14219888Sed *    notice, this list of conditions and the following disclaimer in the
15219888Sed *    documentation and/or other materials provided with the distribution.
16219888Sed *
17219888Sed * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18219888Sed * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19219888Sed * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20219888Sed * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21219888Sed * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22219888Sed * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23219888Sed * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24219888Sed * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25219888Sed * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26219888Sed * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27219888Sed * SUCH DAMAGE.
28219888Sed */
29219888Sed
30219888Sed#include <sys/cdefs.h>
31219888Sed__FBSDID("$FreeBSD: user/ed/newcons/sys/dev/vt/vt_buf.c 219888 2011-03-22 21:31:31Z ed $");
32219888Sed
33219888Sed#include <sys/param.h>
34219888Sed#include <sys/kernel.h>
35219888Sed#include <sys/lock.h>
36219888Sed#include <sys/malloc.h>
37219888Sed#include <sys/mutex.h>
38219888Sed#include <sys/systm.h>
39219888Sed
40219888Sed#include <dev/vt/vt.h>
41219888Sed
42219888Sedstatic MALLOC_DEFINE(M_VTBUF, "vtbuf", "vt buffer");
43219888Sed
44219888Sed#define	VTBUF_LOCK(vb)		mtx_lock_spin(&(vb)->vb_lock)
45219888Sed#define	VTBUF_UNLOCK(vb)	mtx_unlock_spin(&(vb)->vb_lock)
46219888Sed
47219888Sedstatic inline uint64_t
48219888Sedvtbuf_dirty_axis(unsigned int begin, unsigned int end)
49219888Sed{
50219888Sed	uint64_t left, right, mask;
51219888Sed
52219888Sed	/*
53219888Sed	 * Mark all bits between begin % 64 and end % 64 dirty.
54219888Sed	 * This code is functionally equivalent to:
55219888Sed	 *
56219888Sed	 * 	for (i = begin; i < end; i++)
57219888Sed	 * 		mask |= (uint64_t)1 << (i % 64);
58219888Sed	 */
59219888Sed
60219888Sed	/* Obvious case. Mark everything dirty. */
61219888Sed	if (end - begin >= 64)
62219888Sed		return (VBM_DIRTY);
63219888Sed
64219888Sed	/* 1....0; used bits on the left. */
65219888Sed	left = VBM_DIRTY << begin % 64;
66219888Sed	/* 0....1; used bits on the right. */
67219888Sed	right = VBM_DIRTY >> -end % 64;
68219888Sed
69219888Sed	/*
70219888Sed	 * Only take the intersection.  If the result of that is 0, it
71219888Sed	 * means that the selection crossed a 64 bit boundary along the
72219888Sed	 * way, which means we have to take the complement.
73219888Sed	 */
74219888Sed	mask = left & right;
75219888Sed	if (mask == 0)
76219888Sed		mask = left | right;
77219888Sed	return (mask);
78219888Sed}
79219888Sed
80219888Sedstatic inline void
81219888Sedvtbuf_dirty(struct vt_buf *vb, const term_rect_t *area)
82219888Sed{
83219888Sed
84219888Sed	VTBUF_LOCK(vb);
85219888Sed	if (vb->vb_dirtyrect.tr_begin.tp_row > area->tr_begin.tp_row)
86219888Sed		vb->vb_dirtyrect.tr_begin.tp_row = area->tr_begin.tp_row;
87219888Sed	if (vb->vb_dirtyrect.tr_begin.tp_col > area->tr_begin.tp_col)
88219888Sed		vb->vb_dirtyrect.tr_begin.tp_col = area->tr_begin.tp_col;
89219888Sed	if (vb->vb_dirtyrect.tr_end.tp_row < area->tr_end.tp_row)
90219888Sed		vb->vb_dirtyrect.tr_end.tp_row = area->tr_end.tp_row;
91219888Sed	if (vb->vb_dirtyrect.tr_end.tp_col < area->tr_end.tp_col)
92219888Sed		vb->vb_dirtyrect.tr_end.tp_col = area->tr_end.tp_col;
93219888Sed	vb->vb_dirtymask.vbm_row |=
94219888Sed	    vtbuf_dirty_axis(area->tr_begin.tp_row, area->tr_end.tp_row);
95219888Sed	vb->vb_dirtymask.vbm_col |=
96219888Sed	    vtbuf_dirty_axis(area->tr_begin.tp_col, area->tr_end.tp_col);
97219888Sed	VTBUF_UNLOCK(vb);
98219888Sed}
99219888Sed
100219888Sedstatic inline void
101219888Sedvtbuf_dirty_cell(struct vt_buf *vb, const term_pos_t *p)
102219888Sed{
103219888Sed	term_rect_t area;
104219888Sed
105219888Sed	area.tr_begin = *p;
106219888Sed	area.tr_end.tp_row = p->tp_row + 1;
107219888Sed	area.tr_end.tp_col = p->tp_col + 1;
108219888Sed	vtbuf_dirty(vb, &area);
109219888Sed}
110219888Sed
111219888Sedstatic void
112219888Sedvtbuf_make_undirty(struct vt_buf *vb)
113219888Sed{
114219888Sed
115219888Sed	vb->vb_dirtyrect.tr_begin = vb->vb_size;
116219888Sed	vb->vb_dirtyrect.tr_end.tp_row = vb->vb_dirtyrect.tr_end.tp_col = 0;
117219888Sed	vb->vb_dirtymask.vbm_row = vb->vb_dirtymask.vbm_col = 0;
118219888Sed}
119219888Sed
120219888Sedvoid
121219888Sedvtbuf_undirty(struct vt_buf *vb, term_rect_t *r, struct vt_bufmask *m)
122219888Sed{
123219888Sed
124219888Sed	VTBUF_LOCK(vb);
125219888Sed	*r = vb->vb_dirtyrect;
126219888Sed	*m = vb->vb_dirtymask;
127219888Sed	vtbuf_make_undirty(vb);
128219888Sed	VTBUF_UNLOCK(vb);
129219888Sed}
130219888Sed
131219888Sedvoid
132219888Sedvtbuf_copy(struct vt_buf *vb, const term_rect_t *r, const term_pos_t *p2)
133219888Sed{
134219888Sed	const term_pos_t *p1 = &r->tr_begin;
135219888Sed	term_rect_t area;
136219888Sed	unsigned int rows, cols;
137219888Sed	int pr;
138219888Sed
139219888Sed	rows = r->tr_end.tp_row - r->tr_begin.tp_row;
140219888Sed	cols = r->tr_end.tp_col - r->tr_begin.tp_col;
141219888Sed
142219888Sed	/* Handle overlapping copies. */
143219888Sed	if (p2->tp_row < p1->tp_row) {
144219888Sed		/* Move data up. */
145219888Sed		for (pr = 0; pr < rows; pr++)
146219888Sed			memmove(
147219888Sed			    &VTBUF_FIELD(vb, p2->tp_row + pr, p2->tp_col),
148219888Sed			    &VTBUF_FIELD(vb, p1->tp_row + pr, p1->tp_col),
149219888Sed			    cols * sizeof(term_char_t));
150219888Sed	} else {
151219888Sed		/* Move data down. */
152219888Sed		for (pr = rows - 1; pr >= 0; pr--)
153219888Sed			memmove(
154219888Sed			    &VTBUF_FIELD(vb, p2->tp_row + pr, p2->tp_col),
155219888Sed			    &VTBUF_FIELD(vb, p1->tp_row + pr, p1->tp_col),
156219888Sed			    cols * sizeof(term_char_t));
157219888Sed	}
158219888Sed
159219888Sed	area.tr_begin = *p2;
160219888Sed	area.tr_end.tp_row = p2->tp_row + rows;
161219888Sed	area.tr_end.tp_col = p2->tp_col + cols;
162219888Sed	vtbuf_dirty(vb, &area);
163219888Sed}
164219888Sed
165219888Sedvoid
166219888Sedvtbuf_fill(struct vt_buf *vb, const term_rect_t *r, term_char_t c)
167219888Sed{
168219888Sed	unsigned int pr, pc;
169219888Sed
170219888Sed	for (pr = r->tr_begin.tp_row; pr < r->tr_end.tp_row; pr++)
171219888Sed		for (pc = r->tr_begin.tp_col; pc < r->tr_end.tp_col; pc++)
172219888Sed			VTBUF_FIELD(vb, pr, pc) = c;
173219888Sed
174219888Sed	vtbuf_dirty(vb, r);
175219888Sed}
176219888Sed
177219888Sedvoid
178219888Sedvtbuf_init_early(struct vt_buf *vb)
179219888Sed{
180219888Sed
181219888Sed	vb->vb_flags |= VBF_CURSOR;
182219888Sed	vtbuf_make_undirty(vb);
183219888Sed	mtx_init(&vb->vb_lock, "vtbuf", NULL, MTX_SPIN);
184219888Sed}
185219888Sed
186219888Sedvoid
187219888Sedvtbuf_init(struct vt_buf *vb, const term_pos_t *p)
188219888Sed{
189219888Sed
190219888Sed	vb->vb_size = *p;
191219888Sed	vb->vb_buffer = malloc(p->tp_row * p->tp_col * sizeof(term_char_t),
192219888Sed	    M_VTBUF, M_WAITOK);
193219888Sed	vtbuf_init_early(vb);
194219888Sed}
195219888Sed
196219888Sedvoid
197219888Sedvtbuf_grow(struct vt_buf *vb, const term_pos_t *p)
198219888Sed{
199219888Sed	term_char_t *old, *new;
200219888Sed
201219888Sed	if (p->tp_row > vb->vb_size.tp_row ||
202219888Sed	    p->tp_col > vb->vb_size.tp_col) {
203219888Sed		/* Allocate new buffer. */
204219888Sed		new = malloc(p->tp_row * p->tp_col * sizeof(term_char_t),
205219888Sed		    M_VTBUF, M_WAITOK|M_ZERO);
206219888Sed
207219888Sed		/* Toggle it. */
208219888Sed		VTBUF_LOCK(vb);
209219888Sed		old = vb->vb_flags & VBF_STATIC ? NULL : vb->vb_buffer;
210219888Sed		vb->vb_buffer = new;
211219888Sed		vb->vb_flags &= ~VBF_STATIC;
212219888Sed		vb->vb_size = *p;
213219888Sed		vtbuf_make_undirty(vb);
214219888Sed		VTBUF_UNLOCK(vb);
215219888Sed
216219888Sed		/* Deallocate old buffer. */
217219888Sed		if (old != NULL)
218219888Sed			free(old, M_VTBUF);
219219888Sed	}
220219888Sed}
221219888Sed
222219888Sedvoid
223219888Sedvtbuf_putchar(struct vt_buf *vb, const term_pos_t *p, term_char_t c)
224219888Sed{
225219888Sed
226219888Sed	if (VTBUF_FIELD(vb, p->tp_row, p->tp_col) != c) {
227219888Sed		VTBUF_FIELD(vb, p->tp_row, p->tp_col) = c;
228219888Sed		vtbuf_dirty_cell(vb, p);
229219888Sed	}
230219888Sed}
231219888Sed
232219888Sedvoid
233219888Sedvtbuf_cursor_position(struct vt_buf *vb, const term_pos_t *p)
234219888Sed{
235219888Sed
236219888Sed	if (vb->vb_flags & VBF_CURSOR) {
237219888Sed		vtbuf_dirty_cell(vb, &vb->vb_cursor);
238219888Sed		vb->vb_cursor = *p;
239219888Sed		vtbuf_dirty_cell(vb, &vb->vb_cursor);
240219888Sed	} else {
241219888Sed		vb->vb_cursor = *p;
242219888Sed	}
243219888Sed}
244219888Sed
245219888Sedvoid
246219888Sedvtbuf_cursor_visibility(struct vt_buf *vb, int yes)
247219888Sed{
248219888Sed	int oflags, nflags;
249219888Sed
250219888Sed	VTBUF_LOCK(vb);
251219888Sed	oflags = vb->vb_flags;
252219888Sed	if (yes)
253219888Sed		vb->vb_flags |= VBF_CURSOR;
254219888Sed	else
255219888Sed		vb->vb_flags &= ~VBF_CURSOR;
256219888Sed	nflags = vb->vb_flags;
257219888Sed	VTBUF_UNLOCK(vb);
258219888Sed
259219888Sed	if (oflags != nflags)
260219888Sed		vtbuf_dirty_cell(vb, &vb->vb_cursor);
261219888Sed}
262