teken.c revision 186681
1186681Sed/*-
2186681Sed * Copyright (c) 2008-2009 Ed Schouten <ed@FreeBSD.org>
3186681Sed * All rights reserved.
4186681Sed *
5186681Sed * Redistribution and use in source and binary forms, with or without
6186681Sed * modification, are permitted provided that the following conditions
7186681Sed * are met:
8186681Sed * 1. Redistributions of source code must retain the above copyright
9186681Sed *    notice, this list of conditions and the following disclaimer.
10186681Sed * 2. Redistributions in binary form must reproduce the above copyright
11186681Sed *    notice, this list of conditions and the following disclaimer in the
12186681Sed *    documentation and/or other materials provided with the distribution.
13186681Sed *
14186681Sed * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15186681Sed * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16186681Sed * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17186681Sed * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18186681Sed * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19186681Sed * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20186681Sed * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21186681Sed * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22186681Sed * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23186681Sed * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24186681Sed * SUCH DAMAGE.
25186681Sed *
26186681Sed * $FreeBSD: head/sys/dev/syscons/teken/teken.c 186681 2009-01-01 13:26:53Z ed $
27186681Sed */
28186681Sed
29186681Sed#include <sys/cdefs.h>
30186681Sed#if defined(__FreeBSD__) && defined(_KERNEL)
31186681Sed#include <sys/param.h>
32186681Sed#include <sys/lock.h>
33186681Sed#include <sys/systm.h>
34186681Sed#define	teken_assert(x)		MPASS(x)
35186681Sed#define	teken_printf(x,...)
36186681Sed#else /* !(__FreeBSD__ && _KERNEL) */
37186681Sed#include <sys/types.h>
38186681Sed#include <assert.h>
39186681Sed#include <inttypes.h>
40186681Sed#include <stdio.h>
41186681Sed#include <string.h>
42186681Sed#define	teken_assert(x)		assert(x)
43186681Sed#define	teken_printf(x,...)	do { \
44186681Sed	if (df != NULL) \
45186681Sed		fprintf(df, x, ## __VA_ARGS__); \
46186681Sed} while (0)
47186681Sed/* debug messages */
48186681Sedstatic FILE *df;
49186681Sed#endif /* __FreeBSD__ && _KERNEL */
50186681Sed
51186681Sed#include "teken.h"
52186681Sed#ifdef TEKEN_UTF8
53186681Sed#include "teken_wcwidth.h"
54186681Sed#else /* !TEKEN_UTF8 */
55186681Sedstatic inline int
56186681Sedteken_wcwidth(teken_char_t c)
57186681Sed{
58186681Sed
59186681Sed	return (c <= 0x1B) ? -1 : 1;
60186681Sed}
61186681Sed#endif /* TEKEN_UTF8 */
62186681Sed
63186681Sed/* Private flags for teken_format_t. */
64186681Sed#define	TF_REVERSE	0x08
65186681Sed
66186681Sed/* Private flags for t_stateflags. */
67186681Sed#define	TS_FIRSTDIGIT	0x01	/* First numeric digit in escape sequence. */
68186681Sed#define	TS_INSERT	0x02	/* Insert mode. */
69186681Sed#define	TS_AUTOWRAP	0x04	/* Autowrap. */
70186681Sed#define	TS_ORIGIN	0x08	/* Origin mode. */
71186681Sed#define	TS_WRAPPED	0x10	/* Next character should be printed on col 0. */
72186681Sed
73186681Sed/* Character that blanks a cell. */
74186681Sed#define	BLANK	' '
75186681Sed
76186681Sedstatic teken_state_t	teken_state_init;
77186681Sed
78186681Sed/*
79186681Sed * Wrappers for hooks.
80186681Sed */
81186681Sed
82186681Sedstatic inline void
83186681Sedteken_funcs_bell(teken_t *t)
84186681Sed{
85186681Sed
86186681Sed	t->t_funcs->tf_bell(t->t_softc);
87186681Sed}
88186681Sed
89186681Sedstatic inline void
90186681Sedteken_funcs_cursor(teken_t *t)
91186681Sed{
92186681Sed
93186681Sed	teken_assert(t->t_cursor.tp_row < t->t_winsize.tp_row);
94186681Sed	teken_assert(t->t_cursor.tp_col < t->t_winsize.tp_col);
95186681Sed
96186681Sed	t->t_funcs->tf_cursor(t->t_softc, &t->t_cursor);
97186681Sed}
98186681Sed
99186681Sedstatic inline void
100186681Sedteken_funcs_putchar(teken_t *t, const teken_pos_t *p, teken_char_t c,
101186681Sed    const teken_attr_t *a)
102186681Sed{
103186681Sed	teken_attr_t ta;
104186681Sed
105186681Sed	teken_assert(p->tp_row < t->t_winsize.tp_row);
106186681Sed	teken_assert(p->tp_col < t->t_winsize.tp_col);
107186681Sed
108186681Sed	/* Apply inversion. */
109186681Sed	if (a->ta_format & TF_REVERSE) {
110186681Sed		ta.ta_format = a->ta_format;
111186681Sed		ta.ta_fgcolor = a->ta_bgcolor;
112186681Sed		ta.ta_bgcolor = a->ta_fgcolor;
113186681Sed		a = &ta;
114186681Sed	}
115186681Sed
116186681Sed	t->t_funcs->tf_putchar(t->t_softc, p, c, a);
117186681Sed}
118186681Sed
119186681Sedstatic inline void
120186681Sedteken_funcs_fill(teken_t *t, const teken_rect_t *r,
121186681Sed    const teken_char_t c, const teken_attr_t *a)
122186681Sed{
123186681Sed	teken_attr_t ta;
124186681Sed
125186681Sed	teken_assert(r->tr_end.tp_row > r->tr_begin.tp_row);
126186681Sed	teken_assert(r->tr_end.tp_row <= t->t_winsize.tp_row);
127186681Sed	teken_assert(r->tr_end.tp_col > r->tr_begin.tp_col);
128186681Sed	teken_assert(r->tr_end.tp_col <= t->t_winsize.tp_col);
129186681Sed
130186681Sed	/* Apply inversion. */
131186681Sed	if (a->ta_format & TF_REVERSE) {
132186681Sed		ta.ta_format = a->ta_format;
133186681Sed		ta.ta_fgcolor = a->ta_bgcolor;
134186681Sed		ta.ta_bgcolor = a->ta_fgcolor;
135186681Sed		a = &ta;
136186681Sed	}
137186681Sed
138186681Sed	t->t_funcs->tf_fill(t->t_softc, r, c, a);
139186681Sed}
140186681Sed
141186681Sedstatic inline void
142186681Sedteken_funcs_copy(teken_t *t, const teken_rect_t *r, const teken_pos_t *p)
143186681Sed{
144186681Sed
145186681Sed	teken_assert(r->tr_end.tp_row > r->tr_begin.tp_row);
146186681Sed	teken_assert(r->tr_end.tp_row <= t->t_winsize.tp_row);
147186681Sed	teken_assert(r->tr_end.tp_col > r->tr_begin.tp_col);
148186681Sed	teken_assert(r->tr_end.tp_col <= t->t_winsize.tp_col);
149186681Sed	teken_assert(p->tp_row + (r->tr_end.tp_row - r->tr_begin.tp_row) <= t->t_winsize.tp_row);
150186681Sed	teken_assert(p->tp_col + (r->tr_end.tp_col - r->tr_begin.tp_col) <= t->t_winsize.tp_col);
151186681Sed
152186681Sed	t->t_funcs->tf_copy(t->t_softc, r, p);
153186681Sed}
154186681Sed
155186681Sedstatic inline void
156186681Sedteken_funcs_param(teken_t *t, int cmd, int value)
157186681Sed{
158186681Sed
159186681Sed	t->t_funcs->tf_param(t->t_softc, cmd, value);
160186681Sed}
161186681Sed
162186681Sedstatic inline void
163186681Sedteken_funcs_respond(teken_t *t, const void *buf, size_t len)
164186681Sed{
165186681Sed
166186681Sed	t->t_funcs->tf_respond(t->t_softc, buf, len);
167186681Sed}
168186681Sed
169186681Sed#include "teken_subr.h"
170186681Sed#include "teken_subr_compat.h"
171186681Sed
172186681Sed/*
173186681Sed * Programming interface.
174186681Sed */
175186681Sed
176186681Sedvoid
177186681Sedteken_init(teken_t *t, const teken_funcs_t *tf, void *softc)
178186681Sed{
179186681Sed	teken_pos_t tp = { .tp_row = 24, .tp_col = 80 };
180186681Sed
181186681Sed#if !(defined(__FreeBSD__) && defined(_KERNEL))
182186681Sed	df = fopen("teken.log", "w");
183186681Sed	if (df != NULL)
184186681Sed		setvbuf(df, NULL, _IOLBF, BUFSIZ);
185186681Sed#endif /* !(__FreeBSD__ && _KERNEL) */
186186681Sed
187186681Sed	t->t_funcs = tf;
188186681Sed	t->t_softc = softc;
189186681Sed
190186681Sed	t->t_nextstate = teken_state_init;
191186681Sed
192186681Sed	t->t_defattr.ta_format = 0;
193186681Sed	t->t_defattr.ta_fgcolor = TC_WHITE;
194186681Sed	t->t_defattr.ta_bgcolor = TC_BLACK;
195186681Sed	teken_subr_do_reset(t);
196186681Sed
197186681Sed#ifdef TEKEN_UTF8
198186681Sed	t->t_utf8_left = 0;
199186681Sed#endif /* TEKEN_UTF8 */
200186681Sed
201186681Sed	teken_set_winsize(t, &tp);
202186681Sed}
203186681Sed
204186681Sedstatic void
205186681Sedteken_input_char(teken_t *t, teken_char_t c)
206186681Sed{
207186681Sed
208186681Sed	switch (c) {
209186681Sed	case '\0':
210186681Sed		break;
211186681Sed	case '\a':
212186681Sed		teken_subr_bell(t);
213186681Sed		break;
214186681Sed	case '\b':
215186681Sed		teken_subr_backspace(t);
216186681Sed		break;
217186681Sed	case '\n':
218186681Sed	case '\x0B':
219186681Sed		teken_subr_newline(t);
220186681Sed		break;
221186681Sed	case '\r':
222186681Sed		teken_subr_carriage_return(t);
223186681Sed		break;
224186681Sed	case '\t':
225186681Sed		teken_subr_horizontal_tab(t);
226186681Sed		break;
227186681Sed	default:
228186681Sed		t->t_nextstate(t, c);
229186681Sed		break;
230186681Sed	}
231186681Sed
232186681Sed	/* Post-processing assertions. */
233186681Sed	teken_assert(t->t_cursor.tp_row >= t->t_originreg.ts_begin);
234186681Sed	teken_assert(t->t_cursor.tp_row < t->t_originreg.ts_end);
235186681Sed	teken_assert(t->t_cursor.tp_row < t->t_winsize.tp_row);
236186681Sed	teken_assert(t->t_cursor.tp_col < t->t_winsize.tp_col);
237186681Sed	teken_assert(t->t_saved_cursor.tp_row < t->t_winsize.tp_row);
238186681Sed	teken_assert(t->t_saved_cursor.tp_col < t->t_winsize.tp_col);
239186681Sed	teken_assert(t->t_scrollreg.ts_end <= t->t_winsize.tp_row);
240186681Sed	teken_assert(t->t_scrollreg.ts_begin < t->t_scrollreg.ts_end);
241186681Sed	/* Origin region has to be window size or the same as scrollreg. */
242186681Sed	teken_assert((t->t_originreg.ts_begin == t->t_scrollreg.ts_begin &&
243186681Sed	    t->t_originreg.ts_end == t->t_scrollreg.ts_end) ||
244186681Sed	    (t->t_originreg.ts_begin == 0 &&
245186681Sed	    t->t_originreg.ts_end == t->t_winsize.tp_row));
246186681Sed}
247186681Sed
248186681Sedstatic void
249186681Sedteken_input_byte(teken_t *t, unsigned char c)
250186681Sed{
251186681Sed
252186681Sed#ifdef TEKEN_UTF8
253186681Sed	/*
254186681Sed	 * UTF-8 handling.
255186681Sed	 */
256186681Sed	if ((c & 0x80) == 0x00) {
257186681Sed		/* One-byte sequence. */
258186681Sed		t->t_utf8_left = 0;
259186681Sed		teken_input_char(t, c);
260186681Sed	} else if ((c & 0xe0) == 0xc0) {
261186681Sed		/* Two-byte sequence. */
262186681Sed		t->t_utf8_left = 1;
263186681Sed		t->t_utf8_partial = c & 0x1f;
264186681Sed	} else if ((c & 0xf0) == 0xe0) {
265186681Sed		/* Three-byte sequence. */
266186681Sed		t->t_utf8_left = 2;
267186681Sed		t->t_utf8_partial = c & 0x0f;
268186681Sed	} else if ((c & 0xf8) == 0xf0) {
269186681Sed		/* Four-byte sequence. */
270186681Sed		t->t_utf8_left = 3;
271186681Sed		t->t_utf8_partial = c & 0x07;
272186681Sed	} else if ((c & 0xc0) == 0x80) {
273186681Sed		if (t->t_utf8_left == 0)
274186681Sed			return;
275186681Sed		t->t_utf8_left--;
276186681Sed		t->t_utf8_partial = (t->t_utf8_partial << 6) | (c & 0x3f);
277186681Sed		if (t->t_utf8_left == 0) {
278186681Sed			teken_printf("Got UTF-8 char %u\n", t->t_utf8_partial);
279186681Sed			teken_input_char(t, t->t_utf8_partial);
280186681Sed		}
281186681Sed	}
282186681Sed#else /* !TEKEN_UTF8 */
283186681Sed	teken_input_char(t, c);
284186681Sed#endif /* TEKEN_UTF8 */
285186681Sed}
286186681Sed
287186681Sedvoid
288186681Sedteken_input(teken_t *t, const void *buf, size_t len)
289186681Sed{
290186681Sed	const char *c = buf;
291186681Sed
292186681Sed	while (len-- > 0)
293186681Sed		teken_input_byte(t, *c++);
294186681Sed}
295186681Sed
296186681Sedvoid
297186681Sedteken_set_cursor(teken_t *t, const teken_pos_t *p)
298186681Sed{
299186681Sed
300186681Sed	/* XXX: bounds checking with originreg! */
301186681Sed	teken_assert(p->tp_row < t->t_winsize.tp_row);
302186681Sed	teken_assert(p->tp_col < t->t_winsize.tp_col);
303186681Sed
304186681Sed	t->t_cursor = *p;
305186681Sed}
306186681Sed
307186681Sedvoid
308186681Sedteken_set_defattr(teken_t *t, const teken_attr_t *a)
309186681Sed{
310186681Sed
311186681Sed	t->t_curattr = t->t_saved_curattr = t->t_defattr = *a;
312186681Sed}
313186681Sed
314186681Sedvoid
315186681Sedteken_set_winsize(teken_t *t, const teken_pos_t *p)
316186681Sed{
317186681Sed
318186681Sed	teken_assert(p->tp_col <= T_NUMCOL);
319186681Sed
320186681Sed	t->t_winsize = *p;
321186681Sed	/* XXX: bounds checking with cursor/etc! */
322186681Sed	t->t_scrollreg.ts_begin = 0;
323186681Sed	t->t_scrollreg.ts_end = t->t_winsize.tp_row;
324186681Sed	t->t_originreg = t->t_scrollreg;
325186681Sed}
326186681Sed
327186681Sed/*
328186681Sed * State machine.
329186681Sed */
330186681Sed
331186681Sedstatic void
332186681Sedteken_state_switch(teken_t *t, teken_state_t *s)
333186681Sed{
334186681Sed
335186681Sed	t->t_nextstate = s;
336186681Sed	t->t_curnum = 0;
337186681Sed	t->t_stateflags |= TS_FIRSTDIGIT;
338186681Sed}
339186681Sed
340186681Sedstatic int
341186681Sedteken_state_numbers(teken_t *t, teken_char_t c)
342186681Sed{
343186681Sed
344186681Sed	teken_assert(t->t_curnum < T_NUMSIZE);
345186681Sed
346186681Sed	if (c >= '0' && c <= '9') {
347186681Sed		/*
348186681Sed		 * Don't do math with the default value of 1 when a
349186681Sed		 * custom number is inserted.
350186681Sed		 */
351186681Sed		if (t->t_stateflags & TS_FIRSTDIGIT) {
352186681Sed			t->t_stateflags &= ~TS_FIRSTDIGIT;
353186681Sed			t->t_nums[t->t_curnum] = 0;
354186681Sed		} else {
355186681Sed			t->t_nums[t->t_curnum] *= 10;
356186681Sed		}
357186681Sed
358186681Sed		t->t_nums[t->t_curnum] += c - '0';
359186681Sed		return (1);
360186681Sed	} else if (c == ';') {
361186681Sed		if (t->t_stateflags & TS_FIRSTDIGIT)
362186681Sed			t->t_nums[t->t_curnum] = 0;
363186681Sed
364186681Sed		/* Only allow a limited set of arguments. */
365186681Sed		if (++t->t_curnum == T_NUMSIZE) {
366186681Sed			teken_state_switch(t, teken_state_init);
367186681Sed			return (1);
368186681Sed		}
369186681Sed
370186681Sed		t->t_stateflags |= TS_FIRSTDIGIT;
371186681Sed		return (1);
372186681Sed	} else {
373186681Sed		if (t->t_stateflags & TS_FIRSTDIGIT && t->t_curnum > 0) {
374186681Sed			/* Finish off the last empty argument. */
375186681Sed			t->t_nums[t->t_curnum] = 0;
376186681Sed			t->t_curnum++;
377186681Sed		} else if ((t->t_stateflags & TS_FIRSTDIGIT) == 0) {
378186681Sed			/* Also count the last argument. */
379186681Sed			t->t_curnum++;
380186681Sed		}
381186681Sed	}
382186681Sed
383186681Sed	return (0);
384186681Sed}
385186681Sed
386186681Sed#include "teken_state.h"
387