xref: /openbsd/sys/dev/wscons/wsemul_vt100_subr.c (revision 3b273202)
1 /* $OpenBSD: wsemul_vt100_subr.c,v 1.32 2024/11/05 15:54:12 miod Exp $ */
2 /* $NetBSD: wsemul_vt100_subr.c,v 1.7 2000/04/28 21:56:16 mycroft Exp $ */
3 
4 /*
5  * Copyright (c) 1998
6  *	Matthias Drochner.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  *
28  */
29 
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 
33 #include <dev/wscons/wsconsio.h>
34 #include <dev/wscons/wsksymvar.h>
35 #include <dev/wscons/wsdisplayvar.h>
36 #include <dev/wscons/wsemulvar.h>
37 #include <dev/wscons/wsemul_vt100var.h>
38 
39 int	vt100_selectattribute(struct wsemul_vt100_emuldata *, int, int, int,
40 	    uint32_t *, uint32_t *);
41 int	vt100_ansimode(struct wsemul_vt100_emuldata *, int, int);
42 int	vt100_decmode(struct wsemul_vt100_emuldata *, int, int);
43 #define VTMODE_SET 33
44 #define VTMODE_RESET 44
45 #define VTMODE_REPORT 55
46 
47 /*
48  * scroll up within scrolling region
49  */
50 int
wsemul_vt100_scrollup(struct wsemul_vt100_emuldata * edp,int n)51 wsemul_vt100_scrollup(struct wsemul_vt100_emuldata *edp, int n)
52 {
53 	int help;
54 	int rc;
55 
56 	if (n > edp->scrreg_nrows)
57 		n = edp->scrreg_nrows;
58 
59 	help = edp->scrreg_nrows - n;
60 	if (help > 0) {
61 		WSEMULOP(rc, edp, &edp->abortstate, copyrows,
62 		    (edp->emulcookie, edp->scrreg_startrow + n,
63 		     edp->scrreg_startrow, help));
64 		if (rc != 0)
65 			return rc;
66 	}
67 	WSEMULOP(rc, edp, &edp->abortstate, eraserows,
68 	    (edp->emulcookie, edp->scrreg_startrow + help, n, edp->bkgdattr));
69 	if (rc != 0)
70 		return rc;
71 #ifdef HAVE_DOUBLE_WIDTH_HEIGHT
72 	if (edp->dblwid) {
73 		if (help > 0)
74 			memmove(&edp->dblwid[edp->scrreg_startrow],
75 			    &edp->dblwid[edp->scrreg_startrow + n], help);
76 		memset(&edp->dblwid[edp->scrreg_startrow + help], 0, n);
77 	}
78 	CHECK_DW;
79 #endif
80 
81 	return 0;
82 }
83 
84 /*
85  * scroll down within scrolling region
86  */
87 int
wsemul_vt100_scrolldown(struct wsemul_vt100_emuldata * edp,int n)88 wsemul_vt100_scrolldown(struct wsemul_vt100_emuldata *edp, int n)
89 {
90 	int help;
91 	int rc;
92 
93 	if (n > edp->scrreg_nrows)
94 		n = edp->scrreg_nrows;
95 
96 	help = edp->scrreg_nrows - n;
97 	if (help > 0) {
98 		WSEMULOP(rc, edp, &edp->abortstate, copyrows,
99 		    (edp->emulcookie, edp->scrreg_startrow,
100 		     edp->scrreg_startrow + n, help));
101 		if (rc != 0)
102 			return rc;
103 	}
104 	WSEMULOP(rc, edp, &edp->abortstate, eraserows,
105 	    (edp->emulcookie, edp->scrreg_startrow, n, edp->bkgdattr));
106 	if (rc != 0)
107 		return rc;
108 #ifdef HAVE_DOUBLE_WIDTH_HEIGHT
109 	if (edp->dblwid) {
110 		if (help > 0)
111 			memmove(&edp->dblwid[edp->scrreg_startrow + n],
112 			    &edp->dblwid[edp->scrreg_startrow], help);
113 		memset(&edp->dblwid[edp->scrreg_startrow], 0, n);
114 	}
115 	CHECK_DW;
116 #endif
117 
118 	return 0;
119 }
120 
121 /*
122  * erase in display
123  */
124 int
wsemul_vt100_ed(struct wsemul_vt100_emuldata * edp,int arg)125 wsemul_vt100_ed(struct wsemul_vt100_emuldata *edp, int arg)
126 {
127 	int n;
128 	int rc;
129 
130 	switch (arg) {
131 	case 0: /* cursor to end */
132 		WSEMULOP(rc, edp, &edp->abortstate, erasecols,
133 		    ERASECOLS(edp->ccol, COLS_LEFT + 1, edp->bkgdattr));
134 		if (rc != 0)
135 			break;
136 		n = edp->nrows - edp->crow - 1;
137 		if (n > 0) {
138 			WSEMULOP(rc, edp, &edp->abortstate, eraserows,
139 			    (edp->emulcookie, edp->crow + 1, n, edp->bkgdattr));
140 			if (rc != 0)
141 				break;
142 #ifdef HAVE_DOUBLE_WIDTH_HEIGHT
143 			if (edp->dblwid)
144 				memset(&edp->dblwid[edp->crow + 1], 0, n);
145 #endif
146 		}
147 		break;
148 	case 1: /* beginning to cursor */
149 		if (edp->crow > 0) {
150 			WSEMULOP(rc, edp, &edp->abortstate, eraserows,
151 			    (edp->emulcookie, 0, edp->crow, edp->bkgdattr));
152 			if (rc != 0)
153 				break;
154 		}
155 		WSEMULOP(rc, edp, &edp->abortstate, erasecols,
156 		    ERASECOLS(0, edp->ccol + 1, edp->bkgdattr));
157 		if (rc != 0)
158 			break;
159 #ifdef HAVE_DOUBLE_WIDTH_HEIGHT
160 		if (edp->dblwid) {
161 			if (edp->crow > 0)
162 				memset(&edp->dblwid[0], 0, edp->crow);
163 		}
164 #endif
165 		break;
166 	case 2: /* complete display */
167 		WSEMULOP(rc, edp, &edp->abortstate, eraserows,
168 		    (edp->emulcookie, 0, edp->nrows, edp->bkgdattr));
169 		if (rc != 0)
170 			break;
171 #ifdef HAVE_DOUBLE_WIDTH_HEIGHT
172 		if (edp->dblwid)
173 			memset(&edp->dblwid[0], 0, edp->nrows);
174 #endif
175 		break;
176 	default:
177 #ifdef VT100_PRINTUNKNOWN
178 		printf("ed(%d) unknown\n", arg);
179 #endif
180 		rc = 0;
181 		break;
182 	}
183 	if (rc != 0)
184 		return rc;
185 
186 	CHECK_DW;
187 
188 	return 0;
189 }
190 
191 /*
192  * erase in line
193  */
194 int
wsemul_vt100_el(struct wsemul_vt100_emuldata * edp,int arg)195 wsemul_vt100_el(struct wsemul_vt100_emuldata *edp, int arg)
196 {
197 	int rc;
198 
199 	switch (arg) {
200 	case 0: /* cursor to end */
201 		WSEMULOP(rc, edp, &edp->abortstate, erasecols,
202 		    ERASECOLS(edp->ccol, COLS_LEFT + 1, edp->bkgdattr));
203 		break;
204 	case 1: /* beginning to cursor */
205 		WSEMULOP(rc, edp, &edp->abortstate, erasecols,
206 		    ERASECOLS(0, edp->ccol + 1, edp->bkgdattr));
207 		break;
208 	case 2: /* complete line */
209 		WSEMULOP(rc, edp, &edp->abortstate, erasecols,
210 		    (edp->emulcookie, edp->crow, 0, edp->ncols, edp->bkgdattr));
211 		break;
212 	default:
213 #ifdef VT100_PRINTUNKNOWN
214 		printf("el(%d) unknown\n", arg);
215 #endif
216 		rc = 0;
217 		break;
218 	}
219 
220 	return rc;
221 }
222 
223 /*
224  * handle commands after CSI (ESC[)
225  */
226 int
wsemul_vt100_handle_csi(struct wsemul_vt100_emuldata * edp,struct wsemul_inputstate * instate,int kernel)227 wsemul_vt100_handle_csi(struct wsemul_vt100_emuldata *edp,
228     struct wsemul_inputstate *instate, int kernel)
229 {
230 	int n, m, help, flags, fgcol, bgcol;
231 	uint32_t attr, bkgdattr;
232 	u_char c;
233 	int rc = 0;
234 
235 	if (instate->inchar >= 0x100)
236 		c = 0x00;	/* cause the switch below to end in default: */
237 	else
238 		c = (u_char)instate->inchar;
239 
240 #define A3(a, b, c) (((a) << 16) | ((b) << 8) | (c))
241 	switch (A3(edp->modif1, edp->modif2, c)) {
242 	case A3('>', '\0', 'c'): /* DA secondary */
243 		wsdisplay_emulinput(edp->cbcookie, WSEMUL_VT_ID2,
244 		    sizeof(WSEMUL_VT_ID2) - 1);
245 		break;
246 
247 	case A3('\0', '\0', 'J'): /* ED selective erase in display */
248 	case A3('?', '\0', 'J'): /* DECSED selective erase in display */
249 		rc = wsemul_vt100_ed(edp, ARG(0));
250 		break;
251 	case A3('\0', '\0', 'K'): /* EL selective erase in line */
252 	case A3('?', '\0', 'K'): /* DECSEL selective erase in line */
253 		rc = wsemul_vt100_el(edp, ARG(0));
254 		break;
255 	case A3('\0', '\0', 'h'): /* SM */
256 		for (n = 0; n < edp->nargs; n++)
257 			vt100_ansimode(edp, ARG(n), VTMODE_SET);
258 		break;
259 	case A3('?', '\0', 'h'): /* DECSM */
260 		for (n = 0; n < edp->nargs; n++) {
261 			rc = vt100_decmode(edp, ARG(n), VTMODE_SET);
262 			if (rc != 0)
263 				break;
264 		}
265 		break;
266 	case A3('\0', '\0', 'l'): /* RM */
267 		for (n = 0; n < edp->nargs; n++)
268 			vt100_ansimode(edp, ARG(n), VTMODE_RESET);
269 		break;
270 	case A3('?', '\0', 'l'): /* DECRM */
271 		for (n = 0; n < edp->nargs; n++) {
272 			rc = vt100_decmode(edp, ARG(n), VTMODE_RESET);
273 			if (rc != 0)
274 				break;
275 		}
276 		break;
277 	case A3('\0', '$', 'p'): /* DECRQM request mode ANSI */
278 		vt100_ansimode(edp, ARG(0), VTMODE_REPORT);
279 		break;
280 	case A3('?', '$', 'p'): /* DECRQM request mode DEC */
281 		rc = vt100_decmode(edp, ARG(0), VTMODE_REPORT);
282 		break;
283 	case A3('\0', '\0', 'i'): /* MC printer controller mode */
284 	case A3('?', '\0', 'i'): /* MC printer controller mode */
285 		switch (ARG(0)) {
286 		case 0: /* print screen */
287 		case 1: /* print cursor line */
288 		case 4: /* off */
289 		case 5: /* on */
290 #ifdef VT100_PRINTNOTIMPL
291 			printf("CSI%di ignored\n", ARG(0));
292 #endif
293 			break;
294 		default:
295 #ifdef VT100_PRINTUNKNOWN
296 			printf("CSI%di unknown\n", ARG(0));
297 #endif
298 			break;
299 		}
300 		break;
301 
302 #define A2(a, b) (((a) << 8) | (b))
303 	case A2('!', 'p'): /* DECSTR soft reset VT300 only */
304 		wsemul_vt100_reset(edp);
305 		break;
306 
307 	case A2('"', 'p'): /* DECSCL */
308 		switch (ARG(0)) {
309 		case 61: /* VT100 mode (no further arguments!) */
310 			break;
311 		case 62:
312 		case 63: /* VT300 mode */
313 			break;
314 		default:
315 #ifdef VT100_PRINTUNKNOWN
316 			printf("CSI%d\"p unknown\n", ARG(0));
317 #endif
318 			break;
319 		}
320 		switch (ARG(1)) {
321 		case 0:
322 		case 2: /* 8-bit controls */
323 #ifdef VT100_PRINTNOTIMPL
324 			printf("CSI%d;%d\"p ignored\n", ARG(0), ARG(1));
325 #endif
326 			break;
327 		case 1: /* 7-bit controls */
328 			break;
329 		default:
330 #ifdef VT100_PRINTUNKNOWN
331 			printf("CSI%d;%d\"p unknown\n", ARG(0), ARG(1));
332 #endif
333 			break;
334 		}
335 		break;
336 	case A2('"', 'q'): /* DECSCA select character attribute VT300 */
337 		switch (ARG(0)) {
338 		case 0:
339 		case 1: /* erasable */
340 			break;
341 		case 2: /* not erasable */
342 #ifdef VT100_PRINTNOTIMPL
343 			printf("CSI2\"q ignored\n");
344 #endif
345 			break;
346 		default:
347 #ifdef VT100_PRINTUNKNOWN
348 			printf("CSI%d\"q unknown\n", ARG(0));
349 #endif
350 			break;
351 		}
352 		break;
353 
354 	case A2('$', 'u'): /* DECRQTSR request terminal status report */
355 		switch (ARG(0)) {
356 		case 0: /* ignored */
357 			break;
358 		case 1: /* terminal state report */
359 #ifdef VT100_PRINTNOTIMPL
360 			printf("CSI1$u ignored\n");
361 #endif
362 			break;
363 		default:
364 #ifdef VT100_PRINTUNKNOWN
365 			printf("CSI%d$u unknown\n", ARG(0));
366 #endif
367 			break;
368 		}
369 		break;
370 	case A2('$', 'w'): /* DECRQPSR request presentation status report
371 				(VT300 only) */
372 		switch (ARG(0)) {
373 		case 0: /* error */
374 			break;
375 		case 1: /* cursor information report */
376 #ifdef VT100_PRINTNOTIMPL
377 			printf("CSI1$w ignored\n");
378 #endif
379 			break;
380 		case 2: /* tab stop report */
381 		    {
382 			int i, n, ps = 0;
383 			char buf[20];
384 
385 			wsdisplay_emulinput(edp->cbcookie, "\033P2$u", 5);
386 			if (edp->tabs != NULL)
387 			    for (i = 0; i < edp->ncols; i++)
388 				if (edp->tabs[i]) {
389 					n = snprintf(buf, sizeof buf, "%s%d",
390 					    (ps ? "/" : ""), i + 1);
391 					if (n == -1)
392 						n = 0;
393 					else if (n >= sizeof buf)
394 						n = sizeof buf - 1;
395 					wsdisplay_emulinput(edp->cbcookie,
396 					    buf, n);
397 					ps = 1;
398 				}
399 			wsdisplay_emulinput(edp->cbcookie, "\033\\", 2);
400 		    }
401 			break;
402 		default:
403 #ifdef VT100_PRINTUNKNOWN
404 			printf("CSI%d$w unknown\n", ARG(0));
405 #endif
406 			break;
407 		}
408 		break;
409 	/* gratuitous { for brace matching with the next line */
410 	case A2('$', '}'): /* DECSASD select active status display */
411 		switch (ARG(0)) {
412 		case 0: /* main display */
413 		case 1: /* status line */
414 #ifdef VT100_PRINTNOTIMPL	/* { */
415 			printf("CSI%d$} ignored\n", ARG(0));
416 #endif
417 			break;
418 		default:
419 #ifdef VT100_PRINTUNKNOWN	/* { */
420 			printf("CSI%d$} unknown\n", ARG(0));
421 #endif
422 			break;
423 		}
424 		break;
425 	case A2('$', '~'): /* DECSSDD select status line type */
426 		switch (ARG(0)) {
427 		case 0: /* none */
428 		case 1: /* indicator */
429 		case 2: /* host-writable */
430 #ifdef VT100_PRINTNOTIMPL
431 			printf("CSI%d$~ ignored\n", ARG(0));
432 #endif
433 			break;
434 		default:
435 #ifdef VT100_PRINTUNKNOWN
436 			printf("CSI%d$~ unknown\n", ARG(0));
437 #endif
438 			break;
439 		}
440 		break;
441 
442 	case A2('&', 'u'): /* DECRQUPSS request user preferred
443 				  supplemental set */
444 		wsdisplay_emulinput(edp->cbcookie, "\033P0!u%5\033\\", 9);
445 		break;
446 
447 	case '@': /* ICH insert character VT300 only */
448 		n = min(DEF1_ARG(0), COLS_LEFT + 1);
449 		help = NCOLS - (edp->ccol + n);
450 		if (help > 0) {
451 			WSEMULOP(rc, edp, &edp->abortstate, copycols,
452 			    COPYCOLS(edp->ccol, edp->ccol + n, help));
453 			if (rc != 0)
454 				break;
455 		}
456 		WSEMULOP(rc, edp, &edp->abortstate, erasecols,
457 		    ERASECOLS(edp->ccol, n, edp->bkgdattr));
458 		break;
459 	case 'A': /* CUU */
460 		n = ROWS_ABOVE;
461 		if (n > 0)
462 			edp->crow -= min(DEF1_ARG(0), n);
463 		CHECK_DW;
464 		break;
465 	case 'B': /* CUD */
466 		n = ROWS_BELOW;
467 		if (n > 0)
468 			edp->crow += min(DEF1_ARG(0), n);
469 		CHECK_DW;
470 		break;
471 	case 'C': /* CUF */
472 		edp->ccol += min(DEF1_ARG(0), COLS_LEFT);
473 		break;
474 	case 'D': /* CUB */
475 		edp->ccol -= min(DEF1_ARG(0), edp->ccol);
476 		edp->flags &= ~VTFL_LASTCHAR;
477 		break;
478 	case 'G': /* CHA */
479 	case '`': /* HPA */
480 		edp->ccol = min(DEF1_ARG(0), edp->ncols) - 1;
481 		break;
482 	case 'H': /* CUP */
483 	case 'f': /* HVP */
484 		if (edp->flags & VTFL_DECOM)
485 			edp->crow = edp->scrreg_startrow +
486 			    min(DEF1_ARG(0), edp->scrreg_nrows) - 1;
487 		else
488 			edp->crow = min(DEF1_ARG(0), edp->nrows) - 1;
489 		CHECK_DW;
490 		edp->ccol = min(DEF1_ARG(1), NCOLS) - 1;
491 		edp->flags &= ~VTFL_LASTCHAR;
492 		break;
493 	case 'L': /* IL insert line */
494 	case 'M': /* DL delete line */
495 		if (edp->crow >= edp->scrreg_startrow &&
496 		    edp->crow < edp->scrreg_startrow + edp->scrreg_nrows) {
497 			int savscrstartrow, savscrnrows;
498 
499 			n = min(DEF1_ARG(0), ROWS_BELOW + 1);
500 			savscrstartrow = edp->scrreg_startrow;
501 			savscrnrows = edp->scrreg_nrows;
502 			edp->scrreg_nrows -= ROWS_ABOVE;
503 			edp->scrreg_startrow = edp->crow;
504 			if (c == 'L')
505 				rc = wsemul_vt100_scrolldown(edp, n);
506 			else
507 				rc = wsemul_vt100_scrollup(edp, n);
508 			edp->scrreg_startrow = savscrstartrow;
509 			edp->scrreg_nrows = savscrnrows;
510 		} /* else not within scrolling region, ignore the sequence */
511 		break;
512 	case 'P': /* DCH delete character */
513 		n = min(DEF1_ARG(0), COLS_LEFT + 1);
514 		help = NCOLS - (edp->ccol + n);
515 		if (help > 0) {
516 			WSEMULOP(rc, edp, &edp->abortstate, copycols,
517 			    COPYCOLS(edp->ccol + n, edp->ccol, help));
518 			if (rc != 0)
519 				break;
520 		}
521 		WSEMULOP(rc, edp, &edp->abortstate, erasecols,
522 		    ERASECOLS(NCOLS - n, n, edp->bkgdattr));
523 		break;
524 	case 'S': /* SU scroll up */
525 		wsemul_vt100_scrollup(edp, DEF1_ARG(0));
526 		break;
527 	case 'T': /* SD scroll down */
528 		wsemul_vt100_scrolldown(edp, DEF1_ARG(0));
529 		break;
530 	case 'X': /* ECH erase character */
531 		n = min(DEF1_ARG(0), COLS_LEFT + 1);
532 		WSEMULOP(rc, edp, &edp->abortstate, erasecols,
533 		    ERASECOLS(edp->ccol, n, edp->bkgdattr));
534 		break;
535 	case 'Z': /* CBT */
536 		if (edp->ccol == 0)
537 			break;
538 		for (m = 0; m < DEF1_ARG(0); m++) {
539 			if (edp->tabs) {
540 				for (n = edp->ccol - 1; n > 0; n--) {
541 					if (edp->tabs[n])
542 						break;
543 				}
544 			} else
545 				n = (edp->ccol - 1) & ~7;
546 			edp->ccol = n;
547 			if (n == 0)
548 				break;
549 		}
550 		break;
551 	case 'b': /* REP */
552 		/*
553 		 * We arbitrarily limit the repeat count to 65535 to avoid
554 		 * uninterruptible flooding of the console. This matches
555 		 * current xterm behaviour.
556 		 */
557 		n = min(DEF1_ARG(0), 65535);
558 		instate->inchar = instate->last_output;
559 		rc = wsemul_vt100_output_normal(edp, instate, kernel, n);
560 		break;
561 	case 'c': /* DA primary */
562 		if (ARG(0) == 0)
563 			wsdisplay_emulinput(edp->cbcookie, WSEMUL_VT_ID1,
564 			    sizeof(WSEMUL_VT_ID1) - 1);
565 		break;
566 	case 'd': /* VPA */
567 		edp->crow = min(DEF1_ARG(0), edp->nrows) - 1;
568 		break;
569 	case 'g': /* TBC */
570 		if (edp->tabs != NULL)
571 			switch (ARG(0)) {
572 			case 0:
573 				edp->tabs[edp->ccol] = 0;
574 				break;
575 			case 3:
576 				memset(edp->tabs, 0, edp->ncols);
577 				break;
578 			default:
579 #ifdef VT100_PRINTUNKNOWN
580 				printf("CSI%dg unknown\n", ARG(0));
581 #endif
582 				break;
583 			}
584 		break;
585 	case 'm': /* SGR select graphic rendition */
586 		flags = edp->attrflags;
587 		fgcol = edp->fgcol;
588 		bgcol = edp->bgcol;
589 		for (n = 0; n < edp->nargs; n++) {
590 			switch (ARG(n)) {
591 			case 0: /* reset */
592 				if (n == edp->nargs - 1) {
593 					edp->bkgdattr = edp->curattr = edp->defattr;
594 					edp->attrflags = 0;
595 					edp->fgcol = WSCOL_WHITE;
596 					edp->bgcol = WSCOL_BLACK;
597 					return 0;
598 				}
599 				flags = 0;
600 				fgcol = WSCOL_WHITE;
601 				bgcol = WSCOL_BLACK;
602 				break;
603 			case 1: /* bold */
604 				flags |= WSATTR_HILIT;
605 				break;
606 			case 4: /* underline */
607 				flags |= WSATTR_UNDERLINE;
608 				break;
609 			case 5: /* blink */
610 				flags |= WSATTR_BLINK;
611 				break;
612 			case 7: /* reverse */
613 				flags |= WSATTR_REVERSE;
614 				break;
615 			case 22: /* ~bold VT300 only */
616 				flags &= ~WSATTR_HILIT;
617 				break;
618 			case 24: /* ~underline VT300 only */
619 				flags &= ~WSATTR_UNDERLINE;
620 				break;
621 			case 25: /* ~blink VT300 only */
622 				flags &= ~WSATTR_BLINK;
623 				break;
624 			case 27: /* ~reverse VT300 only */
625 				flags &= ~WSATTR_REVERSE;
626 				break;
627 			case 30: case 31: case 32: case 33:
628 			case 34: case 35: case 36: case 37:
629 				/* fg color */
630 				flags |= WSATTR_WSCOLORS;
631 				fgcol = ARG(n) - 30;
632 				break;
633 			case 39:
634 				/* reset fg color */
635 				fgcol = WSCOL_WHITE;
636 				if (bgcol == WSCOL_BLACK)
637 					flags &= ~WSATTR_WSCOLORS;
638 				break;
639 			case 40: case 41: case 42: case 43:
640 			case 44: case 45: case 46: case 47:
641 				/* bg color */
642 				flags |= WSATTR_WSCOLORS;
643 				bgcol = ARG(n) - 40;
644 				break;
645 			case 49:
646 				/* reset bg color */
647 				bgcol = WSCOL_BLACK;
648 				if (fgcol == WSCOL_WHITE)
649 					flags &= ~WSATTR_WSCOLORS;
650 				break;
651 			case 90: case 91: case 92: case 93:
652 			case 94: case 95: case 96: case 97:
653 				/* bright foreground color */
654 				flags |= WSATTR_WSCOLORS;
655 				fgcol = ARG(n) - 82;
656 				break;
657 			case 100: case 101: case 102: case 103:
658 			case 104: case 105: case 106: case 107:
659 				/* bright background color */
660 				flags |= WSATTR_WSCOLORS;
661 				bgcol = ARG(n) - 92;
662 				break;
663 			default:
664 #ifdef VT100_PRINTUNKNOWN
665 				printf("CSI%dm unknown\n", ARG(n));
666 #endif
667 				break;
668 			}
669 		}
670 		if (vt100_selectattribute(edp, flags, fgcol, bgcol, &attr,
671 		    &bkgdattr)) {
672 #ifdef VT100_DEBUG
673 			printf("error allocating attr %d/%d/%x\n",
674 			       fgcol, bgcol, flags);
675 #endif
676 		} else {
677 			edp->curattr = attr;
678 			edp->bkgdattr = bkgdattr;
679 			edp->attrflags = flags;
680 			edp->fgcol = fgcol;
681 			edp->bgcol = bgcol;
682 		}
683 		break;
684 	case 'n': /* reports */
685 		switch (ARG(0)) {
686 		case 5: /* DSR operating status */
687 			/* 0 = OK, 3 = malfunction */
688 			wsdisplay_emulinput(edp->cbcookie, "\033[0n", 4);
689 			break;
690 		case 6: /* DSR cursor position report */
691 		    {
692 			char buf[20];
693 			int row;
694 			if (edp->flags & VTFL_DECOM) {
695 				row = ROWS_ABOVE;
696 				if (row < 0)
697 					row = 0;
698 			} else
699 				row = edp->crow;
700 			n = snprintf(buf, sizeof buf, "\033[%d;%dR",
701 				    row + 1, edp->ccol + 1);
702 			if (n == -1)
703 				n = 0;
704 			else if (n >= sizeof buf)
705 				n = sizeof buf - 1;
706 			wsdisplay_emulinput(edp->cbcookie, buf, n);
707 		    }
708 			break;
709 		case 15: /* DSR printer status */
710 			/* 13 = no printer, 10 = ready, 11 = not ready */
711 			wsdisplay_emulinput(edp->cbcookie, "\033[?13n", 6);
712 			break;
713 		case 25: /* UDK status - VT300 only */
714 			/* 20 = locked, 21 = unlocked */
715 			wsdisplay_emulinput(edp->cbcookie, "\033[?21n", 6);
716 			break;
717 		case 26: /* keyboard dialect */
718 			/* 1 = north american , 7 = german */
719 			wsdisplay_emulinput(edp->cbcookie, "\033[?27;1n", 8);
720 			break;
721 		default:
722 #ifdef VT100_PRINTUNKNOWN
723 			printf("CSI%dn unknown\n", ARG(0));
724 #endif
725 			break;
726 		}
727 		break;
728 	case 'r': /* DECSTBM set top/bottom margins */
729 		help = min(DEF1_ARG(0), edp->nrows) - 1;
730 		n = min(DEFx_ARG(1, edp->nrows), edp->nrows) - help;
731 		if (n < 2) {
732 			/* minimal scrolling region has 2 lines */
733 			return 0;
734 		} else {
735 			edp->scrreg_startrow = help;
736 			edp->scrreg_nrows = n;
737 		}
738 		edp->crow = ((edp->flags & VTFL_DECOM) ?
739 			     edp->scrreg_startrow : 0);
740 		edp->ccol = 0;
741 		break;
742 	case 's':
743 		edp->flags |= VTFL_SAVEDCURS;
744 		edp->savedcursor_row = edp->crow;
745 		edp->savedcursor_col = edp->ccol;
746 		break;
747 	case 'u':
748 		if (edp->flags & VTFL_SAVEDCURS) {
749 			edp->crow = edp->savedcursor_row;
750 			edp->ccol = edp->savedcursor_col;
751 		}
752 		break;
753 	case 'y':
754 		switch (ARG(0)) {
755 		case 4: /* DECTST invoke confidence test */
756 			/* ignore */
757 			break;
758 		default:
759 #ifdef VT100_PRINTUNKNOWN
760 			printf("CSI%dy unknown\n", ARG(0));
761 #endif
762 			break;
763 		}
764 		break;
765 	default:
766 #ifdef VT100_PRINTUNKNOWN
767 		printf("CSI %x (%d, %d) unknown\n",
768 		    instate->inchar, ARG(0), ARG(1));
769 #endif
770 		break;
771 	}
772 
773 	return rc;
774 }
775 
776 /*
777  * get an attribute from the graphics driver,
778  * try to find replacements if the desired appearance
779  * is not supported
780  */
781 int
vt100_selectattribute(struct wsemul_vt100_emuldata * edp,int flags,int fgcol,int bgcol,uint32_t * attr,uint32_t * bkgdattr)782 vt100_selectattribute(struct wsemul_vt100_emuldata *edp, int flags, int fgcol,
783     int bgcol, uint32_t *attr, uint32_t *bkgdattr)
784 {
785 	int error;
786 
787 	if ((flags & WSATTR_WSCOLORS) &&
788 	    !(edp->scrcapabilities & WSSCREEN_WSCOLORS)) {
789 		flags &= ~WSATTR_WSCOLORS;
790 #ifdef VT100_DEBUG
791 		printf("colors ignored (impossible)\n");
792 #endif
793 	}
794 	error = (*edp->emulops->pack_attr)(edp->emulcookie, fgcol, bgcol,
795 	    flags & WSATTR_WSCOLORS, bkgdattr);
796 	if (error)
797 		return (error);
798 
799 	if ((flags & WSATTR_HILIT) &&
800 	    !(edp->scrcapabilities & WSSCREEN_HILIT)) {
801 		flags &= ~WSATTR_HILIT;
802 		if (edp->scrcapabilities & WSSCREEN_WSCOLORS) {
803 			fgcol = WSCOL_RED;
804 			flags |= WSATTR_WSCOLORS;
805 		} else {
806 #ifdef VT100_DEBUG
807 			printf("bold ignored (impossible)\n");
808 #endif
809 		}
810 	}
811 	if ((flags & WSATTR_UNDERLINE) &&
812 	    !(edp->scrcapabilities & WSSCREEN_UNDERLINE)) {
813 		flags &= ~WSATTR_UNDERLINE;
814 		if (edp->scrcapabilities & WSSCREEN_WSCOLORS) {
815 			fgcol = WSCOL_CYAN;
816 			flags &= ~WSATTR_UNDERLINE;
817 			flags |= WSATTR_WSCOLORS;
818 		} else {
819 #ifdef VT100_DEBUG
820 			printf("underline ignored (impossible)\n");
821 #endif
822 		}
823 	}
824 	if ((flags & WSATTR_BLINK) &&
825 	    !(edp->scrcapabilities & WSSCREEN_BLINK)) {
826 		flags &= ~WSATTR_BLINK;
827 #ifdef VT100_DEBUG
828 		printf("blink ignored (impossible)\n");
829 #endif
830 	}
831 	if ((flags & WSATTR_REVERSE) &&
832 	    !(edp->scrcapabilities & WSSCREEN_REVERSE)) {
833 		flags &= ~WSATTR_REVERSE;
834 		if (edp->scrcapabilities & WSSCREEN_WSCOLORS) {
835 			int help;
836 			help = bgcol;
837 			bgcol = fgcol;
838 			fgcol = help;
839 			flags |= WSATTR_WSCOLORS;
840 		} else {
841 #ifdef VT100_DEBUG
842 			printf("reverse ignored (impossible)\n");
843 #endif
844 		}
845 	}
846 	error = (*edp->emulops->pack_attr)(edp->emulcookie, fgcol, bgcol,
847 					    flags, attr);
848 	if (error)
849 		return (error);
850 
851 	return (0);
852 }
853 
854 /*
855  * handle device control sequences if the main state machine
856  * told so by setting edp->dcstype to a nonzero value
857  */
858 void
wsemul_vt100_handle_dcs(struct wsemul_vt100_emuldata * edp)859 wsemul_vt100_handle_dcs(struct wsemul_vt100_emuldata *edp)
860 {
861 	int i, pos;
862 
863 	switch (edp->dcstype) {
864 	case 0: /* not handled */
865 		return;
866 	case DCSTYPE_TABRESTORE:
867 		if (edp->tabs != NULL) {
868 			memset(edp->tabs, 0, edp->ncols);
869 			pos = 0;
870 			if (edp->dcsarg == NULL)
871 				goto out;
872 			for (i = 0; i < edp->dcspos; i++) {
873 				char c = edp->dcsarg[i];
874 				switch (c) {
875 				case '0': case '1': case '2': case '3':
876 				case '4': case '5': case '6': case '7':
877 				case '8': case '9':
878 					pos = pos * 10 + (c - '0');
879 					if (pos > edp->ncols)
880 						goto out;
881 					break;
882 				case '/':
883 					if (pos > 0)
884 						edp->tabs[pos - 1] = 1;
885 					pos = 0;
886 					break;
887 				default:
888 #ifdef VT100_PRINTUNKNOWN
889 					printf("unknown char %c in DCS\n", c);
890 #endif
891 					break;
892 				}
893 			}
894 			if (pos > 0)
895 				edp->tabs[pos - 1] = 1;
896 		}
897 		break;
898 	default:
899 #ifdef VT100_PRINTUNKNOWN
900 		printf("wsemul_vt100_handle_dcs: bad type %d\n", edp->dcstype);
901 #endif
902 		break;
903 	}
904 out:
905 	edp->dcstype = 0;
906 }
907 
908 int
vt100_ansimode(struct wsemul_vt100_emuldata * edp,int nr,int op)909 vt100_ansimode(struct wsemul_vt100_emuldata *edp, int nr, int op)
910 {
911 	int res = 0; /* default: unknown */
912 
913 	switch (nr) {
914 	case 2: /* KAM keyboard locked/unlocked */
915 		break;
916 	case 3: /* CRM control representation */
917 		break;
918 	case 4: /* IRM insert/replace characters */
919 		if (op == VTMODE_SET)
920 			edp->flags |= VTFL_INSERTMODE;
921 		else if (op == VTMODE_RESET)
922 			edp->flags &= ~VTFL_INSERTMODE;
923 		res = ((edp->flags & VTFL_INSERTMODE) ? 1 : 2);
924 		break;
925 	case 10: /* HEM horizontal editing (permanently reset) */
926 		res = 4;
927 		break;
928 	case 12: /* SRM local echo off/on */
929 		res = 4; /* permanently reset ??? */
930 		break;
931 	case 20: /* LNM newline = newline/linefeed */
932 		break;
933 	default:
934 #ifdef VT100_PRINTUNKNOWN
935 		printf("ANSI mode %d unknown\n", nr);
936 #endif
937 		break;
938 	}
939 	return (res);
940 }
941 
942 int
vt100_decmode(struct wsemul_vt100_emuldata * edp,int nr,int op)943 vt100_decmode(struct wsemul_vt100_emuldata *edp, int nr, int op)
944 {
945 #if 0	/* res unused... return it by reference if ever necessary */
946 	int res = 0; /* default: unknown */
947 #endif
948 	int flags = edp->flags;
949 	int rc = 0;
950 
951 	switch (nr) {
952 	case 1: /* DECCKM application/nomal cursor keys */
953 		if (op == VTMODE_SET)
954 			flags |= VTFL_APPLCURSOR;
955 		else if (op == VTMODE_RESET)
956 			flags &= ~VTFL_APPLCURSOR;
957 #if 0
958 		res = ((flags & VTFL_APPLCURSOR) ? 1 : 2);
959 #endif
960 		break;
961 	case 2: /* DECANM ANSI vt100/vt52 */
962 #if 0
963 		res = 3; /* permanently set ??? */
964 #endif
965 		break;
966 	case 3: /* DECCOLM 132/80 cols */
967 	case 4: /* DECSCLM smooth/jump scroll */
968 	case 5: /* DECSCNM light/dark background */
969 #if 0
970 		res = 4; /* all permanently reset ??? */
971 #endif
972 		break;
973 	case 6: /* DECOM move within/outside margins */
974 		if (op == VTMODE_SET)
975 			flags |= VTFL_DECOM;
976 		else if (op == VTMODE_RESET)
977 			flags &= ~VTFL_DECOM;
978 #if 0
979 		res = ((flags & VTFL_DECOM) ? 1 : 2);
980 #endif
981 		break;
982 	case 7: /* DECAWM autowrap */
983 		if (op == VTMODE_SET)
984 			flags |= VTFL_DECAWM;
985 		else if (op == VTMODE_RESET)
986 			flags &= ~VTFL_DECAWM;
987 #if 0
988 		res = ((flags & VTFL_DECAWM) ? 1 : 2);
989 #endif
990 		break;
991 	case 8: /* DECARM keyboard autorepeat */
992 		break;
993 	case 18: /* DECPFF print form feed */
994 		break;
995 	case 19: /* DECPEX printer extent: screen/scrolling region */
996 		break;
997 	case 25: /* DECTCEM text cursor on/off */
998 		if (op == VTMODE_SET)
999 			flags |= VTFL_CURSORON;
1000 		else if (op == VTMODE_RESET)
1001 			flags &= ~VTFL_CURSORON;
1002 		if (flags != edp->flags)
1003 			WSEMULOP(rc, edp, &edp->abortstate, cursor,
1004 			    (edp->emulcookie, flags & VTFL_CURSORON, edp->crow,
1005 			     edp->ccol));
1006 #if 0
1007 		res = ((flags & VTFL_CURSORON) ? 1 : 2);
1008 #endif
1009 		break;
1010 	case 42: /* DECNRCM use 7-bit NRC /
1011 		    7/8 bit from DEC multilingual or ISO-latin-1*/
1012 		if (op == VTMODE_SET)
1013 			flags |= VTFL_NATCHARSET;
1014 		else if (op == VTMODE_RESET)
1015 			flags &= ~VTFL_NATCHARSET;
1016 #if 0
1017 		res = ((flags & VTFL_NATCHARSET) ? 1 : 2);
1018 #endif
1019 		break;
1020 	case 66: /* DECNKM numeric keypad */
1021 		break;
1022 	case 68: /* DECKBUM keyboard usage data processing/typewriter */
1023 		break;
1024 	default:
1025 #ifdef VT100_PRINTUNKNOWN
1026 		printf("DEC mode %d unknown\n", nr);
1027 #endif
1028 		break;
1029 	}
1030 	edp->flags = flags;
1031 
1032 	return rc;
1033 }
1034