teken_subr.h revision 186687
1/*-
2 * Copyright (c) 2008-2009 Ed Schouten <ed@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
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.
13 *
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
24 * SUCH DAMAGE.
25 *
26 * $FreeBSD: head/sys/dev/syscons/teken/teken_subr.h 186687 2009-01-01 23:22:01Z ed $
27 */
28
29static void teken_subr_cursor_up(teken_t *, unsigned int);
30static void teken_subr_erase_line(teken_t *, unsigned int);
31static void teken_subr_regular_character(teken_t *, teken_char_t);
32static void teken_subr_reset_to_initial_state(teken_t *);
33
34static inline int
35teken_tab_isset(teken_t *t, unsigned int col)
36{
37	unsigned int b, o;
38
39	teken_assert(col <= T_NUMCOL);
40
41	b = col / (sizeof(unsigned int) * 8);
42	o = col % (sizeof(unsigned int) * 8);
43
44	return (t->t_tabstops[b] & (1 << o));
45}
46
47static inline void
48teken_tab_clear(teken_t *t, unsigned int col)
49{
50	unsigned int b, o;
51
52	teken_assert(col <= T_NUMCOL);
53
54	b = col / (sizeof(unsigned int) * 8);
55	o = col % (sizeof(unsigned int) * 8);
56
57	t->t_tabstops[b] &= ~(1 << o);
58}
59
60static inline void
61teken_tab_set(teken_t *t, unsigned int col)
62{
63	unsigned int b, o;
64
65	teken_assert(col <= T_NUMCOL);
66
67	b = col / (sizeof(unsigned int) * 8);
68	o = col % (sizeof(unsigned int) * 8);
69
70	t->t_tabstops[b] |= 1 << o;
71}
72
73static void
74teken_tab_default(teken_t *t)
75{
76	unsigned int i;
77
78	memset(&t->t_tabstops, 0, T_NUMCOL / 8);
79
80	for (i = 8; i < T_NUMCOL; i += 8)
81		teken_tab_set(t, i);
82}
83
84static void
85teken_subr_do_scroll(teken_t *t, int amount)
86{
87	teken_rect_t tr;
88	teken_pos_t tp;
89
90	teken_assert(t->t_cursor.tp_row <= t->t_winsize.tp_row);
91	teken_assert(t->t_scrollreg.ts_end <= t->t_winsize.tp_row);
92	teken_assert(amount != 0);
93
94	/* Copy existing data 1 line up. */
95	if (amount > 0) {
96		/* Scroll down. */
97
98		/* Copy existing data up. */
99		if (t->t_scrollreg.ts_begin + amount < t->t_scrollreg.ts_end) {
100			tr.tr_begin.tp_row = t->t_scrollreg.ts_begin + amount;
101			tr.tr_begin.tp_col = 0;
102			tr.tr_end.tp_row = t->t_scrollreg.ts_end;
103			tr.tr_end.tp_col = t->t_winsize.tp_col;
104			tp.tp_row = t->t_scrollreg.ts_begin;
105			tp.tp_col = 0;
106			teken_funcs_copy(t, &tr, &tp);
107
108			tr.tr_begin.tp_row = t->t_scrollreg.ts_end - amount;
109		} else {
110			tr.tr_begin.tp_row = t->t_scrollreg.ts_begin;
111		}
112
113		/* Clear the last lines. */
114		tr.tr_begin.tp_col = 0;
115		tr.tr_end.tp_row = t->t_scrollreg.ts_end;
116		tr.tr_end.tp_col = t->t_winsize.tp_col;
117		teken_funcs_fill(t, &tr, BLANK, &t->t_curattr);
118	} else {
119		/* Scroll up. */
120		amount = -amount;
121
122		/* Copy existing data down. */
123		if (t->t_scrollreg.ts_begin + amount < t->t_scrollreg.ts_end) {
124			tr.tr_begin.tp_row = t->t_scrollreg.ts_begin;
125			tr.tr_begin.tp_col = 0;
126			tr.tr_end.tp_row = t->t_scrollreg.ts_end - amount;
127			tr.tr_end.tp_col = t->t_winsize.tp_col;
128			tp.tp_row = t->t_scrollreg.ts_begin + amount;
129			tp.tp_col = 0;
130			teken_funcs_copy(t, &tr, &tp);
131
132			tr.tr_end.tp_row = t->t_scrollreg.ts_begin + amount;
133		} else {
134			tr.tr_end.tp_row = t->t_scrollreg.ts_end;
135		}
136
137		/* Clear the first lines. */
138		tr.tr_begin.tp_row = t->t_scrollreg.ts_begin;
139		tr.tr_begin.tp_col = 0;
140		tr.tr_end.tp_col = t->t_winsize.tp_col;
141		teken_funcs_fill(t, &tr, BLANK, &t->t_curattr);
142	}
143}
144
145static ssize_t
146teken_subr_do_cpr(teken_t *t, unsigned int cmd, char response[16])
147{
148
149	switch (cmd) {
150	case 5: /* Operating status. */
151		strcpy(response, "0n");
152		return (2);
153	case 6: { /* Cursor position. */
154		int len;
155
156		len = snprintf(response, 16, "%u;%uR",
157		    (t->t_cursor.tp_row - t->t_originreg.ts_begin) + 1,
158		    t->t_cursor.tp_col + 1);
159
160		if (len >= 16)
161			return (-1);
162		return (len);
163	}
164	case 15: /* Printer status. */
165		strcpy(response, "13n");
166		return (3);
167	case 25: /* UDK status. */
168		strcpy(response, "20n");
169		return (3);
170	case 26: /* Keyboard status. */
171		strcpy(response, "27;1n");
172		return (5);
173	default:
174		teken_printf("Unknown DSR\n");
175		return (-1);
176	}
177}
178
179static void
180teken_subr_alignment_test(teken_t *t)
181{
182	teken_rect_t tr;
183
184	t->t_scrollreg.ts_begin = 0;
185	t->t_scrollreg.ts_end = t->t_winsize.tp_row;
186
187	t->t_cursor.tp_row = t->t_cursor.tp_col = 0;
188	t->t_stateflags &= ~TS_WRAPPED;
189	teken_funcs_cursor(t);
190
191	tr.tr_begin.tp_row = 0;
192	tr.tr_begin.tp_col = 0;
193	tr.tr_end = t->t_winsize;
194	teken_funcs_fill(t, &tr, 'E', &t->t_defattr);
195}
196
197static void
198teken_subr_backspace(teken_t *t)
199{
200
201	if (t->t_cursor.tp_col == 0)
202		return;
203
204	t->t_cursor.tp_col--;
205	t->t_stateflags &= ~TS_WRAPPED;
206
207	teken_funcs_cursor(t);
208}
209
210static void
211teken_subr_bell(teken_t *t)
212{
213
214	teken_funcs_bell(t);
215}
216
217static void
218teken_subr_carriage_return(teken_t *t)
219{
220
221	t->t_cursor.tp_col = 0;
222	t->t_stateflags &= ~TS_WRAPPED;
223	teken_funcs_cursor(t);
224}
225
226static void
227teken_subr_cursor_backward(teken_t *t, unsigned int ncols)
228{
229
230	if (ncols > t->t_cursor.tp_col)
231		t->t_cursor.tp_col = 0;
232	else
233		t->t_cursor.tp_col -= ncols;
234	t->t_stateflags &= ~TS_WRAPPED;
235	teken_funcs_cursor(t);
236}
237
238static void
239teken_subr_cursor_backward_tabulation(teken_t *t, unsigned int ntabs)
240{
241
242	do {
243		/* Stop when we've reached the beginning of the line. */
244		if (t->t_cursor.tp_col == 0)
245			break;
246
247		t->t_cursor.tp_col--;
248
249		/* Tab marker set. */
250		if (teken_tab_isset(t, t->t_cursor.tp_col))
251			ntabs--;
252	} while (ntabs > 0);
253}
254
255static void
256teken_subr_cursor_down(teken_t *t, unsigned int nrows)
257{
258
259	if (t->t_cursor.tp_row + nrows >= t->t_scrollreg.ts_end)
260		t->t_cursor.tp_row = t->t_scrollreg.ts_end - 1;
261	else
262		t->t_cursor.tp_row += nrows;
263	t->t_stateflags &= ~TS_WRAPPED;
264	teken_funcs_cursor(t);
265}
266
267static void
268teken_subr_cursor_forward(teken_t *t, unsigned int ncols)
269{
270
271	if (t->t_cursor.tp_col + ncols >= t->t_winsize.tp_col)
272		t->t_cursor.tp_col = t->t_winsize.tp_col - 1;
273	else
274		t->t_cursor.tp_col += ncols;
275	t->t_stateflags &= ~TS_WRAPPED;
276	teken_funcs_cursor(t);
277}
278
279static void
280teken_subr_cursor_forward_tabulation(teken_t *t, unsigned int ntabs)
281{
282
283	do {
284		/* Stop when we've reached the end of the line. */
285		if (t->t_cursor.tp_col == t->t_winsize.tp_col - 1)
286			break;
287
288		t->t_cursor.tp_col++;
289
290		/* Tab marker set. */
291		if (teken_tab_isset(t, t->t_cursor.tp_col))
292			ntabs--;
293	} while (ntabs > 0);
294}
295
296static void
297teken_subr_cursor_next_line(teken_t *t, unsigned int ncols)
298{
299
300	t->t_cursor.tp_col = 0;
301	teken_subr_cursor_down(t, ncols);
302}
303
304static void
305teken_subr_cursor_position(teken_t *t, unsigned int row, unsigned int col)
306{
307
308	t->t_cursor.tp_row = t->t_originreg.ts_begin + row - 1;
309	if (row >= t->t_originreg.ts_end)
310		t->t_cursor.tp_row = t->t_originreg.ts_end - 1;
311
312	t->t_cursor.tp_col = col - 1;
313	if (t->t_cursor.tp_col >= t->t_winsize.tp_col)
314		t->t_cursor.tp_col = t->t_winsize.tp_col - 1;
315
316	t->t_stateflags &= ~TS_WRAPPED;
317	teken_funcs_cursor(t);
318}
319
320static void
321teken_subr_cursor_position_report(teken_t *t, unsigned int cmd)
322{
323	char response[18] = "\x1B[";
324	ssize_t len;
325
326	len = teken_subr_do_cpr(t, cmd, response + 2);
327	if (len < 0)
328		return;
329
330	teken_funcs_respond(t, response, len + 2);
331}
332
333static void
334teken_subr_cursor_previous_line(teken_t *t, unsigned int ncols)
335{
336
337	t->t_cursor.tp_col = 0;
338	teken_subr_cursor_up(t, ncols);
339}
340
341static void
342teken_subr_cursor_up(teken_t *t, unsigned int nrows)
343{
344
345	if (t->t_scrollreg.ts_begin + nrows >= t->t_cursor.tp_row)
346		t->t_cursor.tp_row = t->t_scrollreg.ts_begin;
347	else
348		t->t_cursor.tp_row -= nrows;
349	t->t_stateflags &= ~TS_WRAPPED;
350	teken_funcs_cursor(t);
351}
352
353static void
354teken_subr_delete_character(teken_t *t, unsigned int ncols)
355{
356	teken_rect_t tr;
357
358	tr.tr_begin.tp_row = t->t_cursor.tp_row;
359	tr.tr_end.tp_row = t->t_cursor.tp_row + 1;
360	tr.tr_end.tp_col = t->t_winsize.tp_col;
361
362	if (t->t_cursor.tp_col + ncols >= t->t_winsize.tp_col) {
363		tr.tr_begin.tp_col = t->t_cursor.tp_col;
364	} else {
365		/* Copy characters to the left. */
366		tr.tr_begin.tp_col = t->t_cursor.tp_col + ncols;
367		teken_funcs_copy(t, &tr, &t->t_cursor);
368
369		tr.tr_begin.tp_col = t->t_winsize.tp_col - ncols;
370	}
371
372	/* Blank trailing columns. */
373	teken_funcs_fill(t, &tr, BLANK, &t->t_curattr);
374}
375
376static void
377teken_subr_delete_line(teken_t *t, unsigned int nrows)
378{
379	teken_rect_t tr;
380
381	tr.tr_begin.tp_col = 0;
382	tr.tr_end.tp_row = t->t_scrollreg.ts_end;
383	tr.tr_end.tp_col = t->t_winsize.tp_col;
384
385	if (t->t_cursor.tp_row + nrows >= t->t_scrollreg.ts_end) {
386		tr.tr_begin.tp_row = t->t_cursor.tp_row;
387	} else {
388		teken_pos_t tp;
389
390		/* Copy rows up. */
391		tr.tr_begin.tp_row = t->t_cursor.tp_row + nrows;
392		tp.tp_row = t->t_cursor.tp_row;
393		tp.tp_col = 0;
394		teken_funcs_copy(t, &tr, &tp);
395
396		tr.tr_begin.tp_row = t->t_scrollreg.ts_end - nrows;
397	}
398
399	/* Blank trailing rows. */
400	teken_funcs_fill(t, &tr, BLANK, &t->t_curattr);
401}
402
403static void
404teken_subr_device_control_string(teken_t *t __unused)
405{
406
407	teken_printf("device control string???\n");
408}
409
410static void
411teken_subr_device_status_report(teken_t *t, unsigned int cmd)
412{
413	char response[19] = "\x1B[?";
414	ssize_t len;
415
416	len = teken_subr_do_cpr(t, cmd, response + 3);
417	if (len < 0)
418		return;
419
420	teken_funcs_respond(t, response, len + 3);
421}
422
423static void
424teken_subr_double_height_double_width_line_top(teken_t *t __unused)
425{
426
427	teken_printf("double height double width top\n");
428}
429
430static void
431teken_subr_double_height_double_width_line_bottom(teken_t *t __unused)
432{
433
434	teken_printf("double height double width bottom\n");
435}
436
437static void
438teken_subr_erase_character(teken_t *t, unsigned int ncols)
439{
440	teken_rect_t tr;
441
442	tr.tr_begin = t->t_cursor;
443	tr.tr_end.tp_row = t->t_cursor.tp_row + 1;
444
445	if (t->t_cursor.tp_col + ncols >= t->t_winsize.tp_col)
446		tr.tr_end.tp_col = t->t_winsize.tp_col;
447	else
448		tr.tr_end.tp_col = t->t_cursor.tp_col + ncols;
449
450	teken_funcs_fill(t, &tr, BLANK, &t->t_curattr);
451}
452
453static void
454teken_subr_erase_display(teken_t *t, unsigned int mode)
455{
456	teken_rect_t r;
457
458	r.tr_begin.tp_col = 0;
459	r.tr_end.tp_col = t->t_winsize.tp_col;
460
461	switch (mode) {
462	case 1: /* Erase from the top to the cursor. */
463		teken_subr_erase_line(t, 1);
464
465		/* Erase lines above. */
466		if (t->t_cursor.tp_row == 0)
467			return;
468		r.tr_begin.tp_row = 0;
469		r.tr_end.tp_row = t->t_cursor.tp_row;
470		break;
471	case 2: /* Erase entire display. */
472		r.tr_begin.tp_row = 0;
473		r.tr_end.tp_row = t->t_winsize.tp_row;
474		break;
475	default: /* Erase from cursor to the bottom. */
476		teken_subr_erase_line(t, 0);
477
478		/* Erase lines below. */
479		if (t->t_cursor.tp_row == t->t_winsize.tp_row - 1)
480			return;
481		r.tr_begin.tp_row = t->t_cursor.tp_row + 1;
482		r.tr_end.tp_row = t->t_winsize.tp_row;
483		break;
484	}
485
486	teken_funcs_fill(t, &r, BLANK, &t->t_curattr);
487}
488
489static void
490teken_subr_erase_line(teken_t *t, unsigned int mode)
491{
492	teken_rect_t r;
493
494	r.tr_begin.tp_row = t->t_cursor.tp_row;
495	r.tr_end.tp_row = t->t_cursor.tp_row + 1;
496
497	switch (mode) {
498	case 1: /* Erase from the beginning of the line to the cursor. */
499		r.tr_begin.tp_col = 0;
500		r.tr_end.tp_col = t->t_cursor.tp_col + 1;
501		break;
502	case 2: /* Erase entire line. */
503		r.tr_begin.tp_col = 0;
504		r.tr_end.tp_col = t->t_winsize.tp_col;
505		break;
506	default: /* Erase from cursor to the end of the line. */
507		r.tr_begin.tp_col = t->t_cursor.tp_col;
508		r.tr_end.tp_col = t->t_winsize.tp_col;
509		break;
510	}
511
512	teken_funcs_fill(t, &r, BLANK, &t->t_curattr);
513}
514
515static void
516teken_subr_horizontal_position_absolute(teken_t *t, unsigned int col)
517{
518
519	t->t_cursor.tp_col = col - 1;
520	if (t->t_cursor.tp_col >= t->t_winsize.tp_col)
521		t->t_cursor.tp_col = t->t_winsize.tp_col - 1;
522
523	t->t_stateflags &= ~TS_WRAPPED;
524	teken_funcs_cursor(t);
525}
526
527static void
528teken_subr_horizontal_tab(teken_t *t)
529{
530	teken_rect_t tr;
531
532	tr.tr_begin = t->t_cursor;
533	teken_subr_cursor_forward_tabulation(t, 1);
534	tr.tr_end.tp_row = tr.tr_begin.tp_row + 1;
535	tr.tr_end.tp_col = t->t_cursor.tp_col;
536
537	/* Blank region that we skipped. */
538	if (tr.tr_end.tp_col > tr.tr_begin.tp_col)
539		teken_funcs_fill(t, &tr, BLANK, &t->t_curattr);
540}
541
542static void
543teken_subr_horizontal_tab_set(teken_t *t)
544{
545
546	teken_tab_set(t, t->t_cursor.tp_col);
547}
548
549static void
550teken_subr_index(teken_t *t)
551{
552
553	if (t->t_cursor.tp_row < t->t_scrollreg.ts_end - 1) {
554		t->t_cursor.tp_row++;
555		t->t_stateflags &= ~TS_WRAPPED;
556		teken_funcs_cursor(t);
557	} else {
558		teken_subr_do_scroll(t, 1);
559	}
560}
561
562static void
563teken_subr_insert_character(teken_t *t, unsigned int ncols)
564{
565	teken_rect_t tr;
566
567	tr.tr_begin = t->t_cursor;
568	tr.tr_end.tp_row = t->t_cursor.tp_row + 1;
569
570	if (t->t_cursor.tp_col + ncols >= t->t_winsize.tp_col) {
571		tr.tr_end.tp_col = t->t_winsize.tp_col;
572	} else {
573		teken_pos_t tp;
574
575		/* Copy characters to the right. */
576		tr.tr_end.tp_col = t->t_winsize.tp_col - ncols;
577		tp.tp_row = t->t_cursor.tp_row;
578		tp.tp_col = t->t_cursor.tp_col + ncols;
579		teken_funcs_copy(t, &tr, &tp);
580
581		tr.tr_end.tp_col = t->t_cursor.tp_col + ncols;
582	}
583
584	/* Blank current location. */
585	teken_funcs_fill(t, &tr, BLANK, &t->t_curattr);
586}
587
588static void
589teken_subr_insert_line(teken_t *t, unsigned int nrows)
590{
591	teken_rect_t tr;
592
593	tr.tr_begin.tp_row = t->t_cursor.tp_row;
594	tr.tr_begin.tp_col = 0;
595	tr.tr_end.tp_col = t->t_winsize.tp_col;
596
597	if (t->t_cursor.tp_row + nrows >= t->t_scrollreg.ts_end) {
598		tr.tr_end.tp_row = t->t_scrollreg.ts_end;
599	} else {
600		teken_pos_t tp;
601
602		/* Copy lines down. */
603		tr.tr_end.tp_row = t->t_scrollreg.ts_end - nrows;
604		tp.tp_row = t->t_cursor.tp_row + nrows;
605		tp.tp_col = 0;
606		teken_funcs_copy(t, &tr, &tp);
607
608		tr.tr_end.tp_row = t->t_cursor.tp_row + nrows;
609	}
610
611	/* Blank current location. */
612	teken_funcs_fill(t, &tr, BLANK, &t->t_curattr);
613}
614
615static void
616teken_subr_keypad_application_mode(teken_t *t)
617{
618
619	teken_funcs_param(t, TP_KEYPADAPP, 1);
620}
621
622static void
623teken_subr_keypad_numeric_mode(teken_t *t)
624{
625
626	teken_funcs_param(t, TP_KEYPADAPP, 0);
627}
628
629static void
630teken_subr_newline(teken_t *t)
631{
632
633	t->t_cursor.tp_row++;
634
635	if (t->t_cursor.tp_row >= t->t_scrollreg.ts_end) {
636		teken_subr_do_scroll(t, 1);
637		t->t_cursor.tp_row = t->t_scrollreg.ts_end - 1;
638	}
639
640	t->t_stateflags &= ~TS_WRAPPED;
641	teken_funcs_cursor(t);
642}
643
644static void
645teken_subr_next_line(teken_t *t)
646{
647
648	t->t_cursor.tp_col = 0;
649	teken_subr_newline(t);
650}
651
652static void
653teken_subr_pan_down(teken_t *t, unsigned int nrows)
654{
655
656	teken_subr_do_scroll(t, (int)nrows);
657}
658
659static void
660teken_subr_pan_up(teken_t *t, unsigned int nrows)
661{
662
663	teken_subr_do_scroll(t, -(int)nrows);
664}
665
666static void
667teken_subr_primary_device_attributes(teken_t *t, unsigned int request)
668{
669
670	if (request == 0) {
671		const char response[] = "\x1B[?1;2c";
672
673		teken_funcs_respond(t, response, sizeof response - 1);
674	} else {
675		teken_printf("Unknown DA1\n");
676	}
677}
678
679static void
680teken_subr_do_putchar(teken_t *t, const teken_pos_t *tp, teken_char_t c,
681    int width)
682{
683
684	if (t->t_stateflags & TS_INSERT &&
685	    tp->tp_col < t->t_winsize.tp_col - width) {
686		teken_rect_t ctr;
687		teken_pos_t ctp;
688
689		/* Insert mode. Move existing characters to the right. */
690		ctr.tr_begin = *tp;
691		ctr.tr_end.tp_row = tp->tp_row + 1;
692		ctr.tr_end.tp_col = t->t_winsize.tp_col - width;
693		ctp.tp_row = tp->tp_row;
694		ctp.tp_col = tp->tp_col + width;
695		teken_funcs_copy(t, &ctr, &ctp);
696	}
697
698	teken_funcs_putchar(t, tp, c, &t->t_curattr);
699}
700
701static void
702teken_subr_regular_character(teken_t *t, teken_char_t c)
703{
704	int width;
705
706	/* XXX: Don't process zero-width characters yet. */
707	width = teken_wcwidth(c);
708	if (width <= 0)
709		return;
710
711	if (t->t_cursor.tp_col == t->t_winsize.tp_col - 1 &&
712	    (t->t_stateflags & (TS_WRAPPED|TS_AUTOWRAP)) ==
713	    (TS_WRAPPED|TS_AUTOWRAP)) {
714		teken_pos_t tp;
715
716		/* Perform line wrapping. */
717
718		if (t->t_cursor.tp_row == t->t_scrollreg.ts_end - 1) {
719			/* Perform scrolling. */
720			teken_subr_do_scroll(t, 1);
721			tp.tp_row = t->t_scrollreg.ts_end - 1;
722		} else {
723			/* No scrolling needed. */
724			tp.tp_row = t->t_cursor.tp_row + 1;
725			if (tp.tp_row == t->t_winsize.tp_row) {
726				/*
727				 * Corner case: regular character
728				 * outside scrolling region, but at the
729				 * bottom of the screen.
730				 */
731				teken_subr_do_putchar(t, &t->t_cursor,
732				    c, width);
733				return;
734			}
735		}
736
737		tp.tp_col = 0;
738		teken_subr_do_putchar(t, &tp, c, width);
739
740		t->t_cursor.tp_row = tp.tp_row;
741		t->t_cursor.tp_col = width;
742		t->t_stateflags &= ~TS_WRAPPED;
743	} else {
744		/* No line wrapping needed. */
745		teken_subr_do_putchar(t, &t->t_cursor, c, width);
746		t->t_cursor.tp_col += width;
747
748		if (t->t_cursor.tp_col >= t->t_winsize.tp_col) {
749			t->t_stateflags |= TS_WRAPPED;
750			t->t_cursor.tp_col = t->t_winsize.tp_col - 1;
751		} else {
752			t->t_stateflags &= ~TS_WRAPPED;
753		}
754	}
755
756	teken_funcs_cursor(t);
757}
758
759static void
760teken_subr_reset_dec_mode(teken_t *t, unsigned int cmd)
761{
762
763	switch (cmd) {
764	case 1: /* Cursor keys mode. */
765		teken_funcs_param(t, TP_CURSORKEYS, 0);
766		break;
767	case 2: /* DECANM: ANSI/VT52 mode. */
768		teken_printf("DECRST VT52\n");
769		break;
770	case 3: /* 132 column mode. */
771		teken_funcs_param(t, TP_132COLS, 0);
772		teken_subr_reset_to_initial_state(t);
773		break;
774	case 5: /* Inverse video. */
775		teken_printf("DECRST inverse video\n");
776		break;
777	case 6: /* Origin mode. */
778		t->t_stateflags &= ~TS_ORIGIN;
779		t->t_originreg.ts_begin = 0;
780		t->t_originreg.ts_end = t->t_winsize.tp_row;
781		t->t_cursor.tp_row = t->t_cursor.tp_col = 0;
782		t->t_stateflags &= ~TS_WRAPPED;
783		teken_funcs_cursor(t);
784		break;
785	case 7: /* Autowrap mode. */
786		t->t_stateflags &= ~TS_AUTOWRAP;
787		break;
788	case 8: /* Autorepeat mode. */
789		teken_funcs_param(t, TP_AUTOREPEAT, 0);
790		break;
791	case 25: /* Hide cursor. */
792		teken_funcs_param(t, TP_SHOWCURSOR, 0);
793		break;
794	case 40: /* Disallow 132 columns. */
795		teken_printf("DECRST allow 132\n");
796		break;
797	case 45: /* Disable reverse wraparound. */
798		teken_printf("DECRST reverse wraparound\n");
799		break;
800	case 47: /* Switch to alternate buffer. */
801		teken_printf("Switch to alternate buffer\n");
802		break;
803	default:
804		teken_printf("Unknown DECRST: %u\n", cmd);
805	}
806}
807
808static void
809teken_subr_reset_mode(teken_t *t, unsigned int cmd)
810{
811
812	switch (cmd) {
813	case 4:
814		t->t_stateflags &= ~TS_INSERT;
815		break;
816	default:
817		teken_printf("Unknown reset mode: %u\n", cmd);
818	}
819}
820
821static void
822teken_subr_do_reset(teken_t *t)
823{
824
825	t->t_curattr = t->t_saved_curattr = t->t_defattr;
826	t->t_cursor.tp_row = t->t_cursor.tp_col = 0;
827	t->t_saved_cursor = t->t_cursor;
828	t->t_stateflags = TS_AUTOWRAP;
829
830	teken_tab_default(t);
831}
832
833static void
834teken_subr_reset_to_initial_state(teken_t *t)
835{
836
837	teken_subr_do_reset(t);
838	teken_subr_erase_display(t, 2);
839	teken_funcs_cursor(t);
840}
841
842static void
843teken_subr_restore_cursor(teken_t *t)
844{
845
846	t->t_cursor = t->t_saved_cursor;
847	t->t_curattr = t->t_saved_curattr;
848	t->t_stateflags &= ~TS_WRAPPED;
849	teken_funcs_cursor(t);
850}
851
852static void
853teken_subr_reverse_index(teken_t *t)
854{
855
856	if (t->t_cursor.tp_row > t->t_scrollreg.ts_begin) {
857		t->t_cursor.tp_row--;
858		t->t_stateflags &= ~TS_WRAPPED;
859		teken_funcs_cursor(t);
860	} else {
861		teken_subr_do_scroll(t, -1);
862	}
863}
864
865static void
866teken_subr_save_cursor(teken_t *t)
867{
868
869	t->t_saved_cursor = t->t_cursor;
870	t->t_saved_curattr = t->t_curattr;
871}
872
873static void
874teken_subr_scs(teken_t *t __unused)
875{
876
877	teken_printf("scs???\n");
878}
879
880static void
881teken_subr_secondary_device_attributes(teken_t *t, unsigned int request)
882{
883
884	if (request == 0) {
885		const char response[] = "\x1B[>0;10;0c";
886		teken_funcs_respond(t, response, sizeof response - 1);
887	} else {
888		teken_printf("Unknown DA2\n");
889	}
890}
891
892static void
893teken_subr_set_dec_mode(teken_t *t, unsigned int cmd)
894{
895
896	switch (cmd) {
897	case 1: /* Cursor keys mode. */
898		teken_funcs_param(t, TP_CURSORKEYS, 1);
899		break;
900	case 2: /* DECANM: ANSI/VT52 mode. */
901		teken_printf("DECSET VT52\n");
902		break;
903	case 3: /* 132 column mode. */
904		teken_funcs_param(t, TP_132COLS, 1);
905		teken_subr_reset_to_initial_state(t);
906		break;
907	case 5: /* Inverse video. */
908		teken_printf("DECSET inverse video\n");
909		break;
910	case 6: /* Origin mode. */
911		t->t_stateflags |= TS_ORIGIN;
912		t->t_originreg = t->t_scrollreg;
913		t->t_cursor.tp_row = t->t_scrollreg.ts_begin;
914		t->t_cursor.tp_col = 0;
915		t->t_stateflags &= ~TS_WRAPPED;
916		teken_funcs_cursor(t);
917		break;
918	case 7: /* Autowrap mode. */
919		t->t_stateflags |= TS_AUTOWRAP;
920		break;
921	case 8: /* Autorepeat mode. */
922		teken_funcs_param(t, TP_AUTOREPEAT, 1);
923		break;
924	case 25: /* Display cursor. */
925		teken_funcs_param(t, TP_SHOWCURSOR, 1);
926		break;
927	case 40: /* Allow 132 columns. */
928		teken_printf("DECSET allow 132\n");
929		break;
930	case 45: /* Enable reverse wraparound. */
931		teken_printf("DECSET reverse wraparound\n");
932		break;
933	case 47: /* Switch to alternate buffer. */
934		teken_printf("Switch away from alternate buffer\n");
935		break;
936	default:
937		teken_printf("Unknown DECSET: %u\n", cmd);
938	}
939}
940
941static void
942teken_subr_set_mode(teken_t *t, unsigned int cmd)
943{
944
945	switch (cmd) {
946	case 4:
947		teken_printf("Insert mode\n");
948		t->t_stateflags |= TS_INSERT;
949		break;
950	default:
951		teken_printf("Unknown set mode: %u\n", cmd);
952	}
953}
954
955static void
956teken_subr_set_graphic_rendition(teken_t *t, unsigned int ncmds,
957    unsigned int cmds[])
958{
959	unsigned int i, n;
960
961	/* No attributes means reset. */
962	if (ncmds == 0) {
963		t->t_curattr = t->t_defattr;
964		return;
965	}
966
967	for (i = 0; i < ncmds; i++) {
968		n = cmds[i];
969
970		switch (n) {
971		case 0: /* Reset. */
972			t->t_curattr = t->t_defattr;
973			break;
974		case 1: /* Bold. */
975			t->t_curattr.ta_format |= TF_BOLD;
976			break;
977		case 4: /* Underline. */
978			t->t_curattr.ta_format |= TF_UNDERLINE;
979			break;
980		case 5: /* Blink. */
981			t->t_curattr.ta_format |= TF_BLINK;
982			break;
983		case 7: /* Reverse. */
984			t->t_curattr.ta_format |= TF_REVERSE;
985			break;
986		case 22: /* Remove bold. */
987			t->t_curattr.ta_format &= ~TF_BOLD;
988			break;
989		case 24: /* Remove underline. */
990			t->t_curattr.ta_format &= ~TF_UNDERLINE;
991			break;
992		case 25: /* Remove blink. */
993			t->t_curattr.ta_format &= ~TF_BLINK;
994			break;
995		case 27: /* Remove reverse. */
996			t->t_curattr.ta_format &= ~TF_REVERSE;
997			break;
998		case 30: /* Set foreground color: black */
999		case 31: /* Set foreground color: red */
1000		case 32: /* Set foreground color: green */
1001		case 33: /* Set foreground color: brown */
1002		case 34: /* Set foreground color: blue */
1003		case 35: /* Set foreground color: magenta */
1004		case 36: /* Set foreground color: cyan */
1005		case 37: /* Set foreground color: white */
1006			t->t_curattr.ta_fgcolor = n - 30;
1007			break;
1008		case 39: /* Set default foreground color. */
1009			t->t_curattr.ta_fgcolor = t->t_defattr.ta_fgcolor;
1010			break;
1011		case 40: /* Set background color: black */
1012		case 41: /* Set background color: red */
1013		case 42: /* Set background color: green */
1014		case 43: /* Set background color: brown */
1015		case 44: /* Set background color: blue */
1016		case 45: /* Set background color: magenta */
1017		case 46: /* Set background color: cyan */
1018		case 47: /* Set background color: white */
1019			t->t_curattr.ta_bgcolor = n - 40;
1020			break;
1021		case 49: /* Set default background color. */
1022			t->t_curattr.ta_bgcolor = t->t_defattr.ta_bgcolor;
1023			break;
1024		default:
1025			teken_printf("unsupported attribute %u\n", n);
1026		}
1027	}
1028}
1029
1030static void
1031teken_subr_set_top_and_bottom_margins(teken_t *t, unsigned int top,
1032    unsigned int bottom)
1033{
1034
1035	/* Adjust top row number. */
1036	if (top > 0)
1037		top--;
1038	/* Adjust bottom row number. */
1039	if (bottom == 0 || bottom > t->t_winsize.tp_row)
1040		bottom = t->t_winsize.tp_row;
1041
1042	/* Invalid arguments. */
1043	if (top >= bottom - 1) {
1044		top = 0;
1045		bottom = t->t_winsize.tp_row;
1046	}
1047
1048	t->t_scrollreg.ts_begin = top;
1049	t->t_scrollreg.ts_end = bottom;
1050	if (t->t_stateflags & TS_ORIGIN) {
1051		/* XXX: home cursor? */
1052		t->t_originreg = t->t_scrollreg;
1053		t->t_cursor.tp_row = t->t_originreg.ts_begin;
1054		t->t_cursor.tp_col = 0;
1055		t->t_stateflags &= ~TS_WRAPPED;
1056		teken_funcs_cursor(t);
1057	}
1058}
1059
1060static void
1061teken_subr_single_height_double_width_line(teken_t *t __unused)
1062{
1063
1064	teken_printf("single height double width???\n");
1065}
1066
1067static void
1068teken_subr_single_height_single_width_line(teken_t *t __unused)
1069{
1070
1071	teken_printf("single height single width???\n");
1072}
1073
1074static void
1075teken_subr_string_terminator(teken_t *t __unused)
1076{
1077
1078	teken_printf("string terminator???\n");
1079}
1080
1081static void
1082teken_subr_tab_clear(teken_t *t, unsigned int cmd)
1083{
1084
1085	switch (cmd) {
1086	case 0:
1087		teken_tab_clear(t, t->t_cursor.tp_col);
1088		break;
1089	case 3:
1090		memset(&t->t_tabstops, 0, T_NUMCOL / 8);
1091		break;
1092	}
1093}
1094
1095static void
1096teken_subr_vertical_position_absolute(teken_t *t, unsigned int row)
1097{
1098
1099	t->t_cursor.tp_row = t->t_originreg.ts_begin + row - 1;
1100	if (row >= t->t_originreg.ts_end)
1101		t->t_cursor.tp_row = t->t_originreg.ts_end - 1;
1102
1103
1104	t->t_stateflags &= ~TS_WRAPPED;
1105	teken_funcs_cursor(t);
1106}
1107