1/* $NetBSD: wsemul_vt100.c,v 1.34 2010/02/10 22:25:08 skrll Exp $ */
2
3/*
4 * Copyright (c) 1998
5 *	Matthias Drochner.  All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 *
27 */
28
29#include <sys/cdefs.h>
30__KERNEL_RCSID(0, "$NetBSD: wsemul_vt100.c,v 1.34 2010/02/10 22:25:08 skrll Exp $");
31
32#include "opt_wsmsgattrs.h"
33
34#include <sys/param.h>
35#include <sys/systm.h>
36#include <sys/time.h>
37#include <sys/malloc.h>
38#include <sys/fcntl.h>
39
40#include <dev/wscons/wsconsio.h>
41#include <dev/wscons/wsdisplayvar.h>
42#include <dev/wscons/wsemulvar.h>
43#include <dev/wscons/wsemul_vt100var.h>
44#include <dev/wscons/ascii.h>
45
46void	*wsemul_vt100_cnattach(const struct wsscreen_descr *, void *,
47			       int, int, long);
48void	*wsemul_vt100_attach(int console, const struct wsscreen_descr *,
49			     void *, int, int, void *, long);
50void	wsemul_vt100_output(void *cookie, const u_char *data, u_int count, int);
51void	wsemul_vt100_detach(void *cookie, u_int *crowp, u_int *ccolp);
52void	wsemul_vt100_resetop(void *, enum wsemul_resetops);
53#ifdef WSDISPLAY_CUSTOM_OUTPUT
54static void wsemul_vt100_getmsgattrs(void *, struct wsdisplay_msgattrs *);
55static void wsemul_vt100_setmsgattrs(void *, const struct wsscreen_descr *,
56                                     const struct wsdisplay_msgattrs *);
57#endif /* WSDISPLAY_CUSTOM_OUTPUT */
58
59const struct wsemul_ops wsemul_vt100_ops = {
60	"vt100",
61	wsemul_vt100_cnattach,
62	wsemul_vt100_attach,
63	wsemul_vt100_output,
64	wsemul_vt100_translate,
65	wsemul_vt100_detach,
66	wsemul_vt100_resetop,
67#ifdef WSDISPLAY_CUSTOM_OUTPUT
68	wsemul_vt100_getmsgattrs,
69	wsemul_vt100_setmsgattrs,
70#else
71	NULL,
72	NULL,
73#endif
74};
75
76struct wsemul_vt100_emuldata wsemul_vt100_console_emuldata;
77
78static void wsemul_vt100_init(struct wsemul_vt100_emuldata *,
79			      const struct wsscreen_descr *,
80			      void *, int, int, long);
81
82static void wsemul_vt100_output_normal(struct wsemul_vt100_emuldata *,
83				       u_char, int);
84static void wsemul_vt100_output_c0c1(struct wsemul_vt100_emuldata *,
85				     u_char, int);
86static void wsemul_vt100_nextline(struct wsemul_vt100_emuldata *);
87typedef u_int vt100_handler(struct wsemul_vt100_emuldata *, u_char);
88
89static vt100_handler
90wsemul_vt100_output_esc,
91wsemul_vt100_output_csi,
92wsemul_vt100_output_scs94,
93wsemul_vt100_output_scs94_percent,
94wsemul_vt100_output_scs96,
95wsemul_vt100_output_scs96_percent,
96wsemul_vt100_output_esc_hash,
97wsemul_vt100_output_esc_spc,
98wsemul_vt100_output_string,
99wsemul_vt100_output_string_esc,
100wsemul_vt100_output_dcs,
101wsemul_vt100_output_dcs_dollar;
102
103#define	VT100_EMUL_STATE_NORMAL		0	/* normal processing */
104#define	VT100_EMUL_STATE_ESC		1	/* got ESC */
105#define	VT100_EMUL_STATE_CSI		2	/* got CSI (ESC[) */
106#define	VT100_EMUL_STATE_SCS94		3	/* got ESC{()*+} */
107#define	VT100_EMUL_STATE_SCS94_PERCENT	4	/* got ESC{()*+}% */
108#define	VT100_EMUL_STATE_SCS96		5	/* got ESC{-./} */
109#define	VT100_EMUL_STATE_SCS96_PERCENT	6	/* got ESC{-./}% */
110#define	VT100_EMUL_STATE_ESC_HASH	7	/* got ESC# */
111#define	VT100_EMUL_STATE_ESC_SPC	8	/* got ESC<SPC> */
112#define	VT100_EMUL_STATE_STRING		9	/* waiting for ST (ESC\) */
113#define	VT100_EMUL_STATE_STRING_ESC	10	/* waiting for ST, got ESC */
114#define	VT100_EMUL_STATE_DCS		11	/* got DCS (ESC P) */
115#define	VT100_EMUL_STATE_DCS_DOLLAR	12	/* got DCS<p>$ */
116
117vt100_handler *vt100_output[] = {
118	wsemul_vt100_output_esc,
119	wsemul_vt100_output_csi,
120	wsemul_vt100_output_scs94,
121	wsemul_vt100_output_scs94_percent,
122	wsemul_vt100_output_scs96,
123	wsemul_vt100_output_scs96_percent,
124	wsemul_vt100_output_esc_hash,
125	wsemul_vt100_output_esc_spc,
126	wsemul_vt100_output_string,
127	wsemul_vt100_output_string_esc,
128	wsemul_vt100_output_dcs,
129	wsemul_vt100_output_dcs_dollar,
130};
131
132static void
133wsemul_vt100_init(struct wsemul_vt100_emuldata *edp,
134	const struct wsscreen_descr *type, void *cookie, int ccol, int crow,
135	long defattr)
136{
137	struct vt100base_data *vd = &edp->bd;
138	int error;
139
140	vd->emulops = type->textops;
141	vd->emulcookie = cookie;
142	vd->scrcapabilities = type->capabilities;
143	vd->nrows = type->nrows;
144	vd->ncols = type->ncols;
145	vd->crow = crow;
146	vd->ccol = ccol;
147
148	/* The underlying driver has already allocated a default and simple
149	 * attribute for us, which is stored in defattr.  We try to set the
150	 * values specified by the kernel options below, but in case of
151	 * failure we fallback to the value given by the driver. */
152
153	if (type->capabilities & WSSCREEN_WSCOLORS) {
154		vd->msgattrs.default_attrs = WS_DEFAULT_COLATTR |
155		    WSATTR_WSCOLORS;
156		vd->msgattrs.default_bg = WS_DEFAULT_BG;
157		vd->msgattrs.default_fg = WS_DEFAULT_FG;
158
159		vd->msgattrs.kernel_attrs = WS_KERNEL_COLATTR |
160		    WSATTR_WSCOLORS;
161		vd->msgattrs.kernel_bg = WS_KERNEL_BG;
162		vd->msgattrs.kernel_fg = WS_KERNEL_FG;
163	} else {
164		vd->msgattrs.default_attrs = WS_DEFAULT_MONOATTR;
165		vd->msgattrs.default_bg = vd->msgattrs.default_fg = 0;
166
167		vd->msgattrs.kernel_attrs = WS_KERNEL_MONOATTR;
168		vd->msgattrs.kernel_bg = vd->msgattrs.kernel_fg = 0;
169	}
170
171	error = (*vd->emulops->allocattr)(cookie,
172					   vd->msgattrs.default_fg,
173					   vd->msgattrs.default_bg,
174					   vd->msgattrs.default_attrs,
175					   &vd->defattr);
176	if (error) {
177		vd->defattr = defattr;
178		/* XXX This assumes the driver has allocated white on black
179		 * XXX as the default attribute, which is not always true.
180		 * XXX Maybe we need an emulop that, given an attribute,
181		 * XXX (defattr) returns its flags and colors? */
182		vd->msgattrs.default_attrs = 0;
183		vd->msgattrs.default_bg = WSCOL_BLACK;
184		vd->msgattrs.default_fg = WSCOL_WHITE;
185	} else {
186		if (vd->emulops->replaceattr != NULL)
187			(*vd->emulops->replaceattr)(cookie, defattr,
188			                             vd->defattr);
189	}
190
191#if defined(WS_KERNEL_CUSTOMIZED)
192	/* Set up kernel colors, in case they were customized by the user;
193	 * otherwise default to the colors specified for the console.
194	 * In case of failure, we use console colors too; we can assume
195	 * they are good as they have been previously allocated and
196	 * verified. */
197	error = (*vd->emulops->allocattr)(cookie,
198					   vd->msgattrs.kernel_fg,
199					   vd->msgattrs.kernel_bg,
200					   vd->msgattrs.kernel_attrs,
201					   &edp->kernattr);
202	if (error)
203#endif
204	edp->kernattr = vd->defattr;
205}
206
207void *
208wsemul_vt100_cnattach(const struct wsscreen_descr *type, void *cookie,
209	int ccol, int crow, long defattr)
210{
211	struct wsemul_vt100_emuldata *edp;
212	struct vt100base_data *vd;
213
214	edp = &wsemul_vt100_console_emuldata;
215	vd = &edp->bd;
216	wsemul_vt100_init(edp, type, cookie, ccol, crow, defattr);
217#ifdef DIAGNOSTIC
218	edp->console = 1;
219#endif
220	vd->cbcookie = NULL;
221
222	vd->tabs = 0;
223	vd->dblwid = 0;
224	vd->dw = 0;
225	vd->dcsarg = 0;
226	edp->isolatin1tab = edp->decgraphtab = edp->dectechtab = 0;
227	edp->nrctab = 0;
228	wsemul_vt100_reset(edp);
229	return (edp);
230}
231
232void *
233wsemul_vt100_attach(int console, const struct wsscreen_descr *type,
234	void *cookie, int ccol, int crow, void *cbcookie, long defattr)
235{
236	struct wsemul_vt100_emuldata *edp;
237	struct vt100base_data *vd;
238
239	if (console) {
240		edp = &wsemul_vt100_console_emuldata;
241#ifdef DIAGNOSTIC
242		KASSERT(edp->console == 1);
243#endif
244	} else {
245		edp = malloc(sizeof *edp, M_DEVBUF, M_WAITOK);
246		wsemul_vt100_init(edp, type, cookie, ccol, crow, defattr);
247#ifdef DIAGNOSTIC
248		edp->console = 0;
249#endif
250	}
251	vd = &edp->bd;
252	vd->cbcookie = cbcookie;
253
254	vd->tabs = malloc(vd->ncols, M_DEVBUF, M_NOWAIT);
255	vd->dblwid = malloc(vd->nrows, M_DEVBUF, M_NOWAIT|M_ZERO);
256	vd->dw = 0;
257	vd->dcsarg = malloc(DCS_MAXLEN, M_DEVBUF, M_NOWAIT);
258	edp->isolatin1tab = malloc(128 * sizeof(int), M_DEVBUF, M_NOWAIT);
259	edp->decgraphtab = malloc(128 * sizeof(int), M_DEVBUF, M_NOWAIT);
260	edp->dectechtab = malloc(128 * sizeof(int), M_DEVBUF, M_NOWAIT);
261	edp->nrctab = malloc(128 * sizeof(int), M_DEVBUF, M_NOWAIT);
262	vt100_initchartables(edp);
263	wsemul_vt100_reset(edp);
264	return (edp);
265}
266
267void
268wsemul_vt100_detach(void *cookie, u_int *crowp, u_int *ccolp)
269{
270	struct wsemul_vt100_emuldata *edp = cookie;
271	struct vt100base_data *vd = &edp->bd;
272
273	*crowp = vd->crow;
274	*ccolp = vd->ccol;
275#define f(ptr) if (ptr) {free(ptr, M_DEVBUF); ptr = 0;}
276	f(vd->tabs)
277	f(vd->dblwid)
278	f(vd->dcsarg)
279	f(edp->isolatin1tab)
280	f(edp->decgraphtab)
281	f(edp->dectechtab)
282	f(edp->nrctab)
283#undef f
284	if (edp != &wsemul_vt100_console_emuldata)
285		free(edp, M_DEVBUF);
286}
287
288void
289wsemul_vt100_resetop(void *cookie, enum wsemul_resetops op)
290{
291	struct wsemul_vt100_emuldata *edp = cookie;
292	struct vt100base_data *vd = &edp->bd;
293
294	switch (op) {
295	case WSEMUL_RESET:
296		wsemul_vt100_reset(edp);
297		break;
298	case WSEMUL_SYNCFONT:
299		vt100_initchartables(edp);
300		break;
301	case WSEMUL_CLEARSCREEN:
302		wsemul_vt100_ed(vd, 2);
303		vd->ccol = vd->crow = 0;
304		(*vd->emulops->cursor)(vd->emulcookie,
305					vd->flags & VTFL_CURSORON, 0, 0);
306		break;
307	default:
308		break;
309	}
310}
311
312void
313wsemul_vt100_reset(struct wsemul_vt100_emuldata *edp)
314{
315	struct vt100base_data *vd = &edp->bd;
316	int i;
317
318	edp->state = VT100_EMUL_STATE_NORMAL;
319	vd->flags = VTFL_DECAWM | VTFL_CURSORON;
320	vd->bkgdattr = vd->curattr = vd->defattr;
321	vd->attrflags = vd->msgattrs.default_attrs;
322	vd->fgcol = vd->msgattrs.default_fg;
323	vd->bgcol = vd->msgattrs.default_bg;
324	vd->scrreg_startrow = 0;
325	vd->scrreg_nrows = vd->nrows;
326	if (vd->tabs) {
327		memset(vd->tabs, 0, vd->ncols);
328		for (i = 8; i < vd->ncols; i += 8)
329			vd->tabs[i] = 1;
330	}
331	vd->dcspos = 0;
332	vd->dcstype = 0;
333	edp->chartab_G[0] = 0;
334	edp->chartab_G[1] = edp->nrctab; /* ??? */
335	edp->chartab_G[2] = edp->isolatin1tab;
336	edp->chartab_G[3] = edp->isolatin1tab;
337	edp->chartab0 = 0;
338	edp->chartab1 = 2;
339	edp->sschartab = 0;
340}
341
342/*
343 * now all the state machine bits
344 */
345
346/*
347 * Move the cursor to the next line if possible. If the cursor is at
348 * the bottom of the scroll area, then scroll it up. If the cursor is
349 * at the bottom of the screen then don't move it down.
350 */
351static void
352wsemul_vt100_nextline(struct wsemul_vt100_emuldata *edp)
353{
354	struct vt100base_data *vd = &edp->bd;
355
356	if (ROWS_BELOW(vd) == 0) {
357		/* Bottom of the scroll region. */
358	  	wsemul_vt100_scrollup(vd, 1);
359	} else {
360		if ((vd->crow+1) < vd->nrows)
361			/* Cursor not at the bottom of the screen. */
362			vd->crow++;
363		CHECK_DW(vd);
364	}
365}
366
367static void
368wsemul_vt100_output_normal(struct wsemul_vt100_emuldata *edp, u_char c,
369	int kernel)
370{
371	struct vt100base_data *vd = &edp->bd;
372	u_int *ct, dc;
373
374	if ((vd->flags & (VTFL_LASTCHAR | VTFL_DECAWM)) ==
375	    (VTFL_LASTCHAR | VTFL_DECAWM)) {
376		wsemul_vt100_nextline(edp);
377		vd->ccol = 0;
378		vd->flags &= ~VTFL_LASTCHAR;
379	}
380
381	if (c & 0x80) {
382		c &= 0x7f;
383		ct = edp->chartab_G[edp->chartab1];
384	} else {
385		if (edp->sschartab) {
386			ct = edp->chartab_G[edp->sschartab];
387			edp->sschartab = 0;
388		} else
389			ct = edp->chartab_G[edp->chartab0];
390	}
391	dc = (ct ? ct[c] : c);
392
393	if ((vd->flags & VTFL_INSERTMODE) && COLS_LEFT(vd))
394		COPYCOLS(vd, vd->ccol, vd->ccol + 1, COLS_LEFT(vd));
395
396	(*vd->emulops->putchar)(vd->emulcookie, vd->crow,
397				 vd->ccol << vd->dw, dc,
398				 kernel ? edp->kernattr : vd->curattr);
399
400	if (COLS_LEFT(vd))
401		vd->ccol++;
402	else
403		vd->flags |= VTFL_LASTCHAR;
404}
405
406static void
407wsemul_vt100_output_c0c1(struct wsemul_vt100_emuldata *edp, u_char c,
408	int kernel)
409{
410	struct vt100base_data *vd = &edp->bd;
411	u_int n;
412
413	switch (c) {
414	case ASCII_NUL:
415	default:
416		/* ignore */
417		break;
418	case ASCII_BEL:
419		wsdisplay_emulbell(vd->cbcookie);
420		break;
421	case ASCII_BS:
422		if (vd->ccol > 0) {
423			vd->ccol--;
424			vd->flags &= ~VTFL_LASTCHAR;
425		}
426		break;
427	case ASCII_CR:
428		vd->ccol = 0;
429		vd->flags &= ~VTFL_LASTCHAR;
430		break;
431	case ASCII_HT:
432		if (vd->tabs) {
433			if (!COLS_LEFT(vd))
434				break;
435			for (n = vd->ccol + 1; n < NCOLS(vd) - 1; n++)
436				if (vd->tabs[n])
437					break;
438		} else {
439			n = vd->ccol + min(8 - (vd->ccol & 7), COLS_LEFT(vd));
440		}
441		vd->ccol = n;
442		break;
443	case ASCII_SO: /* LS1 */
444		edp->chartab0 = 1;
445		break;
446	case ASCII_SI: /* LS0 */
447		edp->chartab0 = 0;
448		break;
449	case ASCII_ESC:
450		if (kernel) {
451			printf("wsemul_vt100_output_c0c1: ESC in kernel output ignored\n");
452			break;	/* ignore the ESC */
453		}
454
455		if (edp->state == VT100_EMUL_STATE_STRING) {
456			/* might be a string end */
457			edp->state = VT100_EMUL_STATE_STRING_ESC;
458		} else {
459			/* XXX cancel current escape sequence */
460			edp->state = VT100_EMUL_STATE_ESC;
461		}
462		break;
463#if 0
464	case CSI: /* 8-bit */
465		/* XXX cancel current escape sequence */
466		edp->nargs = 0;
467		memset(edp->args, 0, sizeof (edp->args));
468		edp->modif1 = edp->modif2 = '\0';
469		edp->state = VT100_EMUL_STATE_CSI;
470		break;
471	case DCS: /* 8-bit */
472		/* XXX cancel current escape sequence */
473		edp->nargs = 0;
474		memset(edp->args, 0, sizeof (edp->args));
475		edp->state = VT100_EMUL_STATE_DCS;
476		break;
477	case ST: /* string end 8-bit */
478		/* XXX only in VT100_EMUL_STATE_STRING */
479		wsemul_vt100_handle_dcs(edp);
480		return (VT100_EMUL_STATE_NORMAL);
481#endif
482	case ASCII_LF:
483	case ASCII_VT:
484	case ASCII_FF:
485		wsemul_vt100_nextline(edp);
486		break;
487	}
488}
489
490static u_int
491wsemul_vt100_output_esc(struct wsemul_vt100_emuldata *edp, u_char c)
492{
493	struct vt100base_data *vd = &edp->bd;
494	u_int newstate = VT100_EMUL_STATE_NORMAL;
495	int i;
496
497	switch (c) {
498	case '[': /* CSI */
499		vd->nargs = 0;
500		memset(vd->args, 0, sizeof (vd->args));
501		vd->modif1 = vd->modif2 = '\0';
502		newstate = VT100_EMUL_STATE_CSI;
503		break;
504	case '7': /* DECSC */
505		vd->flags |= VTFL_SAVEDCURS;
506		edp->savedcursor_row = vd->crow;
507		edp->savedcursor_col = vd->ccol;
508		edp->savedattr = vd->curattr;
509		edp->savedbkgdattr = vd->bkgdattr;
510		edp->savedattrflags = vd->attrflags;
511		edp->savedfgcol = vd->fgcol;
512		edp->savedbgcol = vd->bgcol;
513		for (i = 0; i < 4; i++)
514			edp->savedchartab_G[i] = edp->chartab_G[i];
515		edp->savedchartab0 = edp->chartab0;
516		edp->savedchartab1 = edp->chartab1;
517		break;
518	case '8': /* DECRC */
519		if ((vd->flags & VTFL_SAVEDCURS) == 0)
520			break;
521		vd->crow = edp->savedcursor_row;
522		vd->ccol = edp->savedcursor_col;
523		vd->curattr = edp->savedattr;
524		vd->bkgdattr = edp->savedbkgdattr;
525		vd->attrflags = edp->savedattrflags;
526		vd->fgcol = edp->savedfgcol;
527		vd->bgcol = edp->savedbgcol;
528		for (i = 0; i < 4; i++)
529			edp->chartab_G[i] = edp->savedchartab_G[i];
530		edp->chartab0 = edp->savedchartab0;
531		edp->chartab1 = edp->savedchartab1;
532		break;
533	case '=': /* DECKPAM application mode */
534		vd->flags |= VTFL_APPLKEYPAD;
535		break;
536	case '>': /* DECKPNM numeric mode */
537		vd->flags &= ~VTFL_APPLKEYPAD;
538		break;
539	case 'E': /* NEL */
540		vd->ccol = 0;
541		/* FALLTHRU */
542	case 'D': /* IND */
543		wsemul_vt100_nextline(edp);
544		break;
545	case 'H': /* HTS */
546		KASSERT(vd->tabs != 0);
547		vd->tabs[vd->ccol] = 1;
548		break;
549	case '~': /* LS1R */
550		edp->chartab1 = 1;
551		break;
552	case 'n': /* LS2 */
553		edp->chartab0 = 2;
554		break;
555	case '}': /* LS2R */
556		edp->chartab1 = 2;
557		break;
558	case 'o': /* LS3 */
559		edp->chartab0 = 3;
560		break;
561	case '|': /* LS3R */
562		edp->chartab1 = 3;
563		break;
564	case 'N': /* SS2 */
565		edp->sschartab = 2;
566		break;
567	case 'O': /* SS3 */
568		edp->sschartab = 3;
569		break;
570	case 'M': /* RI */
571		if (ROWS_ABOVE(vd) > 0) {
572			vd->crow--;
573			CHECK_DW(vd);
574			break;
575		}
576		wsemul_vt100_scrolldown(vd, 1);
577		break;
578	case 'P': /* DCS */
579		vd->nargs = 0;
580		memset(vd->args, 0, sizeof (vd->args));
581		newstate = VT100_EMUL_STATE_DCS;
582		break;
583	case 'c': /* RIS */
584		wsemul_vt100_reset(edp);
585		wsemul_vt100_ed(vd, 2);
586		vd->ccol = vd->crow = 0;
587		break;
588	case '(': case ')': case '*': case '+': /* SCS */
589		edp->designating = c - '(';
590		newstate = VT100_EMUL_STATE_SCS94;
591		break;
592	case '-': case '.': case '/': /* SCS */
593		edp->designating = c - '-' + 1;
594		newstate = VT100_EMUL_STATE_SCS96;
595		break;
596	case '#':
597		newstate = VT100_EMUL_STATE_ESC_HASH;
598		break;
599	case ' ': /* 7/8 bit */
600		newstate = VT100_EMUL_STATE_ESC_SPC;
601		break;
602	case ']': /* OSC operating system command */
603	case '^': /* PM privacy message */
604	case '_': /* APC application program command */
605		/* ignored */
606		newstate = VT100_EMUL_STATE_STRING;
607		break;
608	case '<': /* exit VT52 mode - ignored */
609		break;
610	default:
611#ifdef VT100_PRINTUNKNOWN
612		printf("ESC%c unknown\n", c);
613#endif
614		break;
615	}
616
617	return (newstate);
618}
619
620static u_int
621wsemul_vt100_output_scs94(struct wsemul_vt100_emuldata *edp, u_char c)
622{
623	u_int newstate = VT100_EMUL_STATE_NORMAL;
624
625	switch (c) {
626	case '%': /* probably DEC supplemental graphic */
627		newstate = VT100_EMUL_STATE_SCS94_PERCENT;
628		break;
629	case 'A': /* british / national */
630		edp->chartab_G[edp->designating] = edp->nrctab;
631		break;
632	case 'B': /* ASCII */
633		edp->chartab_G[edp->designating] = 0;
634		break;
635	case '<': /* user preferred supplemental */
636		/* XXX not really "user" preferred */
637		edp->chartab_G[edp->designating] = edp->isolatin1tab;
638		break;
639	case '0': /* DEC special graphic */
640		edp->chartab_G[edp->designating] = edp->decgraphtab;
641		break;
642	case '>': /* DEC tech */
643		edp->chartab_G[edp->designating] = edp->dectechtab;
644		break;
645	default:
646#ifdef VT100_PRINTUNKNOWN
647		printf("ESC%c%c unknown\n", edp->designating + '(', c);
648#endif
649		break;
650	}
651	return (newstate);
652}
653
654static u_int
655wsemul_vt100_output_scs94_percent(struct wsemul_vt100_emuldata *edp, u_char c)
656{
657	switch (c) {
658	case '5': /* DEC supplemental graphic */
659		/* XXX there are differences */
660		edp->chartab_G[edp->designating] = edp->isolatin1tab;
661		break;
662	default:
663#ifdef VT100_PRINTUNKNOWN
664		printf("ESC%c%%%c unknown\n", edp->designating + '(', c);
665#endif
666		break;
667	}
668	return (VT100_EMUL_STATE_NORMAL);
669}
670
671static u_int
672wsemul_vt100_output_scs96(struct wsemul_vt100_emuldata *edp, u_char c)
673{
674	u_int newstate = VT100_EMUL_STATE_NORMAL;
675	int nrc;
676
677	switch (c) {
678	case '%': /* probably portuguese */
679		newstate = VT100_EMUL_STATE_SCS96_PERCENT;
680		break;
681	case 'A': /* ISO-latin-1 supplemental */
682		edp->chartab_G[edp->designating] = edp->isolatin1tab;
683		break;
684	case '4': /* dutch */
685		nrc = 1;
686		goto setnrc;
687	case '5': case 'C': /* finnish */
688		nrc = 2;
689		goto setnrc;
690	case 'R': /* french */
691		nrc = 3;
692		goto setnrc;
693	case 'Q': /* french canadian */
694		nrc = 4;
695		goto setnrc;
696	case 'K': /* german */
697		nrc = 5;
698		goto setnrc;
699	case 'Y': /* italian */
700		nrc = 6;
701		goto setnrc;
702	case 'E': case '6': /* norwegian / danish */
703		nrc = 7;
704		goto setnrc;
705	case 'Z': /* spanish */
706		nrc = 9;
707		goto setnrc;
708	case '7': case 'H': /* swedish */
709		nrc = 10;
710		goto setnrc;
711	case '=': /* swiss */
712		nrc = 11;
713setnrc:
714		vt100_setnrc(edp, nrc); /* what table ??? */
715		break;
716	default:
717#ifdef VT100_PRINTUNKNOWN
718		printf("ESC%c%c unknown\n", edp->designating + '-' - 1, c);
719#endif
720		break;
721	}
722	return (newstate);
723}
724
725static u_int
726wsemul_vt100_output_scs96_percent(struct wsemul_vt100_emuldata *edp, u_char c)
727{
728	switch (c) {
729	case '6': /* portuguese */
730		vt100_setnrc(edp, 8);
731		break;
732	default:
733#ifdef VT100_PRINTUNKNOWN
734		printf("ESC%c%%%c unknown\n", edp->designating + '-', c);
735#endif
736		break;
737	}
738	return (VT100_EMUL_STATE_NORMAL);
739}
740
741static u_int
742wsemul_vt100_output_esc_spc(struct wsemul_vt100_emuldata *edp,
743    u_char c)
744{
745	switch (c) {
746	case 'F': /* 7-bit controls */
747	case 'G': /* 8-bit controls */
748#ifdef VT100_PRINTNOTIMPL
749		printf("ESC<SPC>%c ignored\n", c);
750#endif
751		break;
752	default:
753#ifdef VT100_PRINTUNKNOWN
754		printf("ESC<SPC>%c unknown\n", c);
755#endif
756		break;
757	}
758	return (VT100_EMUL_STATE_NORMAL);
759}
760
761static u_int
762wsemul_vt100_output_string(struct wsemul_vt100_emuldata *edp, u_char c)
763{
764	struct vt100base_data *vd = &edp->bd;
765
766	if (vd->dcstype && vd->dcspos < DCS_MAXLEN)
767		vd->dcsarg[vd->dcspos++] = c;
768	return (VT100_EMUL_STATE_STRING);
769}
770
771static u_int
772wsemul_vt100_output_string_esc(struct wsemul_vt100_emuldata *edp, u_char c)
773{
774	struct vt100base_data *vd = &edp->bd;
775
776	if (c == '\\') { /* ST complete */
777		wsemul_vt100_handle_dcs(vd);
778		return (VT100_EMUL_STATE_NORMAL);
779	} else
780		return (VT100_EMUL_STATE_STRING);
781}
782
783static u_int
784wsemul_vt100_output_dcs(struct wsemul_vt100_emuldata *edp, u_char c)
785{
786	struct vt100base_data *vd = &edp->bd;
787	u_int newstate = VT100_EMUL_STATE_DCS;
788
789	switch (c) {
790	case '0': case '1': case '2': case '3': case '4':
791	case '5': case '6': case '7': case '8': case '9':
792		/* argument digit */
793		if (vd->nargs > VT100_EMUL_NARGS - 1)
794			break;
795		vd->args[vd->nargs] = (vd->args[vd->nargs] * 10) +
796		    (c - '0');
797		break;
798	case ';': /* argument terminator */
799		vd->nargs++;
800		break;
801	default:
802		vd->nargs++;
803		if (vd->nargs > VT100_EMUL_NARGS) {
804#ifdef VT100_DEBUG
805			printf("vt100: too many arguments\n");
806#endif
807			vd->nargs = VT100_EMUL_NARGS;
808		}
809		newstate = VT100_EMUL_STATE_STRING;
810		switch (c) {
811		case '$':
812			newstate = VT100_EMUL_STATE_DCS_DOLLAR;
813			break;
814		case '{': /* DECDLD soft charset */
815		case '!': /* DECRQUPSS user preferred supplemental set */
816			/* 'u' must follow - need another state */
817		case '|': /* DECUDK program F6..F20 */
818#ifdef VT100_PRINTNOTIMPL
819			printf("DCS%c ignored\n", c);
820#endif
821			break;
822		default:
823#ifdef VT100_PRINTUNKNOWN
824			printf("DCS%c (%d, %d) unknown\n", c, ARG(vd, 0), ARG(vd, 1));
825#endif
826			break;
827		}
828	}
829
830	return (newstate);
831}
832
833static u_int
834wsemul_vt100_output_dcs_dollar(struct wsemul_vt100_emuldata *edp, u_char c)
835{
836	struct vt100base_data *vd = &edp->bd;
837
838	switch (c) {
839	case 'p': /* DECRSTS terminal state restore */
840	case 'q': /* DECRQSS control function request */
841#ifdef VT100_PRINTNOTIMPL
842		printf("DCS$%c ignored\n", c);
843#endif
844		break;
845	case 't': /* DECRSPS restore presentation state */
846		switch (ARG(vd, 0)) {
847		case 0: /* error */
848			break;
849		case 1: /* cursor information restore */
850#ifdef VT100_PRINTNOTIMPL
851			printf("DCS1$t ignored\n");
852#endif
853			break;
854		case 2: /* tab stop restore */
855			vd->dcspos = 0;
856			vd->dcstype = DCSTYPE_TABRESTORE;
857			break;
858		default:
859#ifdef VT100_PRINTUNKNOWN
860			printf("DCS%d$t unknown\n", ARG(vd, 0));
861#endif
862			break;
863		}
864		break;
865	default:
866#ifdef VT100_PRINTUNKNOWN
867		printf("DCS$%c (%d, %d) unknown\n", c, ARG(vd, 0), ARG(vd, 1));
868#endif
869		break;
870	}
871	return (VT100_EMUL_STATE_STRING);
872}
873
874static u_int
875wsemul_vt100_output_esc_hash(struct wsemul_vt100_emuldata *edp, u_char c)
876{
877	struct vt100base_data *vd = &edp->bd;
878	int i, j;
879
880	switch (c) {
881	case '5': /*  DECSWL single width, single height */
882		if (vd->dw) {
883			for (i = 0; i < vd->ncols / 2; i++)
884				(*vd->emulops->copycols)(vd->emulcookie,
885							  vd->crow,
886							  2 * i, i, 1);
887			(*vd->emulops->erasecols)(vd->emulcookie, vd->crow,
888						   i, vd->ncols - i,
889						   vd->bkgdattr);
890			vd->dblwid[vd->crow] = 0;
891			vd->dw = 0;
892		}
893		break;
894	case '6': /*  DECDWL double width, single height */
895	case '3': /*  DECDHL double width, double height, top half */
896	case '4': /*  DECDHL double width, double height, bottom half */
897		if (!vd->dw) {
898			for (i = vd->ncols / 2 - 1; i >= 0; i--)
899				(*vd->emulops->copycols)(vd->emulcookie,
900							  vd->crow,
901							  i, 2 * i, 1);
902			for (i = 0; i < vd->ncols / 2; i++)
903				(*vd->emulops->erasecols)(vd->emulcookie,
904							   vd->crow,
905							   2 * i + 1, 1,
906							   vd->bkgdattr);
907			vd->dblwid[vd->crow] = 1;
908			vd->dw = 1;
909			if (vd->ccol > (vd->ncols >> 1) - 1)
910				vd->ccol = (vd->ncols >> 1) - 1;
911		}
912		break;
913	case '8': /* DECALN */
914		for (i = 0; i < vd->nrows; i++)
915			for (j = 0; j < vd->ncols; j++)
916				(*vd->emulops->putchar)(vd->emulcookie, i, j,
917							 'E', vd->curattr);
918		vd->ccol = 0;
919		vd->crow = 0;
920		break;
921	default:
922#ifdef VT100_PRINTUNKNOWN
923		printf("ESC#%c unknown\n", c);
924#endif
925		break;
926	}
927	return (VT100_EMUL_STATE_NORMAL);
928}
929
930static u_int
931wsemul_vt100_output_csi(struct wsemul_vt100_emuldata *edp, u_char c)
932{
933	struct vt100base_data *vd = &edp->bd;
934	u_int newstate = VT100_EMUL_STATE_CSI;
935
936	switch (c) {
937	case '0': case '1': case '2': case '3': case '4':
938	case '5': case '6': case '7': case '8': case '9':
939		/* argument digit */
940		if (vd->nargs > VT100_EMUL_NARGS - 1)
941			break;
942		vd->args[vd->nargs] = (vd->args[vd->nargs] * 10) +
943		    (c - '0');
944		break;
945	case ';': /* argument terminator */
946		vd->nargs++;
947		break;
948	case '?': /* DEC specific */
949	case '>': /* DA query */
950		vd->modif1 = c;
951		break;
952	case '!':
953	case '"':
954	case '$':
955	case '&':
956		vd->modif2 = c;
957		break;
958	default: /* end of escape sequence */
959		vd->nargs++;
960		if (vd->nargs > VT100_EMUL_NARGS) {
961#ifdef VT100_DEBUG
962			printf("vt100: too many arguments\n");
963#endif
964			vd->nargs = VT100_EMUL_NARGS;
965		}
966		wsemul_vt100_handle_csi(vd, c);
967		newstate = VT100_EMUL_STATE_NORMAL;
968		break;
969	}
970	return (newstate);
971}
972
973void
974wsemul_vt100_output(void *cookie, const u_char *data, u_int count, int kernel)
975{
976	struct wsemul_vt100_emuldata *edp = cookie;
977	struct vt100base_data *vd = &edp->bd;
978
979#ifdef DIAGNOSTIC
980	if (kernel && !edp->console)
981		panic("wsemul_vt100_output: kernel output, not console");
982#endif
983
984	if (vd->flags & VTFL_CURSORON)
985		(*vd->emulops->cursor)(vd->emulcookie, 0,
986					vd->crow, vd->ccol << vd->dw);
987	for (; count > 0; data++, count--) {
988		if ((*data & 0x7f) < 0x20) {
989			wsemul_vt100_output_c0c1(edp, *data, kernel);
990			continue;
991		}
992		if (edp->state == VT100_EMUL_STATE_NORMAL || kernel) {
993			wsemul_vt100_output_normal(edp, *data, kernel);
994			continue;
995		}
996#ifdef DIAGNOSTIC
997		if (edp->state > sizeof(vt100_output) / sizeof(vt100_output[0]))
998			panic("wsemul_vt100: invalid state %d", edp->state);
999#endif
1000		edp->state = vt100_output[edp->state - 1](edp, *data);
1001	}
1002	if (vd->flags & VTFL_CURSORON)
1003		(*vd->emulops->cursor)(vd->emulcookie, 1,
1004					vd->crow, vd->ccol << vd->dw);
1005}
1006
1007#ifdef WSDISPLAY_CUSTOM_OUTPUT
1008static void
1009wsemul_vt100_getmsgattrs(void *cookie, struct wsdisplay_msgattrs *ma)
1010{
1011	struct wsemul_vt100_emuldata *edp = cookie;
1012	struct vt100base_data *vd = &edp->bd;
1013
1014	*ma = vd->msgattrs;
1015}
1016
1017static void
1018wsemul_vt100_setmsgattrs(void *cookie, const struct wsscreen_descr *type,
1019                         const struct wsdisplay_msgattrs *ma)
1020{
1021	int error;
1022	long tmp;
1023	struct wsemul_vt100_emuldata *edp = cookie;
1024	struct vt100base_data *vd = &edp->bd;
1025
1026	vd->msgattrs = *ma;
1027	if (type->capabilities & WSSCREEN_WSCOLORS) {
1028		vd->msgattrs.default_attrs |= WSATTR_WSCOLORS;
1029		vd->msgattrs.kernel_attrs |= WSATTR_WSCOLORS;
1030	} else {
1031		vd->msgattrs.default_bg = vd->msgattrs.kernel_bg = 0;
1032		vd->msgattrs.default_fg = vd->msgattrs.kernel_fg = 0;
1033	}
1034
1035	error = (*vd->emulops->allocattr)(vd->emulcookie,
1036	                                   vd->msgattrs.default_fg,
1037					   vd->msgattrs.default_bg,
1038	                                   vd->msgattrs.default_attrs,
1039	                                   &tmp);
1040#ifdef VT100_DEBUG
1041	if (error)
1042		printf("vt100: failed to allocate attribute for default "
1043		       "messages\n");
1044	else
1045#endif
1046	{
1047		if (vd->curattr == vd->defattr) {
1048			vd->bkgdattr = vd->curattr = tmp;
1049			vd->attrflags = vd->msgattrs.default_attrs;
1050			vd->bgcol = vd->msgattrs.default_bg;
1051			vd->fgcol = vd->msgattrs.default_fg;
1052		} else {
1053			edp->savedbkgdattr = edp->savedattr = tmp;
1054			edp->savedattrflags = vd->msgattrs.default_attrs;
1055			edp->savedbgcol = vd->msgattrs.default_bg;
1056			edp->savedfgcol = vd->msgattrs.default_fg;
1057		}
1058		if (vd->emulops->replaceattr != NULL)
1059			(*vd->emulops->replaceattr)(vd->emulcookie,
1060			                             vd->defattr, tmp);
1061		vd->defattr = tmp;
1062	}
1063
1064	error = (*vd->emulops->allocattr)(vd->emulcookie,
1065	                                   vd->msgattrs.kernel_fg,
1066					   vd->msgattrs.kernel_bg,
1067	                                   vd->msgattrs.kernel_attrs,
1068	                                   &tmp);
1069#ifdef VT100_DEBUG
1070	if (error)
1071		printf("vt100: failed to allocate attribute for kernel "
1072		       "messages\n");
1073	else
1074#endif
1075	{
1076		if (vd->emulops->replaceattr != NULL)
1077			(*vd->emulops->replaceattr)(vd->emulcookie,
1078			                             edp->kernattr, tmp);
1079		edp->kernattr = tmp;
1080	}
1081}
1082#endif /* WSDISPLAY_CUSTOM_OUTPUT */
1083