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