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