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