xref: /openbsd/sys/dev/wscons/wsemul_sun.c (revision 404b540a)
1 /* $OpenBSD: wsemul_sun.c,v 1.28 2009/09/05 16:51:19 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 u_int	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 	struct wsemul_abortstate abortstate;
85 	void *emulcookie;
86 	void *cbcookie;
87 	int scrcapabilities;
88 	u_int nrows, ncols, crow, ccol;
89 	long defattr;			/* default attribute (rendition) */
90 
91 	u_int state;			/* processing state */
92 	u_int args[SUN_EMUL_NARGS];	/* command args, if CONTROL */
93 	int nargs;			/* number of args */
94 
95 	u_int scrolldist;		/* distance to scroll */
96 	long curattr, bkgdattr;		/* currently used attribute */
97 	long kernattr;			/* attribute for kernel output */
98 	int attrflags, fgcol, bgcol;	/* properties of curattr */
99 
100 #ifdef DIAGNOSTIC
101 	int console;
102 #endif
103 };
104 
105 void	wsemul_sun_init(struct wsemul_sun_emuldata *,
106 	    const struct wsscreen_descr *, void *, int, int, long);
107 int	wsemul_sun_jump_scroll(struct wsemul_sun_emuldata *, const u_char *,
108 	    u_int, int);
109 void	wsemul_sun_reset(struct wsemul_sun_emuldata *);
110 int	wsemul_sun_output_lowchars(struct wsemul_sun_emuldata *, u_char, int);
111 int	wsemul_sun_output_normal(struct wsemul_sun_emuldata *, u_char, int);
112 void	wsemul_sun_output_haveesc(struct wsemul_sun_emuldata *, u_char);
113 int	wsemul_sun_output_control(struct wsemul_sun_emuldata *, u_char);
114 int	wsemul_sun_control(struct wsemul_sun_emuldata *, u_char);
115 int	wsemul_sun_selectattribute(struct wsemul_sun_emuldata *, int, int, int,
116 	    long *, long *);
117 int	wsemul_sun_scrollup(struct wsemul_sun_emuldata *, u_int);
118 
119 struct wsemul_sun_emuldata wsemul_sun_console_emuldata;
120 
121 /* some useful utility macros */
122 #define	ARG(n,c) \
123 	((n) >= edp->nargs ? 0 : edp->args[(n) + MAX(0, edp->nargs - (c))])
124 #define	NORMALIZE(arg)		((arg) != 0 ? (arg) : 1)
125 #define	COLS_LEFT		(edp->ncols - 1 - edp->ccol)
126 #define	ROWS_LEFT		(edp->nrows - 1 - edp->crow)
127 
128 void
129 wsemul_sun_init(struct wsemul_sun_emuldata *edp,
130     const struct wsscreen_descr *type, void *cookie, int ccol, int crow,
131     long defattr)
132 {
133 	edp->emulops = type->textops;
134 	edp->emulcookie = cookie;
135 	edp->scrcapabilities = type->capabilities;
136 	edp->nrows = type->nrows;
137 	edp->ncols = type->ncols;
138 	edp->crow = crow;
139 	edp->ccol = ccol;
140 	edp->defattr = defattr;
141 	wsemul_reset_abortstate(&edp->abortstate);
142 }
143 
144 void
145 wsemul_sun_reset(struct wsemul_sun_emuldata *edp)
146 {
147 	edp->state = SUN_EMUL_STATE_NORMAL;
148 	edp->bkgdattr = edp->curattr = edp->defattr;
149 	edp->attrflags = 0;
150 	edp->fgcol = WSCOL_BLACK;
151 	edp->bgcol = WSCOL_WHITE;
152 	edp->scrolldist = 1;
153 }
154 
155 void *
156 wsemul_sun_cnattach(const struct wsscreen_descr *type, void *cookie, int ccol,
157     int crow, long defattr)
158 {
159 	struct wsemul_sun_emuldata *edp;
160 	int res;
161 
162 	edp = &wsemul_sun_console_emuldata;
163 	wsemul_sun_init(edp, type, cookie, ccol, crow, defattr);
164 
165 #ifndef WS_KERNEL_FG
166 #define WS_KERNEL_FG WSCOL_BLACK
167 #endif
168 #ifndef WS_KERNEL_BG
169 #define WS_KERNEL_BG WSCOL_WHITE
170 #endif
171 #ifndef WS_KERNEL_COLATTR
172 #define WS_KERNEL_COLATTR 0
173 #endif
174 #ifndef WS_KERNEL_MONOATTR
175 #define WS_KERNEL_MONOATTR 0
176 #endif
177 	if (type->capabilities & WSSCREEN_WSCOLORS)
178 		res = (*edp->emulops->alloc_attr)(cookie,
179 					    WS_KERNEL_FG, WS_KERNEL_BG,
180 					    WS_KERNEL_COLATTR | WSATTR_WSCOLORS,
181 					    &edp->kernattr);
182 	else
183 		res = (*edp->emulops->alloc_attr)(cookie, 0, 0,
184 					    WS_KERNEL_MONOATTR,
185 					    &edp->kernattr);
186 	if (res)
187 		edp->kernattr = defattr;
188 
189 	edp->cbcookie = NULL;
190 
191 #ifdef DIAGNOSTIC
192 	edp->console = 1;
193 #endif
194 
195 	wsemul_sun_reset(edp);
196 	return (edp);
197 }
198 
199 void *
200 wsemul_sun_attach(int console, const struct wsscreen_descr *type, void *cookie,
201     int ccol, int crow, void *cbcookie, long defattr)
202 {
203 	struct wsemul_sun_emuldata *edp;
204 
205 	if (console) {
206 		edp = &wsemul_sun_console_emuldata;
207 #ifdef DIAGNOSTIC
208 		KASSERT(edp->console == 1);
209 #endif
210 	} else {
211 		edp = malloc(sizeof *edp, M_DEVBUF, M_NOWAIT);
212 		if (edp == NULL)
213 			return (NULL);
214 		wsemul_sun_init(edp, type, cookie, ccol, crow, defattr);
215 
216 #ifdef DIAGNOSTIC
217 		edp->console = 0;
218 #endif
219 	}
220 
221 	edp->cbcookie = cbcookie;
222 
223 	wsemul_sun_reset(edp);
224 	return (edp);
225 }
226 
227 int
228 wsemul_sun_output_lowchars(struct wsemul_sun_emuldata *edp, u_char c,
229     int kernel)
230 {
231 	u_int n;
232 	int rc = 0;
233 
234 	switch (c) {
235 	case ASCII_NUL:
236 	default:
237 		/* ignore */
238 		break;
239 
240 	case ASCII_BEL:		/* "Bell (BEL)" */
241 		wsdisplay_emulbell(edp->cbcookie);
242 		break;
243 
244 	case ASCII_BS:		/* "Backspace (BS)" */
245 		if (edp->ccol > 0)
246 			edp->ccol--;
247 		break;
248 
249 	case ASCII_CR:		/* "Return (CR)" */
250 		edp->ccol = 0;
251 		break;
252 
253 	case ASCII_HT:		/* "Tab (TAB)" */
254 		n = min(8 - (edp->ccol & 7), COLS_LEFT);
255 		if (n != 0) {
256 			WSEMULOP(rc, edp, &edp->abortstate, erasecols,
257 			    (edp->emulcookie, edp->crow, edp->ccol, n,
258 			     kernel ? edp->kernattr : edp->bkgdattr));
259 			if (rc != 0)
260 				break;
261 			edp->ccol += n;
262 		}
263 		break;
264 
265 	case ASCII_FF:		/* "Form Feed (FF)" */
266 		WSEMULOP(rc, edp, &edp->abortstate, eraserows,
267 		    (edp->emulcookie, 0, edp->nrows, edp->bkgdattr));
268 		if (rc != 0)
269 			break;
270 		edp->ccol = edp->crow = 0;
271 		break;
272 
273 	case ASCII_VT:		/* "Reverse Line Feed" */
274 		if (edp->crow > 0)
275 			edp->crow--;
276 		break;
277 
278 	case ASCII_ESC:		/* "Escape (ESC)" */
279 		if (kernel) {
280 			printf("wsemul_sun_output_lowchars: ESC in kernel "
281 			    "output ignored\n");
282 			break;	/* ignore the ESC */
283 		}
284 
285 		edp->state = SUN_EMUL_STATE_HAVEESC;
286 		break;
287 
288 	case ASCII_LF:		/* "Line Feed (LF)" */
289 		/* if the cur line isn't the last, incr and leave. */
290 		if (ROWS_LEFT > 0)
291 			edp->crow++;
292 		else {
293 			rc = wsemul_sun_scrollup(edp, edp->scrolldist);
294 			if (rc != 0)
295 				break;
296 		}
297 		break;
298 	}
299 
300 	return rc;
301 }
302 
303 int
304 wsemul_sun_output_normal(struct wsemul_sun_emuldata *edp, u_char c, int kernel)
305 {
306 	int rc;
307 
308 	WSEMULOP(rc, edp, &edp->abortstate, putchar,
309 	    (edp->emulcookie, edp->crow, edp->ccol,
310 	     c, kernel ? edp->kernattr : edp->curattr));
311 	if (rc != 0)
312 		return rc;
313 
314 	if (++edp->ccol >= edp->ncols) {
315 		/* if the cur line isn't the last, incr and leave. */
316 		if (ROWS_LEFT > 0)
317 			edp->crow++;
318 		else {
319 			rc = wsemul_sun_scrollup(edp, edp->scrolldist);
320 			if (rc != 0) {
321 				/* undo line wrap */
322 				edp->ccol--;
323 
324 				return rc;
325 			}
326 		}
327 		edp->ccol = 0;
328 	}
329 
330 	return 0;
331 }
332 
333 void
334 wsemul_sun_output_haveesc(struct wsemul_sun_emuldata *edp, u_char c)
335 {
336 	switch (c) {
337 	case '[':		/* continuation of multi-char sequence */
338 		edp->nargs = 0;
339 		bzero(edp->args, sizeof (edp->args));
340 		edp->state = SUN_EMUL_STATE_CONTROL;
341 		break;
342 
343 	default:
344 #ifdef DEBUG
345 		printf("ESC%c unknown\n", c);
346 #endif
347 		edp->state = SUN_EMUL_STATE_NORMAL;	/* XXX is this wise? */
348 		break;
349 	}
350 }
351 
352 int
353 wsemul_sun_control(struct wsemul_sun_emuldata *edp, u_char c)
354 {
355 	u_int n, src, dst;
356 	int flags, fgcol, bgcol;
357 	long attr, bkgdattr;
358 	int rc = 0;
359 
360 	switch (c) {
361 	case '@':		/* "Insert Character (ICH)" */
362 		n = min(NORMALIZE(ARG(0,1)), COLS_LEFT + 1);
363 		src = edp->ccol;
364 		dst = edp->ccol + n;
365 		if (dst < edp->ncols) {
366 			WSEMULOP(rc, edp, &edp->abortstate, copycols,
367 			    (edp->emulcookie, edp->crow, src, dst,
368 			     edp->ncols - dst));
369 			if (rc != 0)
370 				break;
371 		}
372 		WSEMULOP(rc, edp, &edp->abortstate, erasecols,
373 		    (edp->emulcookie, edp->crow, src, n, edp->bkgdattr));
374 		break;
375 
376 	case 'A':		/* "Cursor Up (CUU)" */
377 		edp->crow -= min(NORMALIZE(ARG(0,1)), edp->crow);
378 		break;
379 
380 	case 'E':		/* "Cursor Next Line (CNL)" */
381 		edp->ccol = 0;
382 		/* FALLTHROUGH */
383 	case 'B':		/* "Cursor Down (CUD)" */
384 		edp->crow += min(NORMALIZE(ARG(0,1)), ROWS_LEFT);
385 		break;
386 
387 	case 'C':		/* "Cursor Forward (CUF)" */
388 		edp->ccol += min(NORMALIZE(ARG(0,1)), COLS_LEFT);
389 		break;
390 
391 	case 'D':		/* "Cursor Backward (CUB)" */
392 		edp->ccol -= min(NORMALIZE(ARG(0,1)), edp->ccol);
393 		break;
394 
395 	case 'f':		/* "Horizontal And Vertical Position (HVP)" */
396 	case 'H':		/* "Cursor Position (CUP)" */
397 		edp->crow = min(NORMALIZE(ARG(0,2)), edp->nrows) - 1;
398 		edp->ccol = min(NORMALIZE(ARG(1,2)), edp->ncols) - 1;
399 		break;
400 
401 	case 'J':		/* "Erase in Display (ED)" */
402 		if (ROWS_LEFT > 0) {
403 			WSEMULOP(rc, edp, &edp->abortstate, eraserows,
404 			    (edp->emulcookie, edp->crow + 1, ROWS_LEFT,
405 			     edp->bkgdattr));
406 			if (rc != 0)
407 				break;
408 		}
409 		/* FALLTHROUGH */
410 	case 'K':		/* "Erase in Line (EL)" */
411 		WSEMULOP(rc, edp, &edp->abortstate, erasecols,
412 		    (edp->emulcookie, edp->crow, edp->ccol, COLS_LEFT + 1,
413 		     edp->bkgdattr));
414 		break;
415 
416 	case 'L':		/* "Insert Line (IL)" */
417 		n = min(NORMALIZE(ARG(0,1)), ROWS_LEFT + 1);
418 		src = edp->crow;
419 		dst = edp->crow + n;
420 		if (dst < edp->nrows) {
421 			WSEMULOP(rc, edp, &edp->abortstate, copyrows,
422 			    (edp->emulcookie, src, dst, edp->nrows - dst));
423 			if (rc != 0)
424 				break;
425 		}
426 		WSEMULOP(rc, edp, &edp->abortstate, eraserows,
427 		    (edp->emulcookie, src, n, edp->bkgdattr));
428 		break;
429 
430 	case 'M':		/* "Delete Line (DL)" */
431 		n = min(NORMALIZE(ARG(0,1)), ROWS_LEFT + 1);
432 		src = edp->crow + n;
433 		dst = edp->crow;
434 		if (src < edp->nrows) {
435 			WSEMULOP(rc, edp, &edp->abortstate, copyrows,
436 			    (edp->emulcookie, src, dst, edp->nrows - src));
437 			if (rc != 0)
438 				break;
439 		}
440 		WSEMULOP(rc, edp, &edp->abortstate, eraserows,
441 		    (edp->emulcookie, dst + edp->nrows - src, n,
442 		     edp->bkgdattr));
443 		break;
444 
445 	case 'P':		/* "Delete Character (DCH)" */
446 		n = min(NORMALIZE(ARG(0,1)), COLS_LEFT + 1);
447 		src = edp->ccol + n;
448 		dst = edp->ccol;
449 		if (src < edp->ncols) {
450 			WSEMULOP(rc, edp, &edp->abortstate, copycols,
451 			    (edp->emulcookie, edp->crow, src, dst,
452 			     edp->ncols - src));
453 			if (rc != 0)
454 				break;
455 		}
456 		WSEMULOP(rc, edp, &edp->abortstate, erasecols,
457 		    (edp->emulcookie, edp->crow, edp->ncols - n, n,
458 		     edp->bkgdattr));
459 		break;
460 
461 	case 'm':		/* "Select Graphic Rendition (SGR)" */
462 		flags = edp->attrflags;
463 		fgcol = edp->fgcol;
464 		bgcol = edp->bgcol;
465 
466 		for (n = 0; n < edp->nargs; n++) {
467 			switch (ARG(n,edp->nargs)) {
468 			/* Clear all attributes || End underline */
469 			case 0:
470 				if (n == edp->nargs - 1) {
471 					edp->bkgdattr =
472 					    edp->curattr = edp->defattr;
473 					edp->attrflags = 0;
474 					edp->fgcol = WSCOL_BLACK;
475 					edp->bgcol = WSCOL_WHITE;
476 					return 0;
477 				}
478 				flags = 0;
479 				fgcol = WSCOL_BLACK;
480 				bgcol = WSCOL_WHITE;
481 				break;
482 			/* Begin bold */
483 			case 1:
484 				flags |= WSATTR_HILIT;
485 				break;
486 			/* Begin underline */
487 			case 4:
488 				flags |= WSATTR_UNDERLINE;
489 				break;
490 			/* Begin reverse */
491 			case 7:
492 				flags |= WSATTR_REVERSE;
493 				break;
494 			/* ANSI foreground color */
495 			case 30: case 31: case 32: case 33:
496 			case 34: case 35: case 36: case 37:
497 				fgcol = ARG(n,edp->nargs) - 30;
498 				break;
499 			/* ANSI background color */
500 			case 40: case 41: case 42: case 43:
501 			case 44: case 45: case 46: case 47:
502 				bgcol = ARG(n,edp->nargs) - 40;
503 				break;
504 			}
505 		}
506 setattr:
507 		if (wsemul_sun_selectattribute(edp, flags, fgcol, bgcol, &attr,
508 		    &bkgdattr)) {
509 #ifdef DEBUG
510 			printf("error allocating attr %d/%d/%x\n",
511 			    fgcol, bgcol, flags);
512 #endif
513 		} else {
514 			edp->curattr = attr;
515 			edp->bkgdattr = bkgdattr;
516 			edp->attrflags = flags;
517 			edp->fgcol = fgcol;
518 			edp->bgcol = bgcol;
519 		}
520 		break;
521 
522 	case 'p':		/* "Black On White (SUNBOW)" */
523 		flags = 0;
524 		fgcol = WSCOL_BLACK;
525 		bgcol = WSCOL_WHITE;
526 		goto setattr;
527 
528 	case 'q':		/* "White On Black (SUNWOB)" */
529 		flags = 0;
530 		fgcol = WSCOL_WHITE;
531 		bgcol = WSCOL_BLACK;
532 		goto setattr;
533 
534 	case 'r':		/* "Set Scrolling (SUNSCRL)" */
535 		edp->scrolldist = min(ARG(0,1), edp->nrows);
536 		break;
537 
538 	case 's':		/* "Reset Terminal Emulator (SUNRESET)" */
539 		wsemul_sun_reset(edp);
540 		break;
541 	}
542 
543 	return rc;
544 }
545 
546 int
547 wsemul_sun_output_control(struct wsemul_sun_emuldata *edp, u_char c)
548 {
549 	int oargs;
550 	int rc;
551 
552 	switch (c) {
553 	case '0': case '1': case '2': case '3': case '4': /* argument digit */
554 	case '5': case '6': case '7': case '8': case '9':
555 		/*
556 		 * If we receive more arguments than we are expecting,
557 		 * discard the earliest arguments.
558 		 */
559 		if (edp->nargs > SUN_EMUL_NARGS - 1) {
560 			bcopy(edp->args + 1, edp->args,
561 			    (SUN_EMUL_NARGS - 1) * sizeof(edp->args[0]));
562 			edp->args[edp->nargs = SUN_EMUL_NARGS - 1] = 0;
563 		}
564 		edp->args[edp->nargs] = (edp->args[edp->nargs] * 10) +
565 		    (c - '0');
566 		break;
567 
568 	case ';':		/* argument terminator */
569 		edp->nargs++;
570 		break;
571 
572 	default:		/* end of escape sequence */
573 		oargs = edp->nargs++;
574 		if (edp->nargs > SUN_EMUL_NARGS)
575 			edp->nargs = SUN_EMUL_NARGS;
576 		rc = wsemul_sun_control(edp, c);
577 		if (rc != 0) {
578 			/* undo nargs progress */
579 			edp->nargs = oargs;
580 
581 			return rc;
582 		}
583 		edp->state = SUN_EMUL_STATE_NORMAL;
584 		break;
585 	}
586 
587 	return 0;
588 }
589 
590 u_int
591 wsemul_sun_output(void *cookie, const u_char *data, u_int count, int kernel)
592 {
593 	struct wsemul_sun_emuldata *edp = cookie;
594 	u_int processed = 0;
595 	u_char c;
596 #ifdef JUMP_SCROLL
597 	int lines;
598 #endif
599 	int rc = 0;
600 
601 #ifdef DIAGNOSTIC
602 	if (kernel && !edp->console)
603 		panic("wsemul_sun_output: kernel output, not console");
604 #endif
605 
606 	switch (edp->abortstate.state) {
607 	case ABORT_FAILED_CURSOR:
608 		/*
609 		 * If we could not display the cursor back, we pretended not
610 		 * having been able to display the last character. But this
611 		 * is a lie, so compensate here.
612 		 */
613 		data++, count--;
614 		processed++;
615 		wsemul_reset_abortstate(&edp->abortstate);
616 		break;
617 	case ABORT_OK:
618 		/* remove cursor image */
619 		rc = (*edp->emulops->cursor)
620 		    (edp->emulcookie, 0, edp->crow, edp->ccol);
621 		if (rc != 0)
622 			return 0;
623 		break;
624 	default:
625 		break;
626 	}
627 
628 	for (; count > 0; data++, count--) {
629 #ifdef JUMP_SCROLL
630 		switch (edp->abortstate.state) {
631 		case ABORT_FAILED_JUMP_SCROLL:
632 			/*
633 			 * If we failed a previous jump scroll attempt, we
634 			 * need to try to resume it with the same distance.
635 			 * We can not recompute it since there might be more
636 			 * bytes in the tty ring, causing a different result.
637 			 */
638 			lines = edp->abortstate.lines;
639 			break;
640 		case ABORT_OK:
641 			/*
642 			 * If scrolling is not disabled and we are the bottom of
643 			 * the screen, count newlines until an escape sequence
644 			 * appears.
645 			 */
646 			if ((edp->state == SUN_EMUL_STATE_NORMAL || kernel) &&
647 			    ROWS_LEFT == 0 && edp->scrolldist != 0)
648 				lines = wsemul_sun_jump_scroll(edp, data,
649 				    count, kernel);
650 			else
651 				lines = 0;
652 			break;
653 		default:
654 			/*
655 			 * If we are recovering a non-scrolling failure,
656 			 * do not try to scroll yet.
657 			 */
658 			lines = 0;
659 			break;
660 		}
661 
662 		if (lines > 1) {
663 			wsemul_resume_abort(&edp->abortstate);
664 			rc = wsemul_sun_scrollup(edp, lines);
665 			if (rc != 0) {
666 				wsemul_abort_jump_scroll(&edp->abortstate,
667 				    lines);
668 				return processed;
669 			}
670 			wsemul_reset_abortstate(&edp->abortstate);
671 			edp->crow--;
672 		}
673 #endif
674 
675 		wsemul_resume_abort(&edp->abortstate);
676 
677 		c = *data;
678 		if (c < ' ') {
679 			rc = wsemul_sun_output_lowchars(edp, c, kernel);
680 			if (rc != 0)
681 				break;
682 			processed++;
683 			continue;
684 		}
685 
686 		if (kernel) {
687 			rc = wsemul_sun_output_normal(edp, c, 1);
688 			if (rc != 0)
689 				break;
690 			processed++;
691 			continue;
692 		}
693 
694 		switch (edp->state) {
695 		case SUN_EMUL_STATE_NORMAL:
696 			rc = wsemul_sun_output_normal(edp, c, 0);
697 			break;
698 		case SUN_EMUL_STATE_HAVEESC:
699 			wsemul_sun_output_haveesc(edp, c);
700 			break;
701 		case SUN_EMUL_STATE_CONTROL:
702 			rc = wsemul_sun_output_control(edp, c);
703 			break;
704 		default:
705 #ifdef DIAGNOSTIC
706 			panic("wsemul_sun: invalid state %d", edp->state);
707 #else
708 			/* try to recover, if things get screwed up... */
709 			edp->state = SUN_EMUL_STATE_NORMAL;
710 			rc = wsemul_sun_output_normal(edp, c, 0);
711 #endif
712 			break;
713 		}
714 		if (rc != 0)
715 			break;
716 		processed++;
717 	}
718 
719 	if (rc != 0)
720 		wsemul_abort_other(&edp->abortstate);
721 	else {
722 		/* put cursor image back */
723 		rc = (*edp->emulops->cursor)
724 		    (edp->emulcookie, 1, edp->crow, edp->ccol);
725 		if (rc != 0) {
726 			/*
727 			 * Fail the last character output, remembering that
728 			 * only the cursor operation really needs to be done.
729 			 */
730 			wsemul_abort_cursor(&edp->abortstate);
731 			processed--;
732 		}
733 	}
734 
735 	if (rc == 0)
736 		wsemul_reset_abortstate(&edp->abortstate);
737 
738 	return processed;
739 }
740 
741 #ifdef JUMP_SCROLL
742 int
743 wsemul_sun_jump_scroll(struct wsemul_sun_emuldata *edp, const u_char *data,
744     u_int count, int kernel)
745 {
746 	u_char curchar;
747 	u_int pos, lines;
748 
749 	lines = 0;
750 	pos = edp->ccol;
751 	for (; count != 0; data++, count--) {
752 		curchar = *data;
753 		if (curchar == ASCII_FF ||
754 		    curchar == ASCII_VT || curchar == ASCII_ESC)
755 			break;
756 
757 		switch (curchar) {
758 		case ASCII_BS:
759 			if (pos > 0)
760 				pos--;
761 			break;
762 		case ASCII_CR:
763 			pos = 0;
764 			break;
765 		case ASCII_HT:
766 			pos = (pos + 7) & ~7;
767 			if (pos >= edp->ncols)
768 				pos = edp->ncols - 1;
769 			break;
770 		case ASCII_LF:
771 			break;
772 		default:
773 			if (++pos >= edp->ncols) {
774 				pos = 0;
775 				curchar = ASCII_LF;
776 			}
777 			break;
778 		}
779 		if (curchar == ASCII_LF) {
780 			if (++lines >= edp->nrows - 1)
781 				break;
782 		}
783 	}
784 
785 	return lines;
786 }
787 #endif
788 
789 /*
790  * Get an attribute from the graphics driver.
791  * Try to find replacements if the desired appearance is not supported.
792  */
793 int
794 wsemul_sun_selectattribute(struct wsemul_sun_emuldata *edp, int flags,
795     int fgcol, int bgcol, long *attr, long *bkgdattr)
796 {
797 	int error;
798 
799 	/*
800 	 * Rasops will force white on black as normal output colors, unless
801 	 * WSATTR_WSCOLORS is specified. Since Sun console is black on white,
802 	 * always use WSATTR_WSCOLORS and our colors, as we know better.
803 	 */
804 	if (!(edp->scrcapabilities & WSSCREEN_WSCOLORS)) {
805 		flags &= ~WSATTR_WSCOLORS;
806 	} else {
807 		flags |= WSATTR_WSCOLORS;
808 	}
809 
810 	error = (*edp->emulops->alloc_attr)(edp->emulcookie, fgcol, bgcol,
811 					    flags & WSATTR_WSCOLORS, bkgdattr);
812 	if (error)
813 		return (error);
814 
815 	if ((flags & WSATTR_HILIT) &&
816 	    !(edp->scrcapabilities & WSSCREEN_HILIT)) {
817 		flags &= ~WSATTR_HILIT;
818 		if (edp->scrcapabilities & WSSCREEN_WSCOLORS) {
819 			fgcol = WSCOL_RED;
820 			flags |= WSATTR_WSCOLORS;
821 		}
822 	}
823 	if ((flags & WSATTR_UNDERLINE) &&
824 	    !(edp->scrcapabilities & WSSCREEN_UNDERLINE)) {
825 		flags &= ~WSATTR_UNDERLINE;
826 		if (edp->scrcapabilities & WSSCREEN_WSCOLORS) {
827 			fgcol = WSCOL_CYAN;
828 			flags &= ~WSATTR_UNDERLINE;
829 			flags |= WSATTR_WSCOLORS;
830 		}
831 	}
832 	if ((flags & WSATTR_BLINK) &&
833 	    !(edp->scrcapabilities & WSSCREEN_BLINK)) {
834 		flags &= ~WSATTR_BLINK;
835 	}
836 	if ((flags & WSATTR_REVERSE) &&
837 	    !(edp->scrcapabilities & WSSCREEN_REVERSE)) {
838 		flags &= ~WSATTR_REVERSE;
839 		if (edp->scrcapabilities & WSSCREEN_WSCOLORS) {
840 			int help;
841 			help = bgcol;
842 			bgcol = fgcol;
843 			fgcol = help;
844 			flags |= WSATTR_WSCOLORS;
845 		}
846 	}
847 	error = (*edp->emulops->alloc_attr)(edp->emulcookie, fgcol, bgcol,
848 					    flags, attr);
849 	if (error)
850 		return (error);
851 
852 	return (0);
853 }
854 
855 static const char *sun_fkeys[] = {
856 	"\033[224z",	/* F1 */
857 	"\033[225z",
858 	"\033[226z",
859 	"\033[227z",
860 	"\033[228z",
861 	"\033[229z",
862 	"\033[230z",
863 	"\033[231z",
864 	"\033[232z",
865 	"\033[233z",
866 	"\033[234z",
867 	"\033[235z",	/* F12 */
868 };
869 
870 static const char *sun_lkeys[] = {
871 	"\033[207z",	/* KS_Help */
872 	NULL,		/* KS_Execute */
873 	"\033[200z",	/* KS_Find */
874 	NULL,		/* KS_Select */
875 	"\033[193z",	/* KS_Again */
876 	"\033[194z",	/* KS_Props */
877 	"\033[195z",	/* KS_Undo */
878 	"\033[196z",	/* KS_Front */
879 	"\033[197z",	/* KS_Copy */
880 	"\033[198z",	/* KS_Open */
881 	"\033[199z",	/* KS_Paste */
882 	"\033[201z",	/* KS_Cut */
883 };
884 
885 int
886 wsemul_sun_translate(void *cookie, keysym_t in, const char **out)
887 {
888 	static char c;
889 
890 	if (KS_GROUP(in) == KS_GROUP_Keypad && (in & 0x80) == 0) {
891 		c = in & 0xff; /* turn into ASCII */
892 		*out = &c;
893 		return (1);
894 	}
895 
896 	if (in >= KS_f1 && in <= KS_f12) {
897 		*out = sun_fkeys[in - KS_f1];
898 		return (6);
899 	}
900 	if (in >= KS_F1 && in <= KS_F12) {
901 		*out = sun_fkeys[in - KS_F1];
902 		return (6);
903 	}
904 	if (in >= KS_KP_F1 && in <= KS_KP_F4) {
905 		*out = sun_fkeys[in - KS_KP_F1];
906 		return (6);
907 	}
908 	if (in >= KS_Help && in <= KS_Cut && sun_lkeys[in - KS_Help] != NULL) {
909 		*out = sun_lkeys[in - KS_Help];
910 		return (6);
911 	}
912 
913 	switch (in) {
914 	case KS_Home:
915 	case KS_KP_Home:
916 	case KS_KP_Begin:
917 		*out = "\033[214z";
918 		return (6);
919 	case KS_End:
920 	case KS_KP_End:
921 		*out = "\033[220z";
922 		return (6);
923 	case KS_Insert:
924 	case KS_KP_Insert:
925 		*out = "\033[247z";
926 		return (6);
927 	case KS_Prior:
928 	case KS_KP_Prior:
929 		*out = "\033[216z";
930 		return (6);
931 	case KS_Next:
932 	case KS_KP_Next:
933 		*out = "\033[222z";
934 		return (6);
935 	case KS_Up:
936 	case KS_KP_Up:
937 		*out = "\033[A";
938 		return (3);
939 	case KS_Down:
940 	case KS_KP_Down:
941 		*out = "\033[B";
942 		return (3);
943 	case KS_Left:
944 	case KS_KP_Left:
945 		*out = "\033[D";
946 		return (3);
947 	case KS_Right:
948 	case KS_KP_Right:
949 		*out = "\033[C";
950 		return (3);
951 	case KS_KP_Delete:
952 		*out = "\177";
953 		return (1);
954 	}
955 	return (0);
956 }
957 
958 void
959 wsemul_sun_detach(void *cookie, u_int *crowp, u_int *ccolp)
960 {
961 	struct wsemul_sun_emuldata *edp = cookie;
962 
963 	*crowp = edp->crow;
964 	*ccolp = edp->ccol;
965 	if (edp != &wsemul_sun_console_emuldata)
966 		free(edp, M_DEVBUF);
967 }
968 
969 void
970 wsemul_sun_resetop(void *cookie, enum wsemul_resetops op)
971 {
972 	struct wsemul_sun_emuldata *edp = cookie;
973 
974 	switch (op) {
975 	case WSEMUL_RESET:
976 		wsemul_sun_reset(edp);
977 		break;
978 	case WSEMUL_CLEARSCREEN:
979 		(*edp->emulops->eraserows)(edp->emulcookie, 0, edp->nrows,
980 		    edp->bkgdattr);
981 		edp->ccol = edp->crow = 0;
982 		(*edp->emulops->cursor)(edp->emulcookie, 1, 0, 0);
983 		break;
984 	case WSEMUL_CLEARCURSOR:
985 		(*edp->emulops->cursor)(edp->emulcookie, 0, edp->crow,
986 		    edp->ccol);
987 		break;
988 	default:
989 		break;
990 	}
991 }
992 
993 int
994 wsemul_sun_scrollup(struct wsemul_sun_emuldata *edp, u_int lines)
995 {
996 	int rc;
997 
998 	/*
999 	 * if we're in wrap-around mode, go to the first
1000 	 * line and clear it.
1001 	 */
1002 	if (lines == 0) {
1003 		WSEMULOP(rc, edp, &edp->abortstate, eraserows,
1004 		    (edp->emulcookie, 0, 1, edp->bkgdattr));
1005 		if (rc != 0)
1006 			return rc;
1007 
1008 		edp->crow = 0;
1009 		return 0;
1010 	}
1011 
1012 	/*
1013 	 * If the scrolling distance is equal to the screen height
1014 	 * (usually 34), clear the screen; otherwise, scroll by the
1015 	 * scrolling distance.
1016 	 */
1017 	if (lines < edp->nrows) {
1018 		WSEMULOP(rc, edp, &edp->abortstate, copyrows,
1019 		    (edp->emulcookie, lines, 0, edp->nrows - lines));
1020 		if (rc != 0)
1021 			return rc;
1022 	}
1023 	WSEMULOP(rc, edp, &edp->abortstate, eraserows,
1024 	    (edp->emulcookie, edp->nrows - lines, lines, edp->bkgdattr));
1025 	if (rc != 0)
1026 		return rc;
1027 
1028 	edp->crow -= lines - 1;
1029 
1030 	return 0;
1031 }
1032