xref: /openbsd-src/sys/dev/wscons/wsemul_sun.c (revision 2b0358df1d88d06ef4139321dd05bd5e05d91eaf)
1 /* $OpenBSD: wsemul_sun.c,v 1.23 2007/11/27 16:37:27 miod Exp $ */
2 /* $NetBSD: wsemul_sun.c,v 1.11 2000/01/05 11:19:36 drochner Exp $ */
3 
4 /*
5  * Copyright (c) 1996, 1997 Christopher G. Demetriou.  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  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *      This product includes software developed by Christopher G. Demetriou
18  *	for the NetBSD Project.
19  * 4. The name of the author may not be used to endorse or promote products
20  *    derived from this software without specific prior written permission
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 /*
35  * This file implements a sun terminal personality for wscons.
36  *
37  * Derived from old rcons code.
38  * Color support from NetBSD's rcons color code, and wsemul_vt100.
39  */
40 
41 #ifndef	SMALL_KERNEL
42 #define	JUMP_SCROLL
43 #endif
44 
45 #include <sys/param.h>
46 #include <sys/systm.h>
47 #include <sys/time.h>
48 #include <sys/malloc.h>
49 #include <sys/fcntl.h>
50 
51 #include <dev/wscons/wsconsio.h>
52 #include <dev/wscons/wsdisplayvar.h>
53 #include <dev/wscons/wsemulvar.h>
54 #include <dev/wscons/wsksymdef.h>
55 #include <dev/wscons/ascii.h>
56 
57 void	*wsemul_sun_cnattach(const struct wsscreen_descr *, void *,
58     int, int, long);
59 void	*wsemul_sun_attach(int, const struct wsscreen_descr *,
60     void *, int, int, void *, long);
61 void	wsemul_sun_output(void *, const u_char *, u_int, int);
62 int	wsemul_sun_translate(void *, keysym_t, const char **);
63 void	wsemul_sun_detach(void *, u_int *, u_int *);
64 void	wsemul_sun_resetop(void *, enum wsemul_resetops);
65 
66 const struct wsemul_ops wsemul_sun_ops = {
67 	"sun",
68 	wsemul_sun_cnattach,
69 	wsemul_sun_attach,
70 	wsemul_sun_output,
71 	wsemul_sun_translate,
72 	wsemul_sun_detach,
73 	wsemul_sun_resetop
74 };
75 
76 #define	SUN_EMUL_STATE_NORMAL	0	/* normal processing */
77 #define	SUN_EMUL_STATE_HAVEESC	1	/* seen start of ctl seq */
78 #define	SUN_EMUL_STATE_CONTROL	2	/* processing ctl seq */
79 
80 #define	SUN_EMUL_NARGS	2		/* max # of args to a command */
81 
82 struct wsemul_sun_emuldata {
83 	const struct wsdisplay_emulops *emulops;
84 	void *emulcookie;
85 	void *cbcookie;
86 	int scrcapabilities;
87 	u_int nrows, ncols, crow, ccol;
88 	long defattr;			/* default attribute (rendition) */
89 
90 	u_int state;			/* processing state */
91 	u_int args[SUN_EMUL_NARGS];	/* command args, if CONTROL */
92 	int nargs;			/* number of args */
93 
94 	u_int scrolldist;		/* distance to scroll */
95 	long curattr, bkgdattr;		/* currently used attribute */
96 	long kernattr;			/* attribute for kernel output */
97 	int attrflags, fgcol, bgcol;	/* properties of curattr */
98 
99 #ifdef DIAGNOSTIC
100 	int console;
101 #endif
102 };
103 
104 void	wsemul_sun_init(struct wsemul_sun_emuldata *,
105 	    const struct wsscreen_descr *, void *, int, int, long);
106 void	wsemul_sun_jump_scroll(struct wsemul_sun_emuldata *, const u_char *,
107 	    u_int, int);
108 void	wsemul_sun_reset(struct wsemul_sun_emuldata *);
109 void	wsemul_sun_output_lowchars(struct wsemul_sun_emuldata *, u_char, int);
110 void	wsemul_sun_output_normal(struct wsemul_sun_emuldata *, u_char, int);
111 u_int	wsemul_sun_output_haveesc(struct wsemul_sun_emuldata *, u_char);
112 u_int	wsemul_sun_output_control(struct wsemul_sun_emuldata *, u_char);
113 void	wsemul_sun_control(struct wsemul_sun_emuldata *, u_char);
114 int	wsemul_sun_selectattribute(struct wsemul_sun_emuldata *, int, int, int,
115 	    long *, long *);
116 void	wsemul_sun_scrollup(struct wsemul_sun_emuldata *, u_int);
117 
118 struct wsemul_sun_emuldata wsemul_sun_console_emuldata;
119 
120 /* some useful utility macros */
121 #define	ARG(n,c) \
122 	((n) >= edp->nargs ? 0 : edp->args[(n) + MAX(0, edp->nargs - (c))])
123 #define	NORMALIZE(arg)		((arg) != 0 ? (arg) : 1)
124 #define	COLS_LEFT		(edp->ncols - 1 - edp->ccol)
125 #define	ROWS_LEFT		(edp->nrows - 1 - edp->crow)
126 
127 void
128 wsemul_sun_init(struct wsemul_sun_emuldata *edp,
129     const struct wsscreen_descr *type, void *cookie, int ccol, int crow,
130     long defattr)
131 {
132 	edp->emulops = type->textops;
133 	edp->emulcookie = cookie;
134 	edp->scrcapabilities = type->capabilities;
135 	edp->nrows = type->nrows;
136 	edp->ncols = type->ncols;
137 	edp->crow = crow;
138 	edp->ccol = ccol;
139 	edp->defattr = defattr;
140 }
141 
142 void
143 wsemul_sun_reset(struct wsemul_sun_emuldata *edp)
144 {
145 	edp->state = SUN_EMUL_STATE_NORMAL;
146 	edp->bkgdattr = edp->curattr = edp->defattr;
147 	edp->attrflags = 0;
148 	edp->fgcol = WSCOL_BLACK;
149 	edp->bgcol = WSCOL_WHITE;
150 	edp->scrolldist = 1;
151 }
152 
153 void *
154 wsemul_sun_cnattach(const struct wsscreen_descr *type, void *cookie, int ccol,
155     int crow, long defattr)
156 {
157 	struct wsemul_sun_emuldata *edp;
158 	int res;
159 
160 	edp = &wsemul_sun_console_emuldata;
161 	wsemul_sun_init(edp, type, cookie, ccol, crow, defattr);
162 
163 #ifndef WS_KERNEL_FG
164 #define WS_KERNEL_FG WSCOL_BLACK
165 #endif
166 #ifndef WS_KERNEL_BG
167 #define WS_KERNEL_BG WSCOL_WHITE
168 #endif
169 #ifndef WS_KERNEL_COLATTR
170 #define WS_KERNEL_COLATTR 0
171 #endif
172 #ifndef WS_KERNEL_MONOATTR
173 #define WS_KERNEL_MONOATTR 0
174 #endif
175 	if (type->capabilities & WSSCREEN_WSCOLORS)
176 		res = (*edp->emulops->alloc_attr)(cookie,
177 					    WS_KERNEL_FG, WS_KERNEL_BG,
178 					    WS_KERNEL_COLATTR | WSATTR_WSCOLORS,
179 					    &edp->kernattr);
180 	else
181 		res = (*edp->emulops->alloc_attr)(cookie, 0, 0,
182 					    WS_KERNEL_MONOATTR,
183 					    &edp->kernattr);
184 	if (res)
185 		edp->kernattr = defattr;
186 
187 	edp->cbcookie = NULL;
188 
189 #ifdef DIAGNOSTIC
190 	edp->console = 1;
191 #endif
192 
193 	wsemul_sun_reset(edp);
194 	return (edp);
195 }
196 
197 void *
198 wsemul_sun_attach(int console, const struct wsscreen_descr *type, void *cookie,
199     int ccol, int crow, void *cbcookie, long defattr)
200 {
201 	struct wsemul_sun_emuldata *edp;
202 
203 	if (console) {
204 		edp = &wsemul_sun_console_emuldata;
205 #ifdef DIAGNOSTIC
206 		KASSERT(edp->console == 1);
207 #endif
208 	} else {
209 		edp = malloc(sizeof *edp, M_DEVBUF, M_NOWAIT);
210 		if (edp == NULL)
211 			return (NULL);
212 		wsemul_sun_init(edp, type, cookie, ccol, crow, defattr);
213 
214 #ifdef DIAGNOSTIC
215 		edp->console = 0;
216 #endif
217 	}
218 
219 	edp->cbcookie = cbcookie;
220 
221 	wsemul_sun_reset(edp);
222 	return (edp);
223 }
224 
225 void
226 wsemul_sun_output_lowchars(struct wsemul_sun_emuldata *edp, u_char c,
227     int kernel)
228 {
229 	u_int n;
230 
231 	switch (c) {
232 	case ASCII_NUL:
233 	default:
234 		/* ignore */
235 		break;
236 
237 	case ASCII_BEL:		/* "Bell (BEL)" */
238 		wsdisplay_emulbell(edp->cbcookie);
239 		break;
240 
241 	case ASCII_BS:		/* "Backspace (BS)" */
242 		if (edp->ccol > 0)
243 			edp->ccol--;
244 		break;
245 
246 	case ASCII_CR:		/* "Return (CR)" */
247 		edp->ccol = 0;
248 		break;
249 
250 	case ASCII_HT:		/* "Tab (TAB)" */
251 		n = min(8 - (edp->ccol & 7), COLS_LEFT);
252 		if (n != 0) {
253 			(*edp->emulops->erasecols)(edp->emulcookie, edp->crow,
254 			    edp->ccol, n,
255 			    kernel ? edp->kernattr : edp->bkgdattr);
256 			edp->ccol += n;
257 		}
258 		break;
259 
260 	case ASCII_FF:		/* "Form Feed (FF)" */
261 		(*edp->emulops->eraserows)(edp->emulcookie, 0, edp->nrows,
262 		    edp->bkgdattr);
263 		edp->ccol = edp->crow = 0;
264 		break;
265 
266 	case ASCII_VT:		/* "Reverse Line Feed" */
267 		if (edp->crow > 0)
268 			edp->crow--;
269 		break;
270 
271 	case ASCII_ESC:		/* "Escape (ESC)" */
272 		if (kernel) {
273 			printf("wsemul_sun_output_lowchars: ESC in kernel "
274 			    "output ignored\n");
275 			break;	/* ignore the ESC */
276 		}
277 
278 		edp->state = SUN_EMUL_STATE_HAVEESC;
279 		break;
280 
281 	case ASCII_LF:		/* "Line Feed (LF)" */
282                 /* if the cur line isn't the last, incr and leave. */
283 		if (ROWS_LEFT > 0)
284 			edp->crow++;
285 		else
286 			wsemul_sun_scrollup(edp, edp->scrolldist);
287 		break;
288 	}
289 }
290 
291 void
292 wsemul_sun_output_normal(struct wsemul_sun_emuldata *edp, u_char c, int kernel)
293 {
294 
295 	(*edp->emulops->putchar)(edp->emulcookie, edp->crow, edp->ccol,
296 	    c, kernel ? edp->kernattr : edp->curattr);
297 
298 	if (++edp->ccol >= edp->ncols) {
299                 /* if the cur line isn't the last, incr and leave. */
300 		if (ROWS_LEFT > 0)
301 			edp->crow++;
302 		else
303 			wsemul_sun_scrollup(edp, edp->scrolldist);
304 		edp->ccol = 0;
305 	}
306 }
307 
308 u_int
309 wsemul_sun_output_haveesc(struct wsemul_sun_emuldata *edp, u_char c)
310 {
311 	u_int newstate;
312 
313 	switch (c) {
314 	case '[':		/* continuation of multi-char sequence */
315 		edp->nargs = 0;
316 		bzero(edp->args, sizeof (edp->args));
317 		newstate = SUN_EMUL_STATE_CONTROL;
318 		break;
319 
320 	default:
321 #ifdef DEBUG
322 		printf("ESC%c unknown\n", c);
323 #endif
324 		newstate = SUN_EMUL_STATE_NORMAL;	/* XXX is this wise? */
325 		break;
326 	}
327 
328 	return (newstate);
329 }
330 
331 void
332 wsemul_sun_control(struct wsemul_sun_emuldata *edp, u_char c)
333 {
334 	u_int n, src, dst;
335 	int flags, fgcol, bgcol;
336 	long attr, bkgdattr;
337 
338 	switch (c) {
339 	case '@':		/* "Insert Character (ICH)" */
340 		n = min(NORMALIZE(ARG(0,1)), COLS_LEFT + 1);
341 		src = edp->ccol;
342 		dst = edp->ccol + n;
343 		if (dst < edp->ncols) {
344 			(*edp->emulops->copycols)(edp->emulcookie, edp->crow,
345 			    src, dst, edp->ncols - dst);
346 		}
347 		(*edp->emulops->erasecols)(edp->emulcookie, edp->crow,
348 		    src, n, edp->bkgdattr);
349 		break;
350 
351 	case 'A':		/* "Cursor Up (CUU)" */
352 		edp->crow -= min(NORMALIZE(ARG(0,1)), edp->crow);
353 		break;
354 
355 	case 'E':		/* "Cursor Next Line (CNL)" */
356 		edp->ccol = 0;
357 		/* FALLTHROUGH */
358 	case 'B':		/* "Cursor Down (CUD)" */
359 		edp->crow += min(NORMALIZE(ARG(0,1)), ROWS_LEFT);
360 		break;
361 
362 	case 'C':		/* "Cursor Forward (CUF)" */
363 		edp->ccol += min(NORMALIZE(ARG(0,1)), COLS_LEFT);
364 		break;
365 
366 	case 'D':		/* "Cursor Backward (CUB)" */
367 		edp->ccol -= min(NORMALIZE(ARG(0,1)), edp->ccol);
368 		break;
369 
370 	case 'f':		/* "Horizontal And Vertical Position (HVP)" */
371 	case 'H':		/* "Cursor Position (CUP)" */
372 		edp->crow = min(NORMALIZE(ARG(0,2)), edp->nrows) - 1;
373 		edp->ccol = min(NORMALIZE(ARG(1,2)), edp->ncols) - 1;
374 		break;
375 
376 	case 'J':		/* "Erase in Display (ED)" */
377 		if (ROWS_LEFT > 0) {
378 			(*edp->emulops->eraserows)(edp->emulcookie,
379 			     edp->crow + 1, ROWS_LEFT, edp->bkgdattr);
380 		}
381 		/* FALLTHROUGH */
382 	case 'K':		/* "Erase in Line (EL)" */
383 		(*edp->emulops->erasecols)(edp->emulcookie, edp->crow,
384 		    edp->ccol, COLS_LEFT + 1, edp->bkgdattr);
385 		break;
386 
387 	case 'L':		/* "Insert Line (IL)" */
388 		n = min(NORMALIZE(ARG(0,1)), ROWS_LEFT + 1);
389 		src = edp->crow;
390 		dst = edp->crow + n;
391 		if (dst < edp->nrows) {
392 			(*edp->emulops->copyrows)(edp->emulcookie,
393 			    src, dst, edp->nrows - dst);
394 		}
395 		(*edp->emulops->eraserows)(edp->emulcookie,
396 		    src, n, edp->bkgdattr);
397 		break;
398 
399 	case 'M':		/* "Delete Line (DL)" */
400 		n = min(NORMALIZE(ARG(0,1)), ROWS_LEFT + 1);
401 		src = edp->crow + n;
402 		dst = edp->crow;
403 		if (src < edp->nrows) {
404 			(*edp->emulops->copyrows)(edp->emulcookie,
405 			    src, dst, edp->nrows - src);
406 		}
407 		(*edp->emulops->eraserows)(edp->emulcookie,
408 		    dst + edp->nrows - src, n, edp->bkgdattr);
409 		break;
410 
411 	case 'P':		/* "Delete Character (DCH)" */
412 		n = min(NORMALIZE(ARG(0,1)), COLS_LEFT + 1);
413 		src = edp->ccol + n;
414 		dst = edp->ccol;
415 		if (src < edp->ncols) {
416 			(*edp->emulops->copycols)(edp->emulcookie, edp->crow,
417 			    src, dst, edp->ncols - src);
418 		}
419 		(*edp->emulops->erasecols)(edp->emulcookie, edp->crow,
420 		    edp->ncols - n, n, edp->bkgdattr);
421 		break;
422 
423 	case 'm':		/* "Select Graphic Rendition (SGR)" */
424 		flags = edp->attrflags;
425 		fgcol = edp->fgcol;
426 		bgcol = edp->bgcol;
427 
428 		for (n = 0; n < edp->nargs; n++) {
429 			switch (ARG(n,edp->nargs)) {
430 			/* Clear all attributes || End underline */
431 			case 0:
432 				if (n == edp->nargs - 1) {
433 					edp->bkgdattr =
434 					    edp->curattr = edp->defattr;
435 					edp->attrflags = 0;
436 					edp->fgcol = WSCOL_BLACK;
437 					edp->bgcol = WSCOL_WHITE;
438 					return;
439 				}
440 				flags = 0;
441 				fgcol = WSCOL_BLACK;
442 				bgcol = WSCOL_WHITE;
443 				break;
444 			/* Begin bold */
445 			case 1:
446 				flags |= WSATTR_HILIT;
447 				break;
448 			/* Begin underline */
449 			case 4:
450 				flags |= WSATTR_UNDERLINE;
451 				break;
452 			/* Begin reverse */
453 			case 7:
454 				flags |= WSATTR_REVERSE;
455 				break;
456 			/* ANSI foreground color */
457 			case 30: case 31: case 32: case 33:
458 			case 34: case 35: case 36: case 37:
459 				fgcol = ARG(n,edp->nargs) - 30;
460 				break;
461 			/* ANSI background color */
462 			case 40: case 41: case 42: case 43:
463 			case 44: case 45: case 46: case 47:
464 				bgcol = ARG(n,edp->nargs) - 40;
465 				break;
466 			}
467 		}
468 setattr:
469 		if (wsemul_sun_selectattribute(edp, flags, fgcol, bgcol, &attr,
470 		    &bkgdattr)) {
471 #ifdef DEBUG
472 			printf("error allocating attr %d/%d/%x\n",
473 			    fgcol, bgcol, flags);
474 #endif
475 		} else {
476 			edp->curattr = attr;
477 			edp->bkgdattr = bkgdattr;
478 			edp->attrflags = flags;
479 			edp->fgcol = fgcol;
480 			edp->bgcol = bgcol;
481 		}
482 		break;
483 
484 	case 'p':		/* "Black On White (SUNBOW)" */
485 		flags = 0;
486 		fgcol = WSCOL_BLACK;
487 		bgcol = WSCOL_WHITE;
488 		goto setattr;
489 
490 	case 'q':		/* "White On Black (SUNWOB)" */
491 		flags = 0;
492 		fgcol = WSCOL_WHITE;
493 		bgcol = WSCOL_BLACK;
494 		goto setattr;
495 
496 	case 'r':		/* "Set Scrolling (SUNSCRL)" */
497 		edp->scrolldist = min(ARG(0,1), edp->nrows);
498 		break;
499 
500 	case 's':		/* "Reset Terminal Emulator (SUNRESET)" */
501 		wsemul_sun_reset(edp);
502 		break;
503 	}
504 }
505 
506 u_int
507 wsemul_sun_output_control(struct wsemul_sun_emuldata *edp, u_char c)
508 {
509 	u_int newstate = SUN_EMUL_STATE_CONTROL;
510 
511 	switch (c) {
512 	case '0': case '1': case '2': case '3': case '4': /* argument digit */
513 	case '5': case '6': case '7': case '8': case '9':
514 		/*
515 		 * If we receive more arguments than we are expecting,
516 		 * discard the earliest arguments.
517 		 */
518 		if (edp->nargs > SUN_EMUL_NARGS - 1) {
519 			bcopy(edp->args + 1, edp->args,
520 			    (SUN_EMUL_NARGS - 1) * sizeof(edp->args[0]));
521 			edp->args[edp->nargs = SUN_EMUL_NARGS - 1] = 0;
522 		}
523 		edp->args[edp->nargs] = (edp->args[edp->nargs] * 10) +
524 		    (c - '0');
525                 break;
526 
527 	case ';':		/* argument terminator */
528 		edp->nargs++;
529 		break;
530 
531 	default:		/* end of escape sequence */
532 		edp->nargs++;
533 		if (edp->nargs > SUN_EMUL_NARGS)
534 			edp->nargs = SUN_EMUL_NARGS;
535 		wsemul_sun_control(edp, c);
536 		newstate = SUN_EMUL_STATE_NORMAL;
537 		break;
538 	}
539 	return (newstate);
540 }
541 
542 void
543 wsemul_sun_output(void *cookie, const u_char *data, u_int count, int kernel)
544 {
545 	struct wsemul_sun_emuldata *edp = cookie;
546 	u_int newstate;
547 
548 #ifdef DIAGNOSTIC
549 	if (kernel && !edp->console)
550 		panic("wsemul_sun_output: kernel output, not console");
551 #endif
552 
553 	/* XXX */
554 	(*edp->emulops->cursor)(edp->emulcookie, 0, edp->crow, edp->ccol);
555 
556 	for (; count > 0; data++, count--) {
557 #ifdef JUMP_SCROLL
558 		/*
559 		 * If scrolling is not disabled and we are the bottom of
560 		 * the screen, count newlines until an escape sequence
561 		 * appears.
562 		 */
563 		if ((edp->state == SUN_EMUL_STATE_NORMAL || kernel) &&
564 		    ROWS_LEFT == 0 && edp->scrolldist != 0)
565 			wsemul_sun_jump_scroll(edp, data, count, kernel);
566 #endif
567 
568 		if (*data < ' ') {
569 			wsemul_sun_output_lowchars(edp, *data, kernel);
570 			continue;
571 		}
572 
573 		if (kernel) {
574 			wsemul_sun_output_normal(edp, *data, 1);
575 			continue;
576 		}
577 
578 		switch (newstate = edp->state) {
579 		case SUN_EMUL_STATE_NORMAL:
580 			wsemul_sun_output_normal(edp, *data, 0);
581 			break;
582 		case SUN_EMUL_STATE_HAVEESC:
583 			newstate = wsemul_sun_output_haveesc(edp, *data);
584 			break;
585 		case SUN_EMUL_STATE_CONTROL:
586 			newstate = wsemul_sun_output_control(edp, *data);
587 			break;
588 		default:
589 #ifdef DIAGNOSTIC
590 			panic("wsemul_sun: invalid state %d", edp->state);
591 #else
592                         /* try to recover, if things get screwed up... */
593 			newstate = SUN_EMUL_STATE_NORMAL;
594 			wsemul_sun_output_normal(edp, *data, 0);
595 #endif
596                         break;
597 		}
598 		edp->state = newstate;
599 	}
600 	/* XXX */
601 	(*edp->emulops->cursor)(edp->emulcookie, 1, edp->crow, edp->ccol);
602 }
603 
604 #ifdef JUMP_SCROLL
605 void
606 wsemul_sun_jump_scroll(struct wsemul_sun_emuldata *edp, const u_char *data,
607     u_int count, int kernel)
608 {
609 	u_char curchar;
610 	u_int pos, lines;
611 
612 	lines = 0;
613 	pos = edp->ccol;
614 	for (; count != 0; data++, count--) {
615 		curchar = *data;
616 		if (curchar == ASCII_FF ||
617 		    curchar == ASCII_VT || curchar == ASCII_ESC)
618 			break;
619 
620 		switch (curchar) {
621 		case ASCII_BS:
622 			if (pos > 0)
623 				pos--;
624 			break;
625 		case ASCII_CR:
626 			pos = 0;
627 			break;
628 		case ASCII_HT:
629 			pos = (pos + 7) & ~7;
630 			if (pos >= edp->ncols)
631 				pos = edp->ncols - 1;
632 			break;
633 		default:
634 			if (++pos >= edp->ncols) {
635 				pos = 0;
636 				curchar = ASCII_LF;
637 			}
638 			break;
639 		}
640 		if (curchar == ASCII_LF) {
641 			if (++lines >= edp->nrows - 1)
642 				break;
643 		}
644 	}
645 
646 	if (lines > 1) {
647 		wsemul_sun_scrollup(edp, lines);
648 		edp->crow--;
649 	}
650 }
651 #endif
652 
653 /*
654  * Get an attribute from the graphics driver.
655  * Try to find replacements if the desired appearance is not supported.
656  */
657 int
658 wsemul_sun_selectattribute(struct wsemul_sun_emuldata *edp, int flags,
659     int fgcol, int bgcol, long *attr, long *bkgdattr)
660 {
661 	int error;
662 
663 	/*
664 	 * Rasops will force white on black as normal output colors, unless
665 	 * WSATTR_WSCOLORS is specified. Since Sun console is black on white,
666 	 * always use WSATTR_WSCOLORS and our colors, as we know better.
667 	 */
668 	if (!(edp->scrcapabilities & WSSCREEN_WSCOLORS)) {
669 		flags &= ~WSATTR_WSCOLORS;
670 	} else {
671 		flags |= WSATTR_WSCOLORS;
672 	}
673 
674 	error = (*edp->emulops->alloc_attr)(edp->emulcookie, fgcol, bgcol,
675 					    flags & WSATTR_WSCOLORS, bkgdattr);
676 	if (error)
677 		return (error);
678 
679 	if ((flags & WSATTR_HILIT) &&
680 	    !(edp->scrcapabilities & WSSCREEN_HILIT)) {
681 		flags &= ~WSATTR_HILIT;
682 		if (edp->scrcapabilities & WSSCREEN_WSCOLORS) {
683 			fgcol = WSCOL_RED;
684 			flags |= WSATTR_WSCOLORS;
685 		}
686 	}
687 	if ((flags & WSATTR_UNDERLINE) &&
688 	    !(edp->scrcapabilities & WSSCREEN_UNDERLINE)) {
689 		flags &= ~WSATTR_UNDERLINE;
690 		if (edp->scrcapabilities & WSSCREEN_WSCOLORS) {
691 			fgcol = WSCOL_CYAN;
692 			flags &= ~WSATTR_UNDERLINE;
693 			flags |= WSATTR_WSCOLORS;
694 		}
695 	}
696 	if ((flags & WSATTR_BLINK) &&
697 	    !(edp->scrcapabilities & WSSCREEN_BLINK)) {
698 		flags &= ~WSATTR_BLINK;
699 	}
700 	if ((flags & WSATTR_REVERSE) &&
701 	    !(edp->scrcapabilities & WSSCREEN_REVERSE)) {
702 		flags &= ~WSATTR_REVERSE;
703 		if (edp->scrcapabilities & WSSCREEN_WSCOLORS) {
704 			int help;
705 			help = bgcol;
706 			bgcol = fgcol;
707 			fgcol = help;
708 			flags |= WSATTR_WSCOLORS;
709 		}
710 	}
711 	error = (*edp->emulops->alloc_attr)(edp->emulcookie, fgcol, bgcol,
712 					    flags, attr);
713 	if (error)
714 		return (error);
715 
716 	return (0);
717 }
718 
719 static const char *sun_fkeys[] = {
720 	"\033[224z",	/* F1 */
721 	"\033[225z",
722 	"\033[226z",
723 	"\033[227z",
724 	"\033[228z",
725 	"\033[229z",
726 	"\033[230z",
727 	"\033[231z",
728 	"\033[232z",
729 	"\033[233z",
730 	"\033[234z",
731 	"\033[235z",	/* F12 */
732 };
733 
734 static const char *sun_lkeys[] = {
735 	"\033[207z",	/* KS_Help */
736 	NULL,		/* KS_Execute */
737 	"\033[200z",	/* KS_Find */
738 	NULL,		/* KS_Select */
739 	"\033[193z",	/* KS_Again */
740 	"\033[194z",	/* KS_Props */
741 	"\033[195z",	/* KS_Undo */
742 	"\033[196z",	/* KS_Front */
743 	"\033[197z",	/* KS_Copy */
744 	"\033[198z",	/* KS_Open */
745 	"\033[199z",	/* KS_Paste */
746 	"\033[201z",	/* KS_Cut */
747 };
748 
749 int
750 wsemul_sun_translate(void *cookie, keysym_t in, const char **out)
751 {
752 	static char c;
753 
754 	if (KS_GROUP(in) == KS_GROUP_Keypad && (in & 0x80) == 0) {
755 		c = in & 0xff; /* turn into ASCII */
756 		*out = &c;
757 		return (1);
758 	}
759 
760 	if (in >= KS_f1 && in <= KS_f12) {
761 		*out = sun_fkeys[in - KS_f1];
762 		return (6);
763 	}
764 	if (in >= KS_F1 && in <= KS_F12) {
765 		*out = sun_fkeys[in - KS_F1];
766 		return (6);
767 	}
768 	if (in >= KS_KP_F1 && in <= KS_KP_F4) {
769 		*out = sun_fkeys[in - KS_KP_F1];
770 		return (6);
771 	}
772 	if (in >= KS_Help && in <= KS_Cut && sun_lkeys[in - KS_Help] != NULL) {
773 		*out = sun_lkeys[in - KS_Help];
774 		return (6);
775 	}
776 
777 	switch (in) {
778 	case KS_Home:
779 	case KS_KP_Home:
780 	case KS_KP_Begin:
781 		*out = "\033[214z";
782 		return (6);
783 	case KS_End:
784 	case KS_KP_End:
785 		*out = "\033[220z";
786 		return (6);
787 	case KS_Insert:
788 	case KS_KP_Insert:
789 		*out = "\033[247z";
790 		return (6);
791 	case KS_Prior:
792 	case KS_KP_Prior:
793 		*out = "\033[216z";
794 		return (6);
795 	case KS_Next:
796 	case KS_KP_Next:
797 		*out = "\033[222z";
798 		return (6);
799 	case KS_Up:
800 	case KS_KP_Up:
801 		*out = "\033[A";
802 		return (3);
803 	case KS_Down:
804 	case KS_KP_Down:
805 		*out = "\033[B";
806 		return (3);
807 	case KS_Left:
808 	case KS_KP_Left:
809 		*out = "\033[D";
810 		return (3);
811 	case KS_Right:
812 	case KS_KP_Right:
813 		*out = "\033[C";
814 		return (3);
815 	case KS_KP_Delete:
816 		*out = "\177";
817 		return (1);
818 	}
819 	return (0);
820 }
821 
822 void
823 wsemul_sun_detach(void *cookie, u_int *crowp, u_int *ccolp)
824 {
825 	struct wsemul_sun_emuldata *edp = cookie;
826 
827 	*crowp = edp->crow;
828 	*ccolp = edp->ccol;
829 	if (edp != &wsemul_sun_console_emuldata)
830 		free(edp, M_DEVBUF);
831 }
832 
833 void
834 wsemul_sun_resetop(void *cookie, enum wsemul_resetops op)
835 {
836 	struct wsemul_sun_emuldata *edp = cookie;
837 
838 	switch (op) {
839 	case WSEMUL_RESET:
840 		wsemul_sun_reset(edp);
841 		break;
842 	case WSEMUL_CLEARSCREEN:
843 		(*edp->emulops->eraserows)(edp->emulcookie, 0, edp->nrows,
844 		    edp->bkgdattr);
845 		edp->ccol = edp->crow = 0;
846 		(*edp->emulops->cursor)(edp->emulcookie, 1, 0, 0);
847 		break;
848 	case WSEMUL_CLEARCURSOR:
849 		(*edp->emulops->cursor)(edp->emulcookie, 0,
850 		    edp->crow, edp->ccol);
851 		break;
852 	default:
853 		break;
854 	}
855 }
856 
857 void
858 wsemul_sun_scrollup(struct wsemul_sun_emuldata *edp, u_int lines)
859 {
860 	/*
861 	 * if we're in wrap-around mode, go to the first
862 	 * line and clear it.
863 	 */
864 	if (lines == 0) {
865 		edp->crow = 0;
866 		(*edp->emulops->eraserows)(edp->emulcookie, 0, 1,
867 		    edp->bkgdattr);
868 		return;
869 	}
870 
871 	/*
872 	 * If the scrolling distance is equal to the screen height
873 	 * (usually 34), clear the screen; otherwise, scroll by the
874 	 * scrolling distance.
875 	 */
876 	if (lines < edp->nrows)
877 		(*edp->emulops->copyrows)(edp->emulcookie, lines, 0,
878 		    edp->nrows - lines);
879 	(*edp->emulops->eraserows)(edp->emulcookie,
880 	    edp->nrows - lines, lines, edp->bkgdattr);
881 	edp->crow -= lines - 1;
882 }
883