xref: /freebsd/sys/dev/syscons/scterm-sc.c (revision 7660b554bc59a07be0431c17e0e33815818baa69)
1 /*-
2  * Copyright (c) 1999 Kazutaka YOKOTA <yokota@zodiac.mech.utsunomiya-u.ac.jp>
3  * Copyright (c) 1992-1998 S�ren Schmidt
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer as
11  *    the first lines of this file unmodified.
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 AUTHORS ``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 AUTHORS 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 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30 
31 #include "opt_syscons.h"
32 
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/kernel.h>
36 #include <sys/consio.h>
37 
38 #ifdef __sparc64__
39 #include <machine/sc_machdep.h>
40 #else
41 #include <machine/pc/display.h>
42 #endif
43 
44 #include <dev/syscons/syscons.h>
45 #include <dev/syscons/sctermvar.h>
46 
47 #ifndef SC_DUMB_TERMINAL
48 
49 #define MAX_ESC_PAR	5
50 
51 /* attribute flags */
52 typedef struct {
53 	u_short		fg;			/* foreground color */
54 	u_short		bg;			/* background color */
55 } color_t;
56 
57 typedef struct {
58 	int		flags;
59 #define SCTERM_BUSY	(1 << 0)
60 	int		esc;
61 	int		num_param;
62 	int		last_param;
63 	int		param[MAX_ESC_PAR];
64 	int		saved_xpos;
65 	int		saved_ypos;
66 	int		attr_mask;		/* current logical attr mask */
67 #define NORMAL_ATTR	0x00
68 #define BLINK_ATTR	0x01
69 #define BOLD_ATTR	0x02
70 #define UNDERLINE_ATTR	0x04
71 #define REVERSE_ATTR	0x08
72 #define FG_CHANGED	0x10
73 #define BG_CHANGED	0x20
74 	int		cur_attr;		/* current hardware attr word */
75 	color_t		cur_color;		/* current hardware color */
76 	color_t		std_color;		/* normal hardware color */
77 	color_t		rev_color;		/* reverse hardware color */
78 	color_t		dflt_std_color;		/* default normal color */
79 	color_t		dflt_rev_color;		/* default reverse color */
80 } term_stat;
81 
82 static sc_term_init_t	scterm_init;
83 static sc_term_term_t	scterm_term;
84 static sc_term_puts_t	scterm_puts;
85 static sc_term_ioctl_t	scterm_ioctl;
86 static sc_term_reset_t	scterm_reset;
87 static sc_term_default_attr_t	scterm_default_attr;
88 static sc_term_clear_t	scterm_clear;
89 static sc_term_notify_t	scterm_notify;
90 static sc_term_input_t	scterm_input;
91 
92 static sc_term_sw_t sc_term_sc = {
93 	{ NULL, NULL },
94 	"sc",					/* emulator name */
95 	"syscons terminal",			/* description */
96 	"*",					/* matching renderer, any :-) */
97 	sizeof(term_stat),			/* softc size */
98 	0,
99 	scterm_init,
100 	scterm_term,
101 	scterm_puts,
102 	scterm_ioctl,
103 	scterm_reset,
104 	scterm_default_attr,
105 	scterm_clear,
106 	scterm_notify,
107 	scterm_input,
108 };
109 
110 SCTERM_MODULE(sc, sc_term_sc);
111 
112 static term_stat	reserved_term_stat;
113 static void		scterm_scan_esc(scr_stat *scp, term_stat *tcp,
114 					u_char c);
115 static int		mask2attr(term_stat *tcp);
116 
117 static int
118 scterm_init(scr_stat *scp, void **softc, int code)
119 {
120 	term_stat *tcp;
121 
122 	if (*softc == NULL) {
123 		if (reserved_term_stat.flags & SCTERM_BUSY)
124 			return EINVAL;
125 		*softc = &reserved_term_stat;
126 	}
127 	tcp = *softc;
128 
129 	switch (code) {
130 	case SC_TE_COLD_INIT:
131 		bzero(tcp, sizeof(*tcp));
132 		tcp->flags = SCTERM_BUSY;
133 		tcp->esc = 0;
134 		tcp->saved_xpos = -1;
135 		tcp->saved_ypos = -1;
136 		tcp->attr_mask = NORMAL_ATTR;
137 		/* XXX */
138 		tcp->dflt_std_color.fg = SC_NORM_ATTR & 0x0f;
139 		tcp->dflt_std_color.bg = (SC_NORM_ATTR >> 4) & 0x0f;
140 		tcp->dflt_rev_color.fg = SC_NORM_REV_ATTR & 0x0f;
141 		tcp->dflt_rev_color.bg = (SC_NORM_REV_ATTR >> 4) & 0x0f;
142 		tcp->std_color = tcp->dflt_std_color;
143 		tcp->rev_color = tcp->dflt_rev_color;
144 		tcp->cur_color = tcp->std_color;
145 		tcp->cur_attr = mask2attr(tcp);
146 		++sc_term_sc.te_refcount;
147 		break;
148 
149 	case SC_TE_WARM_INIT:
150 		tcp->esc = 0;
151 		tcp->saved_xpos = -1;
152 		tcp->saved_ypos = -1;
153 #if 0
154 		tcp->std_color = tcp->dflt_std_color;
155 		tcp->rev_color = tcp->dflt_rev_color;
156 #endif
157 		tcp->cur_color = tcp->std_color;
158 		tcp->cur_attr = mask2attr(tcp);
159 		break;
160 	}
161 
162 	return 0;
163 }
164 
165 static int
166 scterm_term(scr_stat *scp, void **softc)
167 {
168 	if (*softc == &reserved_term_stat) {
169 		*softc = NULL;
170 		bzero(&reserved_term_stat, sizeof(reserved_term_stat));
171 	}
172 	--sc_term_sc.te_refcount;
173 	return 0;
174 }
175 
176 static void
177 scterm_scan_esc(scr_stat *scp, term_stat *tcp, u_char c)
178 {
179 	static u_char ansi_col[16] = {
180 #ifdef __alpha__
181 		/*
182 		 * DEC is evil.  They switch the red and blue attributes in
183 		 * the palette in the system console.  As a simple work-around,
184 		 * re-map the ANSI colors appropriately.
185 		 */
186 		FG_BLACK,     FG_BLUE,         FG_GREEN,      FG_CYAN,
187 		FG_RED,       FG_MAGENTA,      FG_BROWN,      FG_LIGHTGREY,
188 		FG_DARKGREY,  FG_LIGHTBLUE,    FG_LIGHTGREEN, FG_LIGHTCYAN,
189 		FG_LIGHTRED,  FG_LIGHTMAGENTA, FG_YELLOW,     FG_WHITE
190 #else
191 		FG_BLACK,     FG_RED,          FG_GREEN,      FG_BROWN,
192 		FG_BLUE,      FG_MAGENTA,      FG_CYAN,       FG_LIGHTGREY,
193 		FG_DARKGREY,  FG_LIGHTRED,     FG_LIGHTGREEN, FG_YELLOW,
194 		FG_LIGHTBLUE, FG_LIGHTMAGENTA, FG_LIGHTCYAN,  FG_WHITE
195 #endif
196 	};
197 	static int cattrs[] = {
198 		0,					/* block */
199 		CONS_BLINK_CURSOR,			/* blinking block */
200 		CONS_CHAR_CURSOR,			/* underline */
201 		CONS_CHAR_CURSOR | CONS_BLINK_CURSOR,	/* blinking underline */
202 		CONS_RESET_CURSOR,			/* reset to default */
203 		CONS_HIDDEN_CURSOR,			/* hide cursor */
204 	};
205 	static int tcattrs[] = {
206 		CONS_RESET_CURSOR | CONS_LOCAL_CURSOR,	/* normal */
207 		CONS_HIDDEN_CURSOR | CONS_LOCAL_CURSOR,	/* invisible */
208 		CONS_BLINK_CURSOR | CONS_LOCAL_CURSOR,	/* very visible */
209 	};
210 	sc_softc_t *sc;
211 	int v0, v1, v2;
212 	int i, n;
213 
214 	i = n = 0;
215 	sc = scp->sc;
216 	if (tcp->esc == 1) {	/* seen ESC */
217 		switch (c) {
218 
219 		case '7':	/* Save cursor position */
220 			tcp->saved_xpos = scp->xpos;
221 			tcp->saved_ypos = scp->ypos;
222 			break;
223 
224 		case '8':	/* Restore saved cursor position */
225 			if (tcp->saved_xpos >= 0 && tcp->saved_ypos >= 0)
226 				sc_move_cursor(scp, tcp->saved_xpos,
227 					       tcp->saved_ypos);
228 			break;
229 
230 		case '[':	/* Start ESC [ sequence */
231 			tcp->esc = 2;
232 			tcp->last_param = -1;
233 			for (i = tcp->num_param; i < MAX_ESC_PAR; i++)
234 				tcp->param[i] = 1;
235 			tcp->num_param = 0;
236 			return;
237 
238 		case 'M':	/* Move cursor up 1 line, scroll if at top */
239 			sc_term_up_scroll(scp, 1, sc->scr_map[0x20],
240 					  tcp->cur_attr, 0, 0);
241 			break;
242 #if notyet
243 		case 'Q':
244 			tcp->esc = 4;
245 			return;
246 #endif
247 		case 'c':       /* reset */
248 			tcp->attr_mask = NORMAL_ATTR;
249 			tcp->cur_color = tcp->std_color
250 				       = tcp->dflt_std_color;
251 			tcp->rev_color = tcp->dflt_rev_color;
252 			tcp->cur_attr = mask2attr(tcp);
253 			sc_change_cursor_shape(scp,
254 			    CONS_RESET_CURSOR | CONS_LOCAL_CURSOR, -1, -1);
255 			sc_clear_screen(scp);
256 			break;
257 
258 		case '(':	/* iso-2022: designate 94 character set to G0 */
259 			tcp->esc = 5;
260 			return;
261 		}
262 	} else if (tcp->esc == 2) {	/* seen ESC [ */
263 		if (c >= '0' && c <= '9') {
264 			if (tcp->num_param < MAX_ESC_PAR) {
265 				if (tcp->last_param != tcp->num_param) {
266 					tcp->last_param = tcp->num_param;
267 					tcp->param[tcp->num_param] = 0;
268 				} else {
269 					tcp->param[tcp->num_param] *= 10;
270 				}
271 				tcp->param[tcp->num_param] += c - '0';
272 				return;
273 			}
274 		}
275 		tcp->num_param = tcp->last_param + 1;
276 		switch (c) {
277 
278 		case ';':
279 			if (tcp->num_param < MAX_ESC_PAR)
280 				return;
281 			break;
282 
283 		case '=':
284 			tcp->esc = 3;
285 			tcp->last_param = -1;
286 			for (i = tcp->num_param; i < MAX_ESC_PAR; i++)
287 				tcp->param[i] = 1;
288 			tcp->num_param = 0;
289 			return;
290 
291 		case 'A':	/* up n rows */
292 			sc_term_up(scp, tcp->param[0], 0);
293 			break;
294 
295 		case 'B':	/* down n rows */
296 			sc_term_down(scp, tcp->param[0], 0);
297 			break;
298 
299 		case 'C':	/* right n columns */
300 			sc_term_right(scp, tcp->param[0]);
301 			break;
302 
303 		case 'D':	/* left n columns */
304 			sc_term_left(scp, tcp->param[0]);
305 			break;
306 
307 		case 'E':	/* cursor to start of line n lines down */
308 			n = tcp->param[0];
309 			if (n < 1)
310 				n = 1;
311 			sc_move_cursor(scp, 0, scp->ypos + n);
312 			break;
313 
314 		case 'F':	/* cursor to start of line n lines up */
315 			n = tcp->param[0];
316 			if (n < 1)
317 				n = 1;
318 			sc_move_cursor(scp, 0, scp->ypos - n);
319 			break;
320 
321 		case 'f':	/* Cursor move */
322 		case 'H':
323 			if (tcp->num_param == 0)
324 				sc_move_cursor(scp, 0, 0);
325 			else if (tcp->num_param == 2)
326 				sc_move_cursor(scp, tcp->param[1] - 1,
327 					       tcp->param[0] - 1);
328 			break;
329 
330 		case 'J':	/* Clear all or part of display */
331 			if (tcp->num_param == 0)
332 				n = 0;
333 			else
334 				n = tcp->param[0];
335 			sc_term_clr_eos(scp, n, sc->scr_map[0x20],
336 					tcp->cur_attr);
337 			break;
338 
339 		case 'K':	/* Clear all or part of line */
340 			if (tcp->num_param == 0)
341 				n = 0;
342 			else
343 				n = tcp->param[0];
344 			sc_term_clr_eol(scp, n, sc->scr_map[0x20],
345 					tcp->cur_attr);
346 			break;
347 
348 		case 'L':	/* Insert n lines */
349 			sc_term_ins_line(scp, scp->ypos, tcp->param[0],
350 					 sc->scr_map[0x20], tcp->cur_attr, 0);
351 			break;
352 
353 		case 'M':	/* Delete n lines */
354 			sc_term_del_line(scp, scp->ypos, tcp->param[0],
355 					 sc->scr_map[0x20], tcp->cur_attr, 0);
356 			break;
357 
358 		case 'P':	/* Delete n chars */
359 			sc_term_del_char(scp, tcp->param[0],
360 					 sc->scr_map[0x20], tcp->cur_attr);
361 			break;
362 
363 		case '@':	/* Insert n chars */
364 			sc_term_ins_char(scp, tcp->param[0],
365 					 sc->scr_map[0x20], tcp->cur_attr);
366 			break;
367 
368 		case 'S':	/* scroll up n lines */
369 			sc_term_del_line(scp, 0, tcp->param[0],
370 					 sc->scr_map[0x20], tcp->cur_attr, 0);
371 			break;
372 
373 		case 'T':	/* scroll down n lines */
374 			sc_term_ins_line(scp, 0, tcp->param[0],
375 					 sc->scr_map[0x20], tcp->cur_attr, 0);
376 			break;
377 
378 		case 'X':	/* erase n characters in line */
379 			n = tcp->param[0];
380 			if (n < 1)
381 				n = 1;
382 			if (n > scp->xsize - scp->xpos)
383 				n = scp->xsize - scp->xpos;
384 			sc_vtb_erase(&scp->vtb, scp->cursor_pos, n,
385 				     sc->scr_map[0x20], tcp->cur_attr);
386 			mark_for_update(scp, scp->cursor_pos);
387 			mark_for_update(scp, scp->cursor_pos + n - 1);
388 			break;
389 
390 		case 'Z':	/* move n tabs backwards */
391 			sc_term_backtab(scp, tcp->param[0]);
392 			break;
393 
394 		case '`':	/* move cursor to column n */
395 			sc_term_col(scp, tcp->param[0]);
396 			break;
397 
398 		case 'a':	/* move cursor n columns to the right */
399 			sc_term_right(scp, tcp->param[0]);
400 			break;
401 
402 		case 'd':	/* move cursor to row n */
403 			sc_term_row(scp, tcp->param[0]);
404 			break;
405 
406 		case 'e':	/* move cursor n rows down */
407 			sc_term_down(scp, tcp->param[0], 0);
408 			break;
409 
410 		case 'm':	/* change attribute */
411 			if (tcp->num_param == 0) {
412 				tcp->attr_mask = NORMAL_ATTR;
413 				tcp->cur_color = tcp->std_color;
414 				tcp->cur_attr = mask2attr(tcp);
415 				break;
416 			}
417 			for (i = 0; i < tcp->num_param; i++) {
418 				switch (n = tcp->param[i]) {
419 				case 0:	/* back to normal */
420 					tcp->attr_mask = NORMAL_ATTR;
421 					tcp->cur_color = tcp->std_color;
422 					tcp->cur_attr = mask2attr(tcp);
423 					break;
424 				case 1:	/* bold */
425 					tcp->attr_mask |= BOLD_ATTR;
426 					tcp->cur_attr = mask2attr(tcp);
427 					break;
428 				case 4:	/* underline */
429 					tcp->attr_mask |= UNDERLINE_ATTR;
430 					tcp->cur_attr = mask2attr(tcp);
431 					break;
432 				case 5:	/* blink */
433 					tcp->attr_mask |= BLINK_ATTR;
434 					tcp->cur_attr = mask2attr(tcp);
435 					break;
436 				case 7: /* reverse */
437 					tcp->attr_mask |= REVERSE_ATTR;
438 					tcp->cur_attr = mask2attr(tcp);
439 					break;
440 				case 22: /* remove bold (or dim) */
441 					tcp->attr_mask &= ~BOLD_ATTR;
442 					tcp->cur_attr = mask2attr(tcp);
443 					break;
444 				case 24: /* remove underline */
445 					tcp->attr_mask &= ~UNDERLINE_ATTR;
446 					tcp->cur_attr = mask2attr(tcp);
447 					break;
448 				case 25: /* remove blink */
449 					tcp->attr_mask &= ~BLINK_ATTR;
450 					tcp->cur_attr = mask2attr(tcp);
451 					break;
452 				case 27: /* remove reverse */
453 					tcp->attr_mask &= ~REVERSE_ATTR;
454 					tcp->cur_attr = mask2attr(tcp);
455 					break;
456 				case 30: case 31: /* set ansi fg color */
457 				case 32: case 33: case 34:
458 				case 35: case 36: case 37:
459 					tcp->attr_mask |= FG_CHANGED;
460 					tcp->cur_color.fg = ansi_col[n - 30];
461 					tcp->cur_attr = mask2attr(tcp);
462 					break;
463 				case 39: /* restore fg color back to normal */
464 					tcp->attr_mask &= ~(FG_CHANGED|BOLD_ATTR);
465 					tcp->cur_color.fg = tcp->std_color.fg;
466 					tcp->cur_attr = mask2attr(tcp);
467 					break;
468 				case 40: case 41: /* set ansi bg color */
469 				case 42: case 43: case 44:
470 				case 45: case 46: case 47:
471 					tcp->attr_mask |= BG_CHANGED;
472 					tcp->cur_color.bg = ansi_col[n - 40];
473 					tcp->cur_attr = mask2attr(tcp);
474 		    			break;
475 				case 49: /* restore bg color back to normal */
476 					tcp->attr_mask &= ~BG_CHANGED;
477 					tcp->cur_color.bg = tcp->std_color.bg;
478 					tcp->cur_attr = mask2attr(tcp);
479 					break;
480 				}
481 			}
482 			break;
483 
484 		case 's':	/* Save cursor position */
485 			tcp->saved_xpos = scp->xpos;
486 			tcp->saved_ypos = scp->ypos;
487 			break;
488 
489 		case 'u':	/* Restore saved cursor position */
490 			if (tcp->saved_xpos >= 0 && tcp->saved_ypos >= 0)
491 				sc_move_cursor(scp, tcp->saved_xpos,
492 					       tcp->saved_ypos);
493 			break;
494 
495 		case 'x':
496 			if (tcp->num_param == 0)
497 				n = 0;
498 			else
499 				n = tcp->param[0];
500 			switch (n) {
501 			case 0: /* reset colors and attributes back to normal */
502 				tcp->attr_mask = NORMAL_ATTR;
503 				tcp->cur_color = tcp->std_color
504 					       = tcp->dflt_std_color;
505 				tcp->rev_color = tcp->dflt_rev_color;
506 				tcp->cur_attr = mask2attr(tcp);
507 				break;
508 			case 1:	/* set ansi background */
509 				tcp->attr_mask &= ~BG_CHANGED;
510 				tcp->cur_color.bg = tcp->std_color.bg
511 						  = ansi_col[tcp->param[1] & 0x0f];
512 				tcp->cur_attr = mask2attr(tcp);
513 				break;
514 			case 2:	/* set ansi foreground */
515 				tcp->attr_mask &= ~FG_CHANGED;
516 				tcp->cur_color.fg = tcp->std_color.fg
517 						  = ansi_col[tcp->param[1] & 0x0f];
518 				tcp->cur_attr = mask2attr(tcp);
519 				break;
520 			case 3: /* set adapter attribute directly */
521 				tcp->attr_mask &= ~(FG_CHANGED | BG_CHANGED);
522 				tcp->cur_color.fg = tcp->std_color.fg
523 						  = tcp->param[1] & 0x0f;
524 				tcp->cur_color.bg = tcp->std_color.bg
525 						  = (tcp->param[1] >> 4) & 0x0f;
526 				tcp->cur_attr = mask2attr(tcp);
527 				break;
528 			case 5: /* set ansi reverse background */
529 				tcp->rev_color.bg = ansi_col[tcp->param[1] & 0x0f];
530 				tcp->cur_attr = mask2attr(tcp);
531 				break;
532 			case 6: /* set ansi reverse foreground */
533 				tcp->rev_color.fg = ansi_col[tcp->param[1] & 0x0f];
534 				tcp->cur_attr = mask2attr(tcp);
535 				break;
536 			case 7: /* set adapter reverse attribute directly */
537 				tcp->rev_color.fg = tcp->param[1] & 0x0f;
538 				tcp->rev_color.bg = (tcp->param[1] >> 4) & 0x0f;
539 				tcp->cur_attr = mask2attr(tcp);
540 				break;
541 			}
542 			break;
543 
544 		case 'z':	/* switch to (virtual) console n */
545 			if (tcp->num_param == 1)
546 				sc_switch_scr(sc, tcp->param[0]);
547 			break;
548 		}
549 	} else if (tcp->esc == 3) {	/* seen ESC [0-9]+ = */
550 		if (c >= '0' && c <= '9') {
551 			if (tcp->num_param < MAX_ESC_PAR) {
552 				if (tcp->last_param != tcp->num_param) {
553 					tcp->last_param = tcp->num_param;
554 					tcp->param[tcp->num_param] = 0;
555 				} else {
556 					tcp->param[tcp->num_param] *= 10;
557 				}
558 				tcp->param[tcp->num_param] += c - '0';
559 				return;
560 			}
561 		}
562 		tcp->num_param = tcp->last_param + 1;
563 		switch (c) {
564 
565 		case ';':
566 			if (tcp->num_param < MAX_ESC_PAR)
567 				return;
568 			break;
569 
570 		case 'A':   /* set display border color */
571 			if (tcp->num_param == 1) {
572 				scp->border=tcp->param[0] & 0xff;
573 				if (scp == sc->cur_scp)
574 					sc_set_border(scp, scp->border);
575 			}
576 			break;
577 
578 		case 'B':   /* set bell pitch and duration */
579 			if (tcp->num_param == 2) {
580 				scp->bell_pitch = tcp->param[0];
581 				scp->bell_duration =
582 				    (tcp->param[1] * hz + 99) / 100;
583 			}
584 			break;
585 
586 		case 'C':   /* set global/parmanent cursor type & shape */
587 			i = spltty();
588 			n = tcp->num_param;
589 			v0 = tcp->param[0];
590 			v1 = tcp->param[1];
591 			v2 = tcp->param[2];
592 			switch (n) {
593 			case 1:	/* flags only */
594 				if (v0 < sizeof(cattrs)/sizeof(cattrs[0]))
595 					v0 = cattrs[v0];
596 				else	/* backward compatibility */
597 					v0 = cattrs[v0 & 0x3];
598 				sc_change_cursor_shape(scp, v0, -1, -1);
599 				break;
600 			case 2:
601 				v2 = 0;
602 				v0 &= 0x1f;	/* backward compatibility */
603 				v1 &= 0x1f;
604 				/* FALL THROUGH */
605 			case 3:	/* base and height */
606 				if (v2 == 0)	/* count from top */
607 					sc_change_cursor_shape(scp, -1,
608 					    scp->font_size - v1 - 1,
609 					    v1 - v0 + 1);
610 				else if (v2 == 1) /* count from bottom */
611 					sc_change_cursor_shape(scp, -1,
612 					    v0, v1 - v0 + 1);
613 				break;
614 			}
615 			splx(i);
616 			break;
617 
618 		case 'F':   /* set adapter foreground */
619 			if (tcp->num_param == 1) {
620 				tcp->attr_mask &= ~FG_CHANGED;
621 				tcp->cur_color.fg = tcp->std_color.fg
622 						  = tcp->param[0] & 0x0f;
623 				tcp->cur_attr = mask2attr(tcp);
624 			}
625 			break;
626 
627 		case 'G':   /* set adapter background */
628 			if (tcp->num_param == 1) {
629 				tcp->attr_mask &= ~BG_CHANGED;
630 				tcp->cur_color.bg = tcp->std_color.bg
631 						  = tcp->param[0] & 0x0f;
632 				tcp->cur_attr = mask2attr(tcp);
633 			}
634 			break;
635 
636 		case 'H':   /* set adapter reverse foreground */
637 			if (tcp->num_param == 1) {
638 				tcp->rev_color.fg = tcp->param[0] & 0x0f;
639 				tcp->cur_attr = mask2attr(tcp);
640 			}
641 			break;
642 
643 		case 'I':   /* set adapter reverse background */
644 			if (tcp->num_param == 1) {
645 				tcp->rev_color.bg = tcp->param[0] & 0x0f;
646 				tcp->cur_attr = mask2attr(tcp);
647 			}
648 			break;
649 
650 		case 'S':   /* set local/temporary cursor type & shape */
651 			i = spltty();
652 			n = tcp->num_param;
653 			v0 = tcp->param[0];
654 			switch (n) {
655 			case 0:
656 				v0 = 0;
657 				/* FALL THROUGH */
658 			case 1:
659 				if (v0 < sizeof(tcattrs)/sizeof(tcattrs[0]))
660 					sc_change_cursor_shape(scp,
661 					    tcattrs[v0], -1, -1);
662 				break;
663 			}
664 			splx(i);
665 			break;
666 		}
667 #if notyet
668 	} else if (tcp->esc == 4) {	/* seen ESC Q */
669 		/* to be filled */
670 #endif
671 	} else if (tcp->esc == 5) {	/* seen ESC ( */
672 		switch (c) {
673 		case 'B':   /* iso-2022: desginate ASCII into G0 */
674 			break;
675 		/* other items to be filled */
676 		default:
677 			break;
678 		}
679 	}
680 	tcp->esc = 0;
681 }
682 
683 static void
684 scterm_puts(scr_stat *scp, u_char *buf, int len)
685 {
686 	term_stat *tcp;
687 
688 	tcp = scp->ts;
689 outloop:
690 	scp->sc->write_in_progress++;
691 
692 	if (tcp->esc) {
693 		scterm_scan_esc(scp, tcp, *buf);
694 		buf++;
695 		len--;
696 	} else {
697 		switch (*buf) {
698 		case 0x1b:
699 			tcp->esc = 1;
700 			tcp->num_param = 0;
701 			buf++;
702 			len--;
703 			break;
704 		default:
705 			sc_term_gen_print(scp, &buf, &len, tcp->cur_attr);
706 			break;
707 		}
708 	}
709 
710 	sc_term_gen_scroll(scp, scp->sc->scr_map[0x20], tcp->cur_attr);
711 
712 	scp->sc->write_in_progress--;
713 	if (len)
714 		goto outloop;
715 }
716 
717 static int
718 scterm_ioctl(scr_stat *scp, struct tty *tp, u_long cmd, caddr_t data,
719 	     int flag, struct thread *td)
720 {
721 	term_stat *tcp = scp->ts;
722 	vid_info_t *vi;
723 
724 	switch (cmd) {
725 	case GIO_ATTR:      	/* get current attributes */
726 		/* FIXME: */
727 		*(int*)data = (tcp->cur_attr >> 8) & 0xff;
728 		return 0;
729 	case CONS_GETINFO:  	/* get current (virtual) console info */
730 		vi = (vid_info_t *)data;
731 		if (vi->size != sizeof(struct vid_info))
732 			return EINVAL;
733 		vi->mv_norm.fore = tcp->std_color.fg;
734 		vi->mv_norm.back = tcp->std_color.bg;
735 		vi->mv_rev.fore = tcp->rev_color.fg;
736 		vi->mv_rev.back = tcp->rev_color.bg;
737 		/*
738 		 * The other fields are filled by the upper routine. XXX
739 		 */
740 		return ENOIOCTL;
741 	}
742 	return ENOIOCTL;
743 }
744 
745 static int
746 scterm_reset(scr_stat *scp, int code)
747 {
748 	/* FIXME */
749 	return 0;
750 }
751 
752 static void
753 scterm_default_attr(scr_stat *scp, int color, int rev_color)
754 {
755 	term_stat *tcp = scp->ts;
756 
757 	tcp->dflt_std_color.fg = color & 0x0f;
758 	tcp->dflt_std_color.bg = (color >> 4) & 0x0f;
759 	tcp->dflt_rev_color.fg = rev_color & 0x0f;
760 	tcp->dflt_rev_color.bg = (rev_color >> 4) & 0x0f;
761 	tcp->std_color = tcp->dflt_std_color;
762 	tcp->rev_color = tcp->dflt_rev_color;
763 	tcp->cur_color = tcp->std_color;
764 	tcp->cur_attr = mask2attr(tcp);
765 }
766 
767 static void
768 scterm_clear(scr_stat *scp)
769 {
770 	term_stat *tcp = scp->ts;
771 
772 	sc_move_cursor(scp, 0, 0);
773 	sc_vtb_clear(&scp->vtb, scp->sc->scr_map[0x20], tcp->cur_attr);
774 	mark_all(scp);
775 }
776 
777 static void
778 scterm_notify(scr_stat *scp, int event)
779 {
780 	switch (event) {
781 	case SC_TE_NOTIFY_VTSWITCH_IN:
782 		break;
783 	case SC_TE_NOTIFY_VTSWITCH_OUT:
784 		break;
785 	}
786 }
787 
788 static int
789 scterm_input(scr_stat *scp, int c, struct tty *tp)
790 {
791 	return FALSE;
792 }
793 
794 /*
795  * Calculate hardware attributes word using logical attributes mask and
796  * hardware colors
797  */
798 
799 /* FIXME */
800 static int
801 mask2attr(term_stat *tcp)
802 {
803 	int attr, mask = tcp->attr_mask;
804 
805 	if (mask & REVERSE_ATTR) {
806 		attr = ((mask & FG_CHANGED) ?
807 			tcp->cur_color.bg : tcp->rev_color.fg) |
808 			(((mask & BG_CHANGED) ?
809 			tcp->cur_color.fg : tcp->rev_color.bg) << 4);
810 	} else
811 		attr = tcp->cur_color.fg | (tcp->cur_color.bg << 4);
812 
813 	/* XXX: underline mapping for Hercules adapter can be better */
814 	if (mask & (BOLD_ATTR | UNDERLINE_ATTR))
815 		attr ^= 0x08;
816 	if (mask & BLINK_ATTR)
817 		attr ^= 0x80;
818 
819 	return (attr << 8);
820 }
821 
822 #endif /* SC_DUMB_TERMINAL */
823