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