xref: /openbsd-src/sys/dev/wscons/wsemul_sun.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /* $OpenBSD: wsemul_sun.c,v 1.32 2015/08/28 00:03:53 deraadt Exp $ */
2 /* $NetBSD: wsemul_sun.c,v 1.11 2000/01/05 11:19:36 drochner Exp $ */
3 
4 /*
5  * Copyright (c) 2007, 2013 Miodrag Vallat.
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice, this permission notice, and the disclaimer below
10  * appear in all copies.
11  *
12  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19  */
20 /*
21  * Copyright (c) 1996, 1997 Christopher G. Demetriou.  All rights reserved.
22  *
23  * Redistribution and use in source and binary forms, with or without
24  * modification, are permitted provided that the following conditions
25  * are met:
26  * 1. Redistributions of source code must retain the above copyright
27  *    notice, this list of conditions and the following disclaimer.
28  * 2. Redistributions in binary form must reproduce the above copyright
29  *    notice, this list of conditions and the following disclaimer in the
30  *    documentation and/or other materials provided with the distribution.
31  * 3. All advertising materials mentioning features or use of this software
32  *    must display the following acknowledgement:
33  *      This product includes software developed by Christopher G. Demetriou
34  *	for the NetBSD Project.
35  * 4. The name of the author may not be used to endorse or promote products
36  *    derived from this software without specific prior written permission
37  *
38  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
39  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
40  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
41  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
42  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
43  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
44  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
45  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
46  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
47  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
48  */
49 
50 /*
51  * This file implements a sun terminal personality for wscons.
52  *
53  * Derived from old rcons code.
54  * Color support from NetBSD's rcons color code, and wsemul_vt100.
55  */
56 
57 #include <sys/param.h>
58 #include <sys/systm.h>
59 #include <sys/time.h>
60 #include <sys/malloc.h>
61 #include <sys/fcntl.h>
62 
63 #include <dev/wscons/wscons_features.h>
64 #include <dev/wscons/wsconsio.h>
65 #include <dev/wscons/wsdisplayvar.h>
66 #include <dev/wscons/wsemulvar.h>
67 #include <dev/wscons/wsksymdef.h>
68 #include <dev/wscons/ascii.h>
69 
70 void	*wsemul_sun_cnattach(const struct wsscreen_descr *, void *,
71     int, int, long);
72 void	*wsemul_sun_attach(int, const struct wsscreen_descr *,
73     void *, int, int, void *, long);
74 u_int	wsemul_sun_output(void *, const u_char *, u_int, int);
75 int	wsemul_sun_translate(void *, kbd_t, keysym_t, const u_char **);
76 void	wsemul_sun_detach(void *, u_int *, u_int *);
77 void	wsemul_sun_resetop(void *, enum wsemul_resetops);
78 
79 const struct wsemul_ops wsemul_sun_ops = {
80 	"sun",
81 	wsemul_sun_cnattach,
82 	wsemul_sun_attach,
83 	wsemul_sun_output,
84 	wsemul_sun_translate,
85 	wsemul_sun_detach,
86 	wsemul_sun_resetop
87 };
88 
89 #define	SUN_EMUL_STATE_NORMAL	0	/* normal processing */
90 #define	SUN_EMUL_STATE_HAVEESC	1	/* seen start of ctl seq */
91 #define	SUN_EMUL_STATE_CONTROL	2	/* processing ESC [ ctl seq */
92 #define	SUN_EMUL_STATE_PERCENT	3	/* processing ESC % ctl seq */
93 
94 #define	SUN_EMUL_FLAGS_UTF8	0x01	/* UTF-8 character set */
95 
96 #define	SUN_EMUL_NARGS	2		/* max # of args to a command */
97 
98 struct wsemul_sun_emuldata {
99 	const struct wsdisplay_emulops *emulops;
100 	struct wsemul_abortstate abortstate;
101 	void *emulcookie;
102 	void *cbcookie;
103 	int scrcapabilities;
104 	u_int nrows, ncols, crow, ccol;
105 	long defattr;			/* default attribute (rendition) */
106 
107 	u_int state;			/* processing state */
108 	u_int flags;
109 	u_int args[SUN_EMUL_NARGS];	/* command args, if CONTROL */
110 	int nargs;			/* number of args */
111 
112 	u_int scrolldist;		/* distance to scroll */
113 	long curattr, bkgdattr;		/* currently used attribute */
114 	long kernattr;			/* attribute for kernel output */
115 	int attrflags, fgcol, bgcol;	/* properties of curattr */
116 
117 	struct wsemul_inputstate instate;	/* userland input state */
118 	struct wsemul_inputstate kstate;	/* kernel input state */
119 
120 #ifdef HAVE_UTF8_SUPPORT
121 	u_char translatebuf[6];
122 #else
123 	u_char translatebuf[1];
124 #endif
125 
126 #ifdef DIAGNOSTIC
127 	int console;
128 #endif
129 };
130 
131 void	wsemul_sun_init(struct wsemul_sun_emuldata *,
132 	    const struct wsscreen_descr *, void *, int, int, long);
133 int	wsemul_sun_jump_scroll(struct wsemul_sun_emuldata *, const u_char *,
134 	    u_int, int);
135 void	wsemul_sun_reset(struct wsemul_sun_emuldata *);
136 int	wsemul_sun_output_lowchars(struct wsemul_sun_emuldata *,
137 	    struct wsemul_inputstate *, int);
138 int	wsemul_sun_output_normal(struct wsemul_sun_emuldata *,
139 	    struct wsemul_inputstate *, int);
140 int	wsemul_sun_output_haveesc(struct wsemul_sun_emuldata *,
141 	    struct wsemul_inputstate *);
142 int	wsemul_sun_output_control(struct wsemul_sun_emuldata *,
143 	    struct wsemul_inputstate *);
144 int	wsemul_sun_output_percent(struct wsemul_sun_emuldata *,
145 	    struct wsemul_inputstate *);
146 int	wsemul_sun_control(struct wsemul_sun_emuldata *,
147 	    struct wsemul_inputstate *);
148 int	wsemul_sun_selectattribute(struct wsemul_sun_emuldata *, int, int, int,
149 	    long *, long *);
150 int	wsemul_sun_scrollup(struct wsemul_sun_emuldata *, u_int);
151 
152 struct wsemul_sun_emuldata wsemul_sun_console_emuldata;
153 
154 /* some useful utility macros */
155 #define	ARG(n,c) \
156 	((n) >= edp->nargs ? 0 : edp->args[(n) + MAX(0, edp->nargs - (c))])
157 #define	NORMALIZE(arg)		((arg) != 0 ? (arg) : 1)
158 #define	COLS_LEFT		(edp->ncols - 1 - edp->ccol)
159 #define	ROWS_LEFT		(edp->nrows - 1 - edp->crow)
160 
161 void
162 wsemul_sun_init(struct wsemul_sun_emuldata *edp,
163     const struct wsscreen_descr *type, void *cookie, int ccol, int crow,
164     long defattr)
165 {
166 	edp->emulops = type->textops;
167 	edp->emulcookie = cookie;
168 	edp->scrcapabilities = type->capabilities;
169 	edp->nrows = type->nrows;
170 	edp->ncols = type->ncols;
171 	edp->crow = crow;
172 	edp->ccol = ccol;
173 	edp->defattr = defattr;
174 	wsemul_reset_abortstate(&edp->abortstate);
175 }
176 
177 void
178 wsemul_sun_reset(struct wsemul_sun_emuldata *edp)
179 {
180 	edp->flags = 0;
181 	edp->state = SUN_EMUL_STATE_NORMAL;
182 	edp->bkgdattr = edp->curattr = edp->defattr;
183 	edp->attrflags = 0;
184 	edp->fgcol = WSCOL_BLACK;
185 	edp->bgcol = WSCOL_WHITE;
186 	edp->scrolldist = 1;
187 	edp->instate.inchar = 0;
188 	edp->instate.mbleft = 0;
189 	edp->kstate.inchar = 0;
190 	edp->kstate.mbleft = 0;
191 }
192 
193 void *
194 wsemul_sun_cnattach(const struct wsscreen_descr *type, void *cookie, int ccol,
195     int crow, long defattr)
196 {
197 	struct wsemul_sun_emuldata *edp;
198 	int res;
199 
200 	edp = &wsemul_sun_console_emuldata;
201 	wsemul_sun_init(edp, type, cookie, ccol, crow, defattr);
202 
203 #ifndef WS_KERNEL_FG
204 #define WS_KERNEL_FG WSCOL_BLACK
205 #endif
206 #ifndef WS_KERNEL_BG
207 #define WS_KERNEL_BG WSCOL_WHITE
208 #endif
209 #ifndef WS_KERNEL_COLATTR
210 #define WS_KERNEL_COLATTR 0
211 #endif
212 #ifndef WS_KERNEL_MONOATTR
213 #define WS_KERNEL_MONOATTR 0
214 #endif
215 	if (type->capabilities & WSSCREEN_WSCOLORS)
216 		res = (*edp->emulops->alloc_attr)(cookie,
217 					    WS_KERNEL_FG, WS_KERNEL_BG,
218 					    WS_KERNEL_COLATTR | WSATTR_WSCOLORS,
219 					    &edp->kernattr);
220 	else
221 		res = (*edp->emulops->alloc_attr)(cookie, 0, 0,
222 					    WS_KERNEL_MONOATTR,
223 					    &edp->kernattr);
224 	if (res)
225 		edp->kernattr = defattr;
226 
227 	edp->cbcookie = NULL;
228 
229 #ifdef DIAGNOSTIC
230 	edp->console = 1;
231 #endif
232 
233 	wsemul_sun_reset(edp);
234 	return (edp);
235 }
236 
237 void *
238 wsemul_sun_attach(int console, const struct wsscreen_descr *type, void *cookie,
239     int ccol, int crow, void *cbcookie, long defattr)
240 {
241 	struct wsemul_sun_emuldata *edp;
242 
243 	if (console) {
244 		edp = &wsemul_sun_console_emuldata;
245 #ifdef DIAGNOSTIC
246 		KASSERT(edp->console == 1);
247 #endif
248 	} else {
249 		edp = malloc(sizeof *edp, M_DEVBUF, M_NOWAIT);
250 		if (edp == NULL)
251 			return (NULL);
252 		wsemul_sun_init(edp, type, cookie, ccol, crow, defattr);
253 
254 #ifdef DIAGNOSTIC
255 		edp->console = 0;
256 #endif
257 	}
258 
259 	edp->cbcookie = cbcookie;
260 
261 	wsemul_sun_reset(edp);
262 	return (edp);
263 }
264 
265 int
266 wsemul_sun_output_lowchars(struct wsemul_sun_emuldata *edp,
267     struct wsemul_inputstate *instate, int kernel)
268 {
269 	u_int n;
270 	int rc = 0;
271 
272 	switch (instate->inchar) {
273 	case ASCII_NUL:
274 	default:
275 		/* ignore */
276 		break;
277 
278 	case ASCII_BEL:		/* "Bell (BEL)" */
279 		wsdisplay_emulbell(edp->cbcookie);
280 		break;
281 
282 	case ASCII_BS:		/* "Backspace (BS)" */
283 		if (edp->ccol > 0)
284 			edp->ccol--;
285 		break;
286 
287 	case ASCII_CR:		/* "Return (CR)" */
288 		edp->ccol = 0;
289 		break;
290 
291 	case ASCII_HT:		/* "Tab (TAB)" */
292 		n = min(8 - (edp->ccol & 7), COLS_LEFT);
293 		if (n != 0) {
294 			WSEMULOP(rc, edp, &edp->abortstate, erasecols,
295 			    (edp->emulcookie, edp->crow, edp->ccol, n,
296 			     kernel ? edp->kernattr : edp->bkgdattr));
297 			if (rc != 0)
298 				break;
299 			edp->ccol += n;
300 		}
301 		break;
302 
303 	case ASCII_FF:		/* "Form Feed (FF)" */
304 		WSEMULOP(rc, edp, &edp->abortstate, eraserows,
305 		    (edp->emulcookie, 0, edp->nrows, edp->bkgdattr));
306 		if (rc != 0)
307 			break;
308 		edp->ccol = edp->crow = 0;
309 		break;
310 
311 	case ASCII_VT:		/* "Reverse Line Feed" */
312 		if (edp->crow > 0)
313 			edp->crow--;
314 		break;
315 
316 	case ASCII_ESC:		/* "Escape (ESC)" */
317 		if (kernel) {
318 			printf("wsemul_sun_output_lowchars: ESC in kernel "
319 			    "output ignored\n");
320 			break;	/* ignore the ESC */
321 		}
322 
323 		edp->state = SUN_EMUL_STATE_HAVEESC;
324 		break;
325 
326 	case ASCII_LF:		/* "Line Feed (LF)" */
327 		/* if the cur line isn't the last, incr and leave. */
328 		if (ROWS_LEFT > 0)
329 			edp->crow++;
330 		else {
331 			rc = wsemul_sun_scrollup(edp, edp->scrolldist);
332 			if (rc != 0)
333 				break;
334 		}
335 		break;
336 	}
337 
338 	return rc;
339 }
340 
341 int
342 wsemul_sun_output_normal(struct wsemul_sun_emuldata *edp,
343     struct wsemul_inputstate *instate, int kernel)
344 {
345 	int rc;
346 	u_int outchar;
347 
348 	(*edp->emulops->mapchar)(edp->emulcookie, instate->inchar, &outchar);
349 	WSEMULOP(rc, edp, &edp->abortstate, putchar,
350 	    (edp->emulcookie, edp->crow, edp->ccol,
351 	     outchar, kernel ? edp->kernattr : edp->curattr));
352 	if (rc != 0)
353 		return rc;
354 
355 	if (++edp->ccol >= edp->ncols) {
356 		/* if the cur line isn't the last, incr and leave. */
357 		if (ROWS_LEFT > 0)
358 			edp->crow++;
359 		else {
360 			rc = wsemul_sun_scrollup(edp, edp->scrolldist);
361 			if (rc != 0) {
362 				/* undo line wrap */
363 				edp->ccol--;
364 
365 				return rc;
366 			}
367 		}
368 		edp->ccol = 0;
369 	}
370 
371 	return 0;
372 }
373 
374 int
375 wsemul_sun_output_haveesc(struct wsemul_sun_emuldata *edp,
376     struct wsemul_inputstate *instate)
377 {
378 	switch (instate->inchar) {
379 	case '[':		/* continuation of multi-char sequence */
380 		edp->nargs = 0;
381 		bzero(edp->args, sizeof (edp->args));
382 		edp->state = SUN_EMUL_STATE_CONTROL;
383 		break;
384 #ifdef HAVE_UTF8_SUPPORT
385 	case '%':
386 		edp->state = SUN_EMUL_STATE_PERCENT;
387 		break;
388 #endif
389 	default:
390 #ifdef DEBUG
391 		printf("ESC %x unknown\n", instate->inchar);
392 #endif
393 		edp->state = SUN_EMUL_STATE_NORMAL;	/* XXX is this wise? */
394 		break;
395 	}
396 	return 0;
397 }
398 
399 int
400 wsemul_sun_control(struct wsemul_sun_emuldata *edp,
401     struct wsemul_inputstate *instate)
402 {
403 	u_int n, src, dst;
404 	int flags, fgcol, bgcol;
405 	long attr, bkgdattr;
406 	int rc = 0;
407 
408 	switch (instate->inchar) {
409 	case '@':		/* "Insert Character (ICH)" */
410 		n = min(NORMALIZE(ARG(0,1)), COLS_LEFT + 1);
411 		src = edp->ccol;
412 		dst = edp->ccol + n;
413 		if (dst < edp->ncols) {
414 			WSEMULOP(rc, edp, &edp->abortstate, copycols,
415 			    (edp->emulcookie, edp->crow, src, dst,
416 			     edp->ncols - dst));
417 			if (rc != 0)
418 				break;
419 		}
420 		WSEMULOP(rc, edp, &edp->abortstate, erasecols,
421 		    (edp->emulcookie, edp->crow, src, n, edp->bkgdattr));
422 		break;
423 
424 	case 'A':		/* "Cursor Up (CUU)" */
425 		edp->crow -= min(NORMALIZE(ARG(0,1)), edp->crow);
426 		break;
427 
428 	case 'E':		/* "Cursor Next Line (CNL)" */
429 		edp->ccol = 0;
430 		/* FALLTHROUGH */
431 	case 'B':		/* "Cursor Down (CUD)" */
432 		edp->crow += min(NORMALIZE(ARG(0,1)), ROWS_LEFT);
433 		break;
434 
435 	case 'C':		/* "Cursor Forward (CUF)" */
436 		edp->ccol += min(NORMALIZE(ARG(0,1)), COLS_LEFT);
437 		break;
438 
439 	case 'D':		/* "Cursor Backward (CUB)" */
440 		edp->ccol -= min(NORMALIZE(ARG(0,1)), edp->ccol);
441 		break;
442 
443 	case 'f':		/* "Horizontal And Vertical Position (HVP)" */
444 	case 'H':		/* "Cursor Position (CUP)" */
445 		edp->crow = min(NORMALIZE(ARG(0,2)), edp->nrows) - 1;
446 		edp->ccol = min(NORMALIZE(ARG(1,2)), edp->ncols) - 1;
447 		break;
448 
449 	case 'J':		/* "Erase in Display (ED)" */
450 		if (ROWS_LEFT > 0) {
451 			WSEMULOP(rc, edp, &edp->abortstate, eraserows,
452 			    (edp->emulcookie, edp->crow + 1, ROWS_LEFT,
453 			     edp->bkgdattr));
454 			if (rc != 0)
455 				break;
456 		}
457 		/* FALLTHROUGH */
458 	case 'K':		/* "Erase in Line (EL)" */
459 		WSEMULOP(rc, edp, &edp->abortstate, erasecols,
460 		    (edp->emulcookie, edp->crow, edp->ccol, COLS_LEFT + 1,
461 		     edp->bkgdattr));
462 		break;
463 
464 	case 'L':		/* "Insert Line (IL)" */
465 		n = min(NORMALIZE(ARG(0,1)), ROWS_LEFT + 1);
466 		src = edp->crow;
467 		dst = edp->crow + n;
468 		if (dst < edp->nrows) {
469 			WSEMULOP(rc, edp, &edp->abortstate, copyrows,
470 			    (edp->emulcookie, src, dst, edp->nrows - dst));
471 			if (rc != 0)
472 				break;
473 		}
474 		WSEMULOP(rc, edp, &edp->abortstate, eraserows,
475 		    (edp->emulcookie, src, n, edp->bkgdattr));
476 		break;
477 
478 	case 'M':		/* "Delete Line (DL)" */
479 		n = min(NORMALIZE(ARG(0,1)), ROWS_LEFT + 1);
480 		src = edp->crow + n;
481 		dst = edp->crow;
482 		if (src < edp->nrows) {
483 			WSEMULOP(rc, edp, &edp->abortstate, copyrows,
484 			    (edp->emulcookie, src, dst, edp->nrows - src));
485 			if (rc != 0)
486 				break;
487 		}
488 		WSEMULOP(rc, edp, &edp->abortstate, eraserows,
489 		    (edp->emulcookie, dst + edp->nrows - src, n,
490 		     edp->bkgdattr));
491 		break;
492 
493 	case 'P':		/* "Delete Character (DCH)" */
494 		n = min(NORMALIZE(ARG(0,1)), COLS_LEFT + 1);
495 		src = edp->ccol + n;
496 		dst = edp->ccol;
497 		if (src < edp->ncols) {
498 			WSEMULOP(rc, edp, &edp->abortstate, copycols,
499 			    (edp->emulcookie, edp->crow, src, dst,
500 			     edp->ncols - src));
501 			if (rc != 0)
502 				break;
503 		}
504 		WSEMULOP(rc, edp, &edp->abortstate, erasecols,
505 		    (edp->emulcookie, edp->crow, edp->ncols - n, n,
506 		     edp->bkgdattr));
507 		break;
508 
509 	case 'm':		/* "Select Graphic Rendition (SGR)" */
510 		flags = edp->attrflags;
511 		fgcol = edp->fgcol;
512 		bgcol = edp->bgcol;
513 
514 		for (n = 0; n < edp->nargs; n++) {
515 			switch (ARG(n,edp->nargs)) {
516 			/* Clear all attributes || End underline */
517 			case 0:
518 				if (n == edp->nargs - 1) {
519 					edp->bkgdattr =
520 					    edp->curattr = edp->defattr;
521 					edp->attrflags = 0;
522 					edp->fgcol = WSCOL_BLACK;
523 					edp->bgcol = WSCOL_WHITE;
524 					return 0;
525 				}
526 				flags = 0;
527 				fgcol = WSCOL_BLACK;
528 				bgcol = WSCOL_WHITE;
529 				break;
530 			/* Begin bold */
531 			case 1:
532 				flags |= WSATTR_HILIT;
533 				break;
534 			/* Begin underline */
535 			case 4:
536 				flags |= WSATTR_UNDERLINE;
537 				break;
538 			/* Begin reverse */
539 			case 7:
540 				flags |= WSATTR_REVERSE;
541 				break;
542 			/* ANSI foreground color */
543 			case 30: case 31: case 32: case 33:
544 			case 34: case 35: case 36: case 37:
545 				fgcol = ARG(n,edp->nargs) - 30;
546 				break;
547 			/* ANSI background color */
548 			case 40: case 41: case 42: case 43:
549 			case 44: case 45: case 46: case 47:
550 				bgcol = ARG(n,edp->nargs) - 40;
551 				break;
552 			}
553 		}
554 setattr:
555 		if (wsemul_sun_selectattribute(edp, flags, fgcol, bgcol, &attr,
556 		    &bkgdattr)) {
557 #ifdef DEBUG
558 			printf("error allocating attr %d/%d/%x\n",
559 			    fgcol, bgcol, flags);
560 #endif
561 		} else {
562 			edp->curattr = attr;
563 			edp->bkgdattr = bkgdattr;
564 			edp->attrflags = flags;
565 			edp->fgcol = fgcol;
566 			edp->bgcol = bgcol;
567 		}
568 		break;
569 
570 	case 'p':		/* "Black On White (SUNBOW)" */
571 		flags = 0;
572 		fgcol = WSCOL_BLACK;
573 		bgcol = WSCOL_WHITE;
574 		goto setattr;
575 
576 	case 'q':		/* "White On Black (SUNWOB)" */
577 		flags = 0;
578 		fgcol = WSCOL_WHITE;
579 		bgcol = WSCOL_BLACK;
580 		goto setattr;
581 
582 	case 'r':		/* "Set Scrolling (SUNSCRL)" */
583 		edp->scrolldist = min(ARG(0,1), edp->nrows);
584 		break;
585 
586 	case 's':		/* "Reset Terminal Emulator (SUNRESET)" */
587 		wsemul_sun_reset(edp);
588 		break;
589 	}
590 
591 	return rc;
592 }
593 
594 int
595 wsemul_sun_output_control(struct wsemul_sun_emuldata *edp,
596     struct wsemul_inputstate *instate)
597 {
598 	int oargs;
599 	int rc;
600 
601 	switch (instate->inchar) {
602 	case '0': case '1': case '2': case '3': case '4': /* argument digit */
603 	case '5': case '6': case '7': case '8': case '9':
604 		/*
605 		 * If we receive more arguments than we are expecting,
606 		 * discard the earliest arguments.
607 		 */
608 		if (edp->nargs > SUN_EMUL_NARGS - 1) {
609 			bcopy(edp->args + 1, edp->args,
610 			    (SUN_EMUL_NARGS - 1) * sizeof(edp->args[0]));
611 			edp->args[edp->nargs = SUN_EMUL_NARGS - 1] = 0;
612 		}
613 		edp->args[edp->nargs] = (edp->args[edp->nargs] * 10) +
614 		    (instate->inchar - '0');
615 		break;
616 
617 	case ';':		/* argument terminator */
618 		edp->nargs++;
619 		break;
620 
621 	default:		/* end of escape sequence */
622 		oargs = edp->nargs++;
623 		if (edp->nargs > SUN_EMUL_NARGS)
624 			edp->nargs = SUN_EMUL_NARGS;
625 		rc = wsemul_sun_control(edp, instate);
626 		if (rc != 0) {
627 			/* undo nargs progress */
628 			edp->nargs = oargs;
629 
630 			return rc;
631 		}
632 		edp->state = SUN_EMUL_STATE_NORMAL;
633 		break;
634 	}
635 
636 	return 0;
637 }
638 
639 #ifdef HAVE_UTF8_SUPPORT
640 int
641 wsemul_sun_output_percent(struct wsemul_sun_emuldata *edp,
642     struct wsemul_inputstate *instate)
643 {
644 	switch (instate->inchar) {
645 	case 'G':
646 		edp->flags |= SUN_EMUL_FLAGS_UTF8;
647 		edp->kstate.mbleft = edp->instate.mbleft = 0;
648 		break;
649 	case '@':
650 		edp->flags &= ~SUN_EMUL_FLAGS_UTF8;
651 		break;
652 	}
653 	edp->state = SUN_EMUL_STATE_NORMAL;
654 	return 0;
655 }
656 #endif
657 
658 u_int
659 wsemul_sun_output(void *cookie, const u_char *data, u_int count, int kernel)
660 {
661 	struct wsemul_sun_emuldata *edp = cookie;
662 	struct wsemul_inputstate *instate;
663 	u_int processed = 0;
664 #ifdef HAVE_JUMP_SCROLL
665 	int lines;
666 #endif
667 	int rc = 0;
668 
669 #ifdef DIAGNOSTIC
670 	if (kernel && !edp->console)
671 		panic("wsemul_sun_output: kernel output, not console");
672 #endif
673 
674 	instate = kernel ? &edp->kstate : &edp->instate;
675 
676 	switch (edp->abortstate.state) {
677 	case ABORT_FAILED_CURSOR:
678 		/*
679 		 * If we could not display the cursor back, we pretended not
680 		 * having been able to display the last character. But this
681 		 * is a lie, so compensate here.
682 		 */
683 		data++, count--;
684 		processed++;
685 		wsemul_reset_abortstate(&edp->abortstate);
686 		break;
687 	case ABORT_OK:
688 		/* remove cursor image */
689 		rc = (*edp->emulops->cursor)
690 		    (edp->emulcookie, 0, edp->crow, edp->ccol);
691 		if (rc != 0)
692 			return 0;
693 		break;
694 	default:
695 		break;
696 	}
697 
698 	for (;;) {
699 #ifdef HAVE_JUMP_SCROLL
700 		switch (edp->abortstate.state) {
701 		case ABORT_FAILED_JUMP_SCROLL:
702 			/*
703 			 * If we failed a previous jump scroll attempt, we
704 			 * need to try to resume it with the same distance.
705 			 * We can not recompute it since there might be more
706 			 * bytes in the tty ring, causing a different result.
707 			 */
708 			lines = edp->abortstate.lines;
709 			break;
710 		case ABORT_OK:
711 			/*
712 			 * If scrolling is not disabled and we are the bottom of
713 			 * the screen, count newlines until an escape sequence
714 			 * appears.
715 			 */
716 			if ((edp->state == SUN_EMUL_STATE_NORMAL || kernel) &&
717 			    ROWS_LEFT == 0 && edp->scrolldist != 0)
718 				lines = wsemul_sun_jump_scroll(edp, data,
719 				    count, kernel);
720 			else
721 				lines = 0;
722 			break;
723 		default:
724 			/*
725 			 * If we are recovering a non-scrolling failure,
726 			 * do not try to scroll yet.
727 			 */
728 			lines = 0;
729 			break;
730 		}
731 
732 		if (lines > 1) {
733 			wsemul_resume_abort(&edp->abortstate);
734 			rc = wsemul_sun_scrollup(edp, lines);
735 			if (rc != 0) {
736 				wsemul_abort_jump_scroll(&edp->abortstate,
737 				    lines);
738 				return processed;
739 			}
740 			wsemul_reset_abortstate(&edp->abortstate);
741 			edp->crow--;
742 		}
743 #endif
744 
745 		wsemul_resume_abort(&edp->abortstate);
746 
747 		if (wsemul_getchar(&data, &count, instate,
748 #ifdef HAVE_UTF8_SUPPORT
749 		    (edp->state == SUN_EMUL_STATE_NORMAL && !kernel) ?
750 		      edp->flags & SUN_EMUL_FLAGS_UTF8 : 0
751 #else
752 		    0
753 #endif
754 		    ) != 0)
755 			break;
756 
757 		if (instate->inchar < ' ') {
758 			rc = wsemul_sun_output_lowchars(edp, instate, kernel);
759 			if (rc != 0)
760 				break;
761 			processed++;
762 			continue;
763 		}
764 
765 		if (kernel) {
766 			rc = wsemul_sun_output_normal(edp, instate, 1);
767 			if (rc != 0)
768 				break;
769 			processed++;
770 			continue;
771 		}
772 
773 		switch (edp->state) {
774 		case SUN_EMUL_STATE_NORMAL:
775 			rc = wsemul_sun_output_normal(edp, instate, 0);
776 			break;
777 		case SUN_EMUL_STATE_HAVEESC:
778 			rc = wsemul_sun_output_haveesc(edp, instate);
779 			break;
780 		case SUN_EMUL_STATE_CONTROL:
781 			rc = wsemul_sun_output_control(edp, instate);
782 			break;
783 #ifdef HAVE_UTF8_SUPPORT
784 		case SUN_EMUL_STATE_PERCENT:
785 			rc = wsemul_sun_output_percent(edp, instate);
786 			break;
787 #endif
788 		default:
789 #ifdef DIAGNOSTIC
790 			panic("wsemul_sun: invalid state %d", edp->state);
791 #else
792 			/* try to recover, if things get screwed up... */
793 			edp->state = SUN_EMUL_STATE_NORMAL;
794 			rc = wsemul_sun_output_normal(edp, instate, 0);
795 #endif
796 			break;
797 		}
798 		if (rc != 0)
799 			break;
800 		processed++;
801 	}
802 
803 	if (rc != 0)
804 		wsemul_abort_other(&edp->abortstate);
805 	else {
806 		/* put cursor image back */
807 		rc = (*edp->emulops->cursor)
808 		    (edp->emulcookie, 1, edp->crow, edp->ccol);
809 		if (rc != 0) {
810 			/*
811 			 * Fail the last character output, remembering that
812 			 * only the cursor operation really needs to be done.
813 			 */
814 			wsemul_abort_cursor(&edp->abortstate);
815 			processed--;
816 		}
817 	}
818 
819 	if (rc == 0)
820 		wsemul_reset_abortstate(&edp->abortstate);
821 
822 	return processed;
823 }
824 
825 #ifdef HAVE_JUMP_SCROLL
826 int
827 wsemul_sun_jump_scroll(struct wsemul_sun_emuldata *edp, const u_char *data,
828     u_int count, int kernel)
829 {
830 	u_int pos, lines;
831 	struct wsemul_inputstate tmpstate;
832 
833 	lines = 0;
834 	pos = edp->ccol;
835 	tmpstate = kernel ? edp->kstate : edp->instate;	/* structure copy */
836 
837 	while (wsemul_getchar(&data, &count, &tmpstate,
838 #ifdef HAVE_UTF8_SUPPORT
839 	    kernel ? 0 : edp->flags & SUN_EMUL_FLAGS_UTF8
840 #else
841 	    0
842 #endif
843 	    ) == 0) {
844 		if (tmpstate.inchar == ASCII_FF ||
845 		    tmpstate.inchar == ASCII_VT ||
846 		    tmpstate.inchar == ASCII_ESC)
847 			break;
848 
849 		switch (tmpstate.inchar) {
850 		case ASCII_BS:
851 			if (pos > 0)
852 				pos--;
853 			break;
854 		case ASCII_CR:
855 			pos = 0;
856 			break;
857 		case ASCII_HT:
858 			pos = (pos + 7) & ~7;
859 			if (pos >= edp->ncols)
860 				pos = edp->ncols - 1;
861 			break;
862 		case ASCII_LF:
863 			break;
864 		default:
865 			if (++pos >= edp->ncols) {
866 				pos = 0;
867 				tmpstate.inchar = ASCII_LF;
868 			}
869 			break;
870 		}
871 		if (tmpstate.inchar == ASCII_LF) {
872 			if (++lines >= edp->nrows - 1)
873 				break;
874 		}
875 	}
876 
877 	return lines;
878 }
879 #endif
880 
881 /*
882  * Get an attribute from the graphics driver.
883  * Try to find replacements if the desired appearance is not supported.
884  */
885 int
886 wsemul_sun_selectattribute(struct wsemul_sun_emuldata *edp, int flags,
887     int fgcol, int bgcol, long *attr, long *bkgdattr)
888 {
889 	int error;
890 
891 	/*
892 	 * Rasops will force white on black as normal output colors, unless
893 	 * WSATTR_WSCOLORS is specified. Since Sun console is black on white,
894 	 * always use WSATTR_WSCOLORS and our colors, as we know better.
895 	 */
896 	if (!(edp->scrcapabilities & WSSCREEN_WSCOLORS)) {
897 		flags &= ~WSATTR_WSCOLORS;
898 	} else {
899 		flags |= WSATTR_WSCOLORS;
900 	}
901 
902 	error = (*edp->emulops->alloc_attr)(edp->emulcookie, fgcol, bgcol,
903 					    flags & WSATTR_WSCOLORS, bkgdattr);
904 	if (error)
905 		return (error);
906 
907 	if ((flags & WSATTR_HILIT) &&
908 	    !(edp->scrcapabilities & WSSCREEN_HILIT)) {
909 		flags &= ~WSATTR_HILIT;
910 		if (edp->scrcapabilities & WSSCREEN_WSCOLORS) {
911 			fgcol = WSCOL_RED;
912 			flags |= WSATTR_WSCOLORS;
913 		}
914 	}
915 	if ((flags & WSATTR_UNDERLINE) &&
916 	    !(edp->scrcapabilities & WSSCREEN_UNDERLINE)) {
917 		flags &= ~WSATTR_UNDERLINE;
918 		if (edp->scrcapabilities & WSSCREEN_WSCOLORS) {
919 			fgcol = WSCOL_CYAN;
920 			flags &= ~WSATTR_UNDERLINE;
921 			flags |= WSATTR_WSCOLORS;
922 		}
923 	}
924 	if ((flags & WSATTR_BLINK) &&
925 	    !(edp->scrcapabilities & WSSCREEN_BLINK)) {
926 		flags &= ~WSATTR_BLINK;
927 	}
928 	if ((flags & WSATTR_REVERSE) &&
929 	    !(edp->scrcapabilities & WSSCREEN_REVERSE)) {
930 		flags &= ~WSATTR_REVERSE;
931 		if (edp->scrcapabilities & WSSCREEN_WSCOLORS) {
932 			int help;
933 			help = bgcol;
934 			bgcol = fgcol;
935 			fgcol = help;
936 			flags |= WSATTR_WSCOLORS;
937 		}
938 	}
939 	error = (*edp->emulops->alloc_attr)(edp->emulcookie, fgcol, bgcol,
940 					    flags, attr);
941 	if (error)
942 		return (error);
943 
944 	return (0);
945 }
946 
947 static const u_char *sun_fkeys[] = {
948 	"\033[224z",	/* F1 */
949 	"\033[225z",
950 	"\033[226z",
951 	"\033[227z",
952 	"\033[228z",
953 	"\033[229z",
954 	"\033[230z",
955 	"\033[231z",
956 	"\033[232z",
957 	"\033[233z",
958 	"\033[234z",
959 	"\033[235z",	/* F12 */
960 };
961 
962 static const u_char *sun_lkeys[] = {
963 	"\033[207z",	/* KS_Help */
964 	NULL,		/* KS_Execute */
965 	"\033[200z",	/* KS_Find */
966 	NULL,		/* KS_Select */
967 	"\033[193z",	/* KS_Again */
968 	"\033[194z",	/* KS_Props */
969 	"\033[195z",	/* KS_Undo */
970 	"\033[196z",	/* KS_Front */
971 	"\033[197z",	/* KS_Copy */
972 	"\033[198z",	/* KS_Open */
973 	"\033[199z",	/* KS_Paste */
974 	"\033[201z",	/* KS_Cut */
975 };
976 
977 int
978 wsemul_sun_translate(void *cookie, kbd_t layout, keysym_t in,
979     const u_char **out)
980 {
981 	struct wsemul_sun_emuldata *edp = cookie;
982 
983 	if (KS_GROUP(in) == KS_GROUP_Ascii) {
984 		*out = edp->translatebuf;
985 		return (wsemul_utf8_translate(KS_VALUE(in), layout,
986 		    edp->translatebuf, edp->flags & SUN_EMUL_FLAGS_UTF8));
987 	}
988 
989 	if (KS_GROUP(in) == KS_GROUP_Keypad && (in & 0x80) == 0) {
990 		edp->translatebuf[0] = in & 0xff; /* turn into ASCII */
991 		*out = edp->translatebuf;
992 		return (1);
993 	}
994 
995 	if (in >= KS_f1 && in <= KS_f12) {
996 		*out = sun_fkeys[in - KS_f1];
997 		return (6);
998 	}
999 	if (in >= KS_F1 && in <= KS_F12) {
1000 		*out = sun_fkeys[in - KS_F1];
1001 		return (6);
1002 	}
1003 	if (in >= KS_KP_F1 && in <= KS_KP_F4) {
1004 		*out = sun_fkeys[in - KS_KP_F1];
1005 		return (6);
1006 	}
1007 	if (in >= KS_Help && in <= KS_Cut && sun_lkeys[in - KS_Help] != NULL) {
1008 		*out = sun_lkeys[in - KS_Help];
1009 		return (6);
1010 	}
1011 
1012 	switch (in) {
1013 	case KS_Home:
1014 	case KS_KP_Home:
1015 	case KS_KP_Begin:
1016 		*out = "\033[214z";
1017 		return (6);
1018 	case KS_End:
1019 	case KS_KP_End:
1020 		*out = "\033[220z";
1021 		return (6);
1022 	case KS_Insert:
1023 	case KS_KP_Insert:
1024 		*out = "\033[247z";
1025 		return (6);
1026 	case KS_Prior:
1027 	case KS_KP_Prior:
1028 		*out = "\033[216z";
1029 		return (6);
1030 	case KS_Next:
1031 	case KS_KP_Next:
1032 		*out = "\033[222z";
1033 		return (6);
1034 	case KS_Up:
1035 	case KS_KP_Up:
1036 		*out = "\033[A";
1037 		return (3);
1038 	case KS_Down:
1039 	case KS_KP_Down:
1040 		*out = "\033[B";
1041 		return (3);
1042 	case KS_Left:
1043 	case KS_KP_Left:
1044 		*out = "\033[D";
1045 		return (3);
1046 	case KS_Right:
1047 	case KS_KP_Right:
1048 		*out = "\033[C";
1049 		return (3);
1050 	case KS_KP_Delete:
1051 		*out = "\177";
1052 		return (1);
1053 	}
1054 	return (0);
1055 }
1056 
1057 void
1058 wsemul_sun_detach(void *cookie, u_int *crowp, u_int *ccolp)
1059 {
1060 	struct wsemul_sun_emuldata *edp = cookie;
1061 
1062 	*crowp = edp->crow;
1063 	*ccolp = edp->ccol;
1064 	if (edp != &wsemul_sun_console_emuldata)
1065 		free(edp, M_DEVBUF, sizeof *edp);
1066 }
1067 
1068 void
1069 wsemul_sun_resetop(void *cookie, enum wsemul_resetops op)
1070 {
1071 	struct wsemul_sun_emuldata *edp = cookie;
1072 
1073 	switch (op) {
1074 	case WSEMUL_RESET:
1075 		wsemul_sun_reset(edp);
1076 		break;
1077 	case WSEMUL_CLEARSCREEN:
1078 		(*edp->emulops->eraserows)(edp->emulcookie, 0, edp->nrows,
1079 		    edp->bkgdattr);
1080 		edp->ccol = edp->crow = 0;
1081 		(*edp->emulops->cursor)(edp->emulcookie, 1, 0, 0);
1082 		break;
1083 	case WSEMUL_CLEARCURSOR:
1084 		(*edp->emulops->cursor)(edp->emulcookie, 0, edp->crow,
1085 		    edp->ccol);
1086 		break;
1087 	default:
1088 		break;
1089 	}
1090 }
1091 
1092 int
1093 wsemul_sun_scrollup(struct wsemul_sun_emuldata *edp, u_int lines)
1094 {
1095 	int rc;
1096 
1097 	/*
1098 	 * if we're in wrap-around mode, go to the first
1099 	 * line and clear it.
1100 	 */
1101 	if (lines == 0) {
1102 		WSEMULOP(rc, edp, &edp->abortstate, eraserows,
1103 		    (edp->emulcookie, 0, 1, edp->bkgdattr));
1104 		if (rc != 0)
1105 			return rc;
1106 
1107 		edp->crow = 0;
1108 		return 0;
1109 	}
1110 
1111 	/*
1112 	 * If the scrolling distance is equal to the screen height
1113 	 * (usually 34), clear the screen; otherwise, scroll by the
1114 	 * scrolling distance.
1115 	 */
1116 	if (lines < edp->nrows) {
1117 		WSEMULOP(rc, edp, &edp->abortstate, copyrows,
1118 		    (edp->emulcookie, lines, 0, edp->nrows - lines));
1119 		if (rc != 0)
1120 			return rc;
1121 	}
1122 	WSEMULOP(rc, edp, &edp->abortstate, eraserows,
1123 	    (edp->emulcookie, edp->nrows - lines, lines, edp->bkgdattr));
1124 	if (rc != 0)
1125 		return rc;
1126 
1127 	edp->crow -= lines - 1;
1128 
1129 	return 0;
1130 }
1131