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