2 * Copyright (c) 2008-2009 Ed Schouten <ed@FreeBSD.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 #include <sys/cdefs.h>
30 #if defined(__FreeBSD__) && defined(_KERNEL)
31 #include <sys/param.h>
32 #include <sys/limits.h>
34 #include <sys/systm.h>
35 #define teken_assert(x) MPASS(x)
36 #else /* !(__FreeBSD__ && _KERNEL) */
37 #include <sys/types.h>
43 #define teken_assert(x) assert(x)
44 #endif /* __FreeBSD__ && _KERNEL */
47 #define teken_printf(x,...)
49 /* Private flags for t_stateflags. */
50 #define TS_FIRSTDIGIT 0x0001 /* First numeric digit in escape sequence. */
51 #define TS_INSERT 0x0002 /* Insert mode. */
52 #define TS_AUTOWRAP 0x0004 /* Autowrap. */
53 #define TS_ORIGIN 0x0008 /* Origin mode. */
54 #define TS_WRAPPED 0x0010 /* Next character should be printed on col 0. */
55 #define TS_8BIT 0x0020 /* UTF-8 disabled. */
56 #define TS_CONS25 0x0040 /* cons25 emulation. */
57 #define TS_INSTRING 0x0080 /* Inside string. */
58 #define TS_CURSORKEYS 0x0100 /* Cursor keys mode. */
60 /* Character that blanks a cell. */
64 #include "teken_wcwidth.h"
65 #include "teken_scs.h"
67 static teken_state_t teken_state_init
;
74 teken_funcs_bell(teken_t
*t
)
77 t
->t_funcs
->tf_bell(t
->t_softc
);
81 teken_funcs_cursor(teken_t
*t
)
84 teken_assert(t
->t_cursor
.tp_row
< t
->t_winsize
.tp_row
);
85 teken_assert(t
->t_cursor
.tp_col
< t
->t_winsize
.tp_col
);
87 t
->t_funcs
->tf_cursor(t
->t_softc
, &t
->t_cursor
);
91 teken_funcs_putchar(teken_t
*t
, const teken_pos_t
*p
, teken_char_t c
,
92 const teken_attr_t
*a
)
95 teken_assert(p
->tp_row
< t
->t_winsize
.tp_row
);
96 teken_assert(p
->tp_col
< t
->t_winsize
.tp_col
);
98 t
->t_funcs
->tf_putchar(t
->t_softc
, p
, c
, a
);
102 teken_funcs_fill(teken_t
*t
, const teken_rect_t
*r
,
103 const teken_char_t c
, const teken_attr_t
*a
)
106 teken_assert(r
->tr_end
.tp_row
> r
->tr_begin
.tp_row
);
107 teken_assert(r
->tr_end
.tp_row
<= t
->t_winsize
.tp_row
);
108 teken_assert(r
->tr_end
.tp_col
> r
->tr_begin
.tp_col
);
109 teken_assert(r
->tr_end
.tp_col
<= t
->t_winsize
.tp_col
);
111 t
->t_funcs
->tf_fill(t
->t_softc
, r
, c
, a
);
115 teken_funcs_copy(teken_t
*t
, const teken_rect_t
*r
, const teken_pos_t
*p
)
118 teken_assert(r
->tr_end
.tp_row
> r
->tr_begin
.tp_row
);
119 teken_assert(r
->tr_end
.tp_row
<= t
->t_winsize
.tp_row
);
120 teken_assert(r
->tr_end
.tp_col
> r
->tr_begin
.tp_col
);
121 teken_assert(r
->tr_end
.tp_col
<= t
->t_winsize
.tp_col
);
122 teken_assert(p
->tp_row
+ (r
->tr_end
.tp_row
- r
->tr_begin
.tp_row
) <= t
->t_winsize
.tp_row
);
123 teken_assert(p
->tp_col
+ (r
->tr_end
.tp_col
- r
->tr_begin
.tp_col
) <= t
->t_winsize
.tp_col
);
125 t
->t_funcs
->tf_copy(t
->t_softc
, r
, p
);
129 teken_funcs_param(teken_t
*t
, int cmd
, unsigned int value
)
132 t
->t_funcs
->tf_param(t
->t_softc
, cmd
, value
);
136 teken_funcs_respond(teken_t
*t
, const void *buf
, size_t len
)
139 t
->t_funcs
->tf_respond(t
->t_softc
, buf
, len
);
142 #include "teken_subr.h"
143 #include "teken_subr_compat.h"
146 * Programming interface.
150 teken_init(teken_t
*t
, const teken_funcs_t
*tf
, void *softc
)
152 teken_pos_t tp
= { .tp_row
= 24, .tp_col
= 80 };
157 t
->t_nextstate
= teken_state_init
;
161 t
->t_defattr
.ta_format
= 0;
162 t
->t_defattr
.ta_fgcolor
= TC_WHITE
;
163 t
->t_defattr
.ta_bgcolor
= TC_BLACK
;
164 teken_subr_do_reset(t
);
166 teken_set_winsize(t
, &tp
);
170 teken_input_char(teken_t
*t
, teken_char_t c
)
174 * There is no support for DCS and OSC. Just discard strings
175 * until we receive characters that may indicate string
178 if (t
->t_stateflags
& TS_INSTRING
) {
181 t
->t_stateflags
&= ~TS_INSTRING
;
184 t
->t_stateflags
&= ~TS_INSTRING
;
198 teken_subr_backspace(t
);
202 teken_subr_newline(t
);
205 teken_subr_newpage(t
);
208 if (t
->t_stateflags
& TS_CONS25
)
209 t
->t_nextstate(t
, c
);
214 if (t
->t_stateflags
& TS_CONS25
)
215 t
->t_nextstate(t
, c
);
220 teken_subr_carriage_return(t
);
223 teken_subr_horizontal_tab(t
);
226 t
->t_nextstate(t
, c
);
230 /* Post-processing assertions. */
231 teken_assert(t
->t_cursor
.tp_row
>= t
->t_originreg
.ts_begin
);
232 teken_assert(t
->t_cursor
.tp_row
< t
->t_originreg
.ts_end
);
233 teken_assert(t
->t_cursor
.tp_row
< t
->t_winsize
.tp_row
);
234 teken_assert(t
->t_cursor
.tp_col
< t
->t_winsize
.tp_col
);
235 teken_assert(t
->t_saved_cursor
.tp_row
< t
->t_winsize
.tp_row
);
236 teken_assert(t
->t_saved_cursor
.tp_col
< t
->t_winsize
.tp_col
);
237 teken_assert(t
->t_scrollreg
.ts_end
<= t
->t_winsize
.tp_row
);
238 teken_assert(t
->t_scrollreg
.ts_begin
< t
->t_scrollreg
.ts_end
);
239 /* Origin region has to be window size or the same as scrollreg. */
240 teken_assert((t
->t_originreg
.ts_begin
== t
->t_scrollreg
.ts_begin
&&
241 t
->t_originreg
.ts_end
== t
->t_scrollreg
.ts_end
) ||
242 (t
->t_originreg
.ts_begin
== 0 &&
243 t
->t_originreg
.ts_end
== t
->t_winsize
.tp_row
));
247 teken_input_byte(teken_t
*t
, unsigned char c
)
253 if ((c
& 0x80) == 0x00 || t
->t_stateflags
& TS_8BIT
) {
254 /* One-byte sequence. */
256 teken_input_char(t
, c
);
257 } else if ((c
& 0xe0) == 0xc0) {
258 /* Two-byte sequence. */
260 t
->t_utf8_partial
= c
& 0x1f;
261 } else if ((c
& 0xf0) == 0xe0) {
262 /* Three-byte sequence. */
264 t
->t_utf8_partial
= c
& 0x0f;
265 } else if ((c
& 0xf8) == 0xf0) {
266 /* Four-byte sequence. */
268 t
->t_utf8_partial
= c
& 0x07;
269 } else if ((c
& 0xc0) == 0x80) {
270 if (t
->t_utf8_left
== 0)
273 t
->t_utf8_partial
= (t
->t_utf8_partial
<< 6) | (c
& 0x3f);
274 if (t
->t_utf8_left
== 0) {
275 teken_printf("Got UTF-8 char %x\n", t
->t_utf8_partial
);
276 teken_input_char(t
, t
->t_utf8_partial
);
282 teken_input(teken_t
*t
, const void *buf
, size_t len
)
287 teken_input_byte(t
, *c
++);
291 teken_get_cursor(teken_t
*t
)
294 return (&t
->t_cursor
);
298 teken_set_cursor(teken_t
*t
, const teken_pos_t
*p
)
301 /* XXX: bounds checking with originreg! */
302 teken_assert(p
->tp_row
< t
->t_winsize
.tp_row
);
303 teken_assert(p
->tp_col
< t
->t_winsize
.tp_col
);
309 teken_get_curattr(teken_t
*t
)
312 return (&t
->t_curattr
);
316 teken_set_curattr(teken_t
*t
, const teken_attr_t
*a
)
323 teken_get_defattr(teken_t
*t
)
326 return (&t
->t_defattr
);
330 teken_set_defattr(teken_t
*t
, const teken_attr_t
*a
)
333 t
->t_curattr
= t
->t_saved_curattr
= t
->t_defattr
= *a
;
337 teken_get_winsize(teken_t
*t
)
340 return (&t
->t_winsize
);
344 teken_trim_cursor_pos(teken_t
*t
, const teken_pos_t
*new)
346 const teken_pos_t
*cur
;
350 if (cur
->tp_row
< new->tp_row
|| cur
->tp_col
< new->tp_col
)
352 if (t
->t_cursor
.tp_row
>= new->tp_row
)
353 t
->t_cursor
.tp_row
= new->tp_row
- 1;
354 if (t
->t_cursor
.tp_col
>= new->tp_col
)
355 t
->t_cursor
.tp_col
= new->tp_col
- 1;
359 teken_set_winsize(teken_t
*t
, const teken_pos_t
*p
)
362 teken_trim_cursor_pos(t
, p
);
364 teken_subr_do_reset(t
);
368 teken_set_winsize_noreset(teken_t
*t
, const teken_pos_t
*p
)
371 teken_trim_cursor_pos(t
, p
);
373 teken_subr_do_resize(t
);
377 teken_set_8bit(teken_t
*t
)
380 t
->t_stateflags
|= TS_8BIT
;
384 teken_set_cons25(teken_t
*t
)
387 t
->t_stateflags
|= TS_CONS25
;
395 teken_state_switch(teken_t
*t
, teken_state_t
*s
)
400 t
->t_stateflags
|= TS_FIRSTDIGIT
;
404 teken_state_numbers(teken_t
*t
, teken_char_t c
)
407 teken_assert(t
->t_curnum
< T_NUMSIZE
);
409 if (c
>= '0' && c
<= '9') {
410 if (t
->t_stateflags
& TS_FIRSTDIGIT
) {
412 t
->t_stateflags
&= ~TS_FIRSTDIGIT
;
413 t
->t_nums
[t
->t_curnum
] = c
- '0';
414 } else if (t
->t_nums
[t
->t_curnum
] < UINT_MAX
/ 100) {
416 * There is no need to continue parsing input
417 * once the value exceeds the size of the
418 * terminal. It would only allow for integer
419 * overflows when performing arithmetic on the
422 * Ignore any further digits if the value is
423 * already UINT_MAX / 100.
425 t
->t_nums
[t
->t_curnum
] =
426 t
->t_nums
[t
->t_curnum
] * 10 + c
- '0';
429 } else if (c
== ';') {
430 if (t
->t_stateflags
& TS_FIRSTDIGIT
)
431 t
->t_nums
[t
->t_curnum
] = 0;
433 /* Only allow a limited set of arguments. */
434 if (++t
->t_curnum
== T_NUMSIZE
) {
435 teken_state_switch(t
, teken_state_init
);
439 t
->t_stateflags
|= TS_FIRSTDIGIT
;
442 if (t
->t_stateflags
& TS_FIRSTDIGIT
&& t
->t_curnum
> 0) {
443 /* Finish off the last empty argument. */
444 t
->t_nums
[t
->t_curnum
] = 0;
446 } else if ((t
->t_stateflags
& TS_FIRSTDIGIT
) == 0) {
447 /* Also count the last argument. */
456 teken_256to8(teken_color_t c
)
458 unsigned int r
, g
, b
;
461 /* Traditional color indices. */
463 } else if (c
>= 244) {
464 /* Upper grayscale colors. */
466 } else if (c
>= 232) {
467 /* Lower grayscale colors. */
471 /* Convert to RGB. */
478 /* Possibly green. */
494 /* Possibly brown. */
506 static const char * const special_strings_cons25
[] = {
507 [TKEY_UP
] = "\x1B[A", [TKEY_DOWN
] = "\x1B[B",
508 [TKEY_LEFT
] = "\x1B[D", [TKEY_RIGHT
] = "\x1B[C",
510 [TKEY_HOME
] = "\x1B[H", [TKEY_END
] = "\x1B[F",
511 [TKEY_INSERT
] = "\x1B[L", [TKEY_DELETE
] = "\x7F",
512 [TKEY_PAGE_UP
] = "\x1B[I", [TKEY_PAGE_DOWN
] = "\x1B[G",
514 [TKEY_F1
] = "\x1B[M", [TKEY_F2
] = "\x1B[N",
515 [TKEY_F3
] = "\x1B[O", [TKEY_F4
] = "\x1B[P",
516 [TKEY_F5
] = "\x1B[Q", [TKEY_F6
] = "\x1B[R",
517 [TKEY_F7
] = "\x1B[S", [TKEY_F8
] = "\x1B[T",
518 [TKEY_F9
] = "\x1B[U", [TKEY_F10
] = "\x1B[V",
519 [TKEY_F11
] = "\x1B[W", [TKEY_F12
] = "\x1B[X",
522 static const char * const special_strings_ckeys
[] = {
523 [TKEY_UP
] = "\x1BOA", [TKEY_DOWN
] = "\x1BOB",
524 [TKEY_LEFT
] = "\x1BOD", [TKEY_RIGHT
] = "\x1BOC",
526 [TKEY_HOME
] = "\x1BOH", [TKEY_END
] = "\x1BOF",
529 static const char * const special_strings_normal
[] = {
530 [TKEY_UP
] = "\x1B[A", [TKEY_DOWN
] = "\x1B[B",
531 [TKEY_LEFT
] = "\x1B[D", [TKEY_RIGHT
] = "\x1B[C",
533 [TKEY_HOME
] = "\x1B[H", [TKEY_END
] = "\x1B[F",
534 [TKEY_INSERT
] = "\x1B[2~", [TKEY_DELETE
] = "\x1B[3~",
535 [TKEY_PAGE_UP
] = "\x1B[5~", [TKEY_PAGE_DOWN
] = "\x1B[6~",
537 [TKEY_F1
] = "\x1BOP", [TKEY_F2
] = "\x1BOQ",
538 [TKEY_F3
] = "\x1BOR", [TKEY_F4
] = "\x1BOS",
539 [TKEY_F5
] = "\x1B[15~", [TKEY_F6
] = "\x1B[17~",
540 [TKEY_F7
] = "\x1B[18~", [TKEY_F8
] = "\x1B[19~",
541 [TKEY_F9
] = "\x1B[20~", [TKEY_F10
] = "\x1B[21~",
542 [TKEY_F11
] = "\x1B[23~", [TKEY_F12
] = "\x1B[24~",
546 teken_get_sequence(teken_t
*t
, unsigned int k
)
550 if (t
->t_stateflags
& TS_CONS25
&&
551 k
< sizeof special_strings_cons25
/ sizeof(char *))
552 return (special_strings_cons25
[k
]);
554 /* Cursor keys mode. */
555 if (t
->t_stateflags
& TS_CURSORKEYS
&&
556 k
< sizeof special_strings_ckeys
/ sizeof(char *))
557 return (special_strings_ckeys
[k
]);
559 /* Default xterm sequences. */
560 if (k
< sizeof special_strings_normal
/ sizeof(char *))
561 return (special_strings_normal
[k
]);
566 #include "teken_state.h"