teken.c revision 186729
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 186729 2009-01-03 22:51:54Z 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. */
71186729Sed#ifdef TEKEN_CONS25
72186729Sed#define	TS_WRAPPED	0x00	/* Simple line wrapping. */
73186729Sed#else /* !TEKEN_CONS25 */
74186681Sed#define	TS_WRAPPED	0x10	/* Next character should be printed on col 0. */
75186729Sed#endif /* TEKEN_CONS25 */
76186681Sed
77186681Sed/* Character that blanks a cell. */
78186681Sed#define	BLANK	' '
79186681Sed
80186681Sedstatic teken_state_t	teken_state_init;
81186681Sed
82186681Sed/*
83186681Sed * Wrappers for hooks.
84186681Sed */
85186681Sed
86186681Sedstatic inline void
87186681Sedteken_funcs_bell(teken_t *t)
88186681Sed{
89186681Sed
90186681Sed	t->t_funcs->tf_bell(t->t_softc);
91186681Sed}
92186681Sed
93186681Sedstatic inline void
94186681Sedteken_funcs_cursor(teken_t *t)
95186681Sed{
96186681Sed
97186681Sed	teken_assert(t->t_cursor.tp_row < t->t_winsize.tp_row);
98186681Sed	teken_assert(t->t_cursor.tp_col < t->t_winsize.tp_col);
99186681Sed
100186681Sed	t->t_funcs->tf_cursor(t->t_softc, &t->t_cursor);
101186681Sed}
102186681Sed
103186681Sedstatic inline void
104186681Sedteken_funcs_putchar(teken_t *t, const teken_pos_t *p, teken_char_t c,
105186681Sed    const teken_attr_t *a)
106186681Sed{
107186681Sed	teken_attr_t ta;
108186681Sed
109186681Sed	teken_assert(p->tp_row < t->t_winsize.tp_row);
110186681Sed	teken_assert(p->tp_col < t->t_winsize.tp_col);
111186681Sed
112186681Sed	/* Apply inversion. */
113186681Sed	if (a->ta_format & TF_REVERSE) {
114186681Sed		ta.ta_format = a->ta_format;
115186681Sed		ta.ta_fgcolor = a->ta_bgcolor;
116186681Sed		ta.ta_bgcolor = a->ta_fgcolor;
117186681Sed		a = &ta;
118186681Sed	}
119186681Sed
120186681Sed	t->t_funcs->tf_putchar(t->t_softc, p, c, a);
121186681Sed}
122186681Sed
123186681Sedstatic inline void
124186681Sedteken_funcs_fill(teken_t *t, const teken_rect_t *r,
125186681Sed    const teken_char_t c, const teken_attr_t *a)
126186681Sed{
127186681Sed	teken_attr_t ta;
128186681Sed
129186681Sed	teken_assert(r->tr_end.tp_row > r->tr_begin.tp_row);
130186681Sed	teken_assert(r->tr_end.tp_row <= t->t_winsize.tp_row);
131186681Sed	teken_assert(r->tr_end.tp_col > r->tr_begin.tp_col);
132186681Sed	teken_assert(r->tr_end.tp_col <= t->t_winsize.tp_col);
133186681Sed
134186681Sed	/* Apply inversion. */
135186681Sed	if (a->ta_format & TF_REVERSE) {
136186681Sed		ta.ta_format = a->ta_format;
137186681Sed		ta.ta_fgcolor = a->ta_bgcolor;
138186681Sed		ta.ta_bgcolor = a->ta_fgcolor;
139186681Sed		a = &ta;
140186681Sed	}
141186681Sed
142186681Sed	t->t_funcs->tf_fill(t->t_softc, r, c, a);
143186681Sed}
144186681Sed
145186681Sedstatic inline void
146186681Sedteken_funcs_copy(teken_t *t, const teken_rect_t *r, const teken_pos_t *p)
147186681Sed{
148186681Sed
149186681Sed	teken_assert(r->tr_end.tp_row > r->tr_begin.tp_row);
150186681Sed	teken_assert(r->tr_end.tp_row <= t->t_winsize.tp_row);
151186681Sed	teken_assert(r->tr_end.tp_col > r->tr_begin.tp_col);
152186681Sed	teken_assert(r->tr_end.tp_col <= t->t_winsize.tp_col);
153186681Sed	teken_assert(p->tp_row + (r->tr_end.tp_row - r->tr_begin.tp_row) <= t->t_winsize.tp_row);
154186681Sed	teken_assert(p->tp_col + (r->tr_end.tp_col - r->tr_begin.tp_col) <= t->t_winsize.tp_col);
155186681Sed
156186681Sed	t->t_funcs->tf_copy(t->t_softc, r, p);
157186681Sed}
158186681Sed
159186681Sedstatic inline void
160186681Sedteken_funcs_param(teken_t *t, int cmd, int value)
161186681Sed{
162186681Sed
163186681Sed	t->t_funcs->tf_param(t->t_softc, cmd, value);
164186681Sed}
165186681Sed
166186681Sedstatic inline void
167186681Sedteken_funcs_respond(teken_t *t, const void *buf, size_t len)
168186681Sed{
169186681Sed
170186681Sed	t->t_funcs->tf_respond(t->t_softc, buf, len);
171186681Sed}
172186681Sed
173186681Sed#include "teken_subr.h"
174186681Sed#include "teken_subr_compat.h"
175186681Sed
176186681Sed/*
177186681Sed * Programming interface.
178186681Sed */
179186681Sed
180186681Sedvoid
181186681Sedteken_init(teken_t *t, const teken_funcs_t *tf, void *softc)
182186681Sed{
183186681Sed	teken_pos_t tp = { .tp_row = 24, .tp_col = 80 };
184186681Sed
185186681Sed#if !(defined(__FreeBSD__) && defined(_KERNEL))
186186681Sed	df = fopen("teken.log", "w");
187186681Sed	if (df != NULL)
188186681Sed		setvbuf(df, NULL, _IOLBF, BUFSIZ);
189186681Sed#endif /* !(__FreeBSD__ && _KERNEL) */
190186681Sed
191186681Sed	t->t_funcs = tf;
192186681Sed	t->t_softc = softc;
193186681Sed
194186681Sed	t->t_nextstate = teken_state_init;
195186681Sed
196186681Sed	t->t_defattr.ta_format = 0;
197186681Sed	t->t_defattr.ta_fgcolor = TC_WHITE;
198186681Sed	t->t_defattr.ta_bgcolor = TC_BLACK;
199186681Sed	teken_subr_do_reset(t);
200186681Sed
201186681Sed#ifdef TEKEN_UTF8
202186681Sed	t->t_utf8_left = 0;
203186681Sed#endif /* TEKEN_UTF8 */
204186681Sed
205186681Sed	teken_set_winsize(t, &tp);
206186681Sed}
207186681Sed
208186681Sedstatic void
209186681Sedteken_input_char(teken_t *t, teken_char_t c)
210186681Sed{
211186681Sed
212186681Sed	switch (c) {
213186681Sed	case '\0':
214186681Sed		break;
215186681Sed	case '\a':
216186681Sed		teken_subr_bell(t);
217186681Sed		break;
218186681Sed	case '\b':
219186681Sed		teken_subr_backspace(t);
220186681Sed		break;
221186681Sed	case '\n':
222186681Sed	case '\x0B':
223186681Sed		teken_subr_newline(t);
224186681Sed		break;
225186681Sed	case '\r':
226186681Sed		teken_subr_carriage_return(t);
227186681Sed		break;
228186681Sed	case '\t':
229186681Sed		teken_subr_horizontal_tab(t);
230186681Sed		break;
231186681Sed	default:
232186681Sed		t->t_nextstate(t, c);
233186681Sed		break;
234186681Sed	}
235186681Sed
236186681Sed	/* Post-processing assertions. */
237186681Sed	teken_assert(t->t_cursor.tp_row >= t->t_originreg.ts_begin);
238186681Sed	teken_assert(t->t_cursor.tp_row < t->t_originreg.ts_end);
239186681Sed	teken_assert(t->t_cursor.tp_row < t->t_winsize.tp_row);
240186681Sed	teken_assert(t->t_cursor.tp_col < t->t_winsize.tp_col);
241186681Sed	teken_assert(t->t_saved_cursor.tp_row < t->t_winsize.tp_row);
242186681Sed	teken_assert(t->t_saved_cursor.tp_col < t->t_winsize.tp_col);
243186681Sed	teken_assert(t->t_scrollreg.ts_end <= t->t_winsize.tp_row);
244186681Sed	teken_assert(t->t_scrollreg.ts_begin < t->t_scrollreg.ts_end);
245186681Sed	/* Origin region has to be window size or the same as scrollreg. */
246186681Sed	teken_assert((t->t_originreg.ts_begin == t->t_scrollreg.ts_begin &&
247186681Sed	    t->t_originreg.ts_end == t->t_scrollreg.ts_end) ||
248186681Sed	    (t->t_originreg.ts_begin == 0 &&
249186681Sed	    t->t_originreg.ts_end == t->t_winsize.tp_row));
250186681Sed}
251186681Sed
252186681Sedstatic void
253186681Sedteken_input_byte(teken_t *t, unsigned char c)
254186681Sed{
255186681Sed
256186681Sed#ifdef TEKEN_UTF8
257186681Sed	/*
258186681Sed	 * UTF-8 handling.
259186681Sed	 */
260186681Sed	if ((c & 0x80) == 0x00) {
261186681Sed		/* One-byte sequence. */
262186681Sed		t->t_utf8_left = 0;
263186681Sed		teken_input_char(t, c);
264186681Sed	} else if ((c & 0xe0) == 0xc0) {
265186681Sed		/* Two-byte sequence. */
266186681Sed		t->t_utf8_left = 1;
267186681Sed		t->t_utf8_partial = c & 0x1f;
268186681Sed	} else if ((c & 0xf0) == 0xe0) {
269186681Sed		/* Three-byte sequence. */
270186681Sed		t->t_utf8_left = 2;
271186681Sed		t->t_utf8_partial = c & 0x0f;
272186681Sed	} else if ((c & 0xf8) == 0xf0) {
273186681Sed		/* Four-byte sequence. */
274186681Sed		t->t_utf8_left = 3;
275186681Sed		t->t_utf8_partial = c & 0x07;
276186681Sed	} else if ((c & 0xc0) == 0x80) {
277186681Sed		if (t->t_utf8_left == 0)
278186681Sed			return;
279186681Sed		t->t_utf8_left--;
280186681Sed		t->t_utf8_partial = (t->t_utf8_partial << 6) | (c & 0x3f);
281186681Sed		if (t->t_utf8_left == 0) {
282186681Sed			teken_printf("Got UTF-8 char %u\n", t->t_utf8_partial);
283186681Sed			teken_input_char(t, t->t_utf8_partial);
284186681Sed		}
285186681Sed	}
286186681Sed#else /* !TEKEN_UTF8 */
287186681Sed	teken_input_char(t, c);
288186681Sed#endif /* TEKEN_UTF8 */
289186681Sed}
290186681Sed
291186681Sedvoid
292186681Sedteken_input(teken_t *t, const void *buf, size_t len)
293186681Sed{
294186681Sed	const char *c = buf;
295186681Sed
296186681Sed	while (len-- > 0)
297186681Sed		teken_input_byte(t, *c++);
298186681Sed}
299186681Sed
300186681Sedvoid
301186681Sedteken_set_cursor(teken_t *t, const teken_pos_t *p)
302186681Sed{
303186681Sed
304186681Sed	/* XXX: bounds checking with originreg! */
305186681Sed	teken_assert(p->tp_row < t->t_winsize.tp_row);
306186681Sed	teken_assert(p->tp_col < t->t_winsize.tp_col);
307186681Sed
308186681Sed	t->t_cursor = *p;
309186681Sed}
310186681Sed
311186681Sedvoid
312186681Sedteken_set_defattr(teken_t *t, const teken_attr_t *a)
313186681Sed{
314186681Sed
315186681Sed	t->t_curattr = t->t_saved_curattr = t->t_defattr = *a;
316186681Sed}
317186681Sed
318186681Sedvoid
319186681Sedteken_set_winsize(teken_t *t, const teken_pos_t *p)
320186681Sed{
321186681Sed
322186681Sed	teken_assert(p->tp_col <= T_NUMCOL);
323186681Sed
324186681Sed	t->t_winsize = *p;
325186681Sed	/* XXX: bounds checking with cursor/etc! */
326186681Sed	t->t_scrollreg.ts_begin = 0;
327186681Sed	t->t_scrollreg.ts_end = t->t_winsize.tp_row;
328186681Sed	t->t_originreg = t->t_scrollreg;
329186681Sed}
330186681Sed
331186681Sed/*
332186681Sed * State machine.
333186681Sed */
334186681Sed
335186681Sedstatic void
336186681Sedteken_state_switch(teken_t *t, teken_state_t *s)
337186681Sed{
338186681Sed
339186681Sed	t->t_nextstate = s;
340186681Sed	t->t_curnum = 0;
341186681Sed	t->t_stateflags |= TS_FIRSTDIGIT;
342186681Sed}
343186681Sed
344186681Sedstatic int
345186681Sedteken_state_numbers(teken_t *t, teken_char_t c)
346186681Sed{
347186681Sed
348186681Sed	teken_assert(t->t_curnum < T_NUMSIZE);
349186681Sed
350186681Sed	if (c >= '0' && c <= '9') {
351186681Sed		/*
352186681Sed		 * Don't do math with the default value of 1 when a
353186681Sed		 * custom number is inserted.
354186681Sed		 */
355186681Sed		if (t->t_stateflags & TS_FIRSTDIGIT) {
356186681Sed			t->t_stateflags &= ~TS_FIRSTDIGIT;
357186681Sed			t->t_nums[t->t_curnum] = 0;
358186681Sed		} else {
359186681Sed			t->t_nums[t->t_curnum] *= 10;
360186681Sed		}
361186681Sed
362186681Sed		t->t_nums[t->t_curnum] += c - '0';
363186681Sed		return (1);
364186681Sed	} else if (c == ';') {
365186681Sed		if (t->t_stateflags & TS_FIRSTDIGIT)
366186681Sed			t->t_nums[t->t_curnum] = 0;
367186681Sed
368186681Sed		/* Only allow a limited set of arguments. */
369186681Sed		if (++t->t_curnum == T_NUMSIZE) {
370186681Sed			teken_state_switch(t, teken_state_init);
371186681Sed			return (1);
372186681Sed		}
373186681Sed
374186681Sed		t->t_stateflags |= TS_FIRSTDIGIT;
375186681Sed		return (1);
376186681Sed	} else {
377186681Sed		if (t->t_stateflags & TS_FIRSTDIGIT && t->t_curnum > 0) {
378186681Sed			/* Finish off the last empty argument. */
379186681Sed			t->t_nums[t->t_curnum] = 0;
380186681Sed			t->t_curnum++;
381186681Sed		} else if ((t->t_stateflags & TS_FIRSTDIGIT) == 0) {
382186681Sed			/* Also count the last argument. */
383186681Sed			t->t_curnum++;
384186681Sed		}
385186681Sed	}
386186681Sed
387186681Sed	return (0);
388186681Sed}
389186681Sed
390186681Sed#include "teken_state.h"
391