9970 loader: fix EFI getchar() for multiple consoles
[unleashed.git] / usr / src / boot / sys / boot / efi / libefi / efi_console.c
blob4bf1944a321a3056ce939cd6647d5822f0d2adbf
1 /*
2 * Copyright (c) 2000 Doug Rabson
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
27 #include <sys/cdefs.h>
29 #include <efi.h>
30 #include <efilib.h>
32 #include "bootstrap.h"
34 static SIMPLE_TEXT_OUTPUT_INTERFACE *conout;
35 static SIMPLE_INPUT_INTERFACE *conin;
37 #ifdef TERM_EMU
38 #define DEFAULT_FGCOLOR EFI_LIGHTGRAY
39 #define DEFAULT_BGCOLOR EFI_BLACK
41 #define MAXARGS 8
42 #define KEYBUFSZ 10
43 static unsigned keybuf[KEYBUFSZ]; /* keybuf for extended codes */
45 static int pending;
47 static int args[MAXARGS], argc;
48 static int fg_c, bg_c;
49 static UINTN curx, cury;
50 static int esc;
52 void get_pos(UINTN *x, UINTN *y);
53 void curs_move(UINTN *_x, UINTN *_y, UINTN x, UINTN y);
54 static void CL(int);
55 void HO(void);
56 void end_term(void);
57 #endif
59 static void efi_cons_probe(struct console *);
60 static int efi_cons_init(struct console *, int);
61 void efi_cons_putchar(struct console *, int);
62 int efi_cons_getchar(struct console *);
63 void efi_cons_efiputchar(int);
64 int efi_cons_poll(struct console *);
66 struct console efi_console = {
67 "text",
68 "EFI console",
69 C_WIDEOUT,
70 efi_cons_probe,
71 efi_cons_init,
72 efi_cons_putchar,
73 efi_cons_getchar,
74 efi_cons_poll,
78 #ifdef TERM_EMU
80 /* Get cursor position. */
81 void
82 get_pos(UINTN *x, UINTN *y)
84 *x = conout->Mode->CursorColumn;
85 *y = conout->Mode->CursorRow;
88 /* Move cursor to x rows and y cols (0-based). */
89 void
90 curs_move(UINTN *_x, UINTN *_y, UINTN x, UINTN y)
92 conout->SetCursorPosition(conout, x, y);
93 if (_x != NULL)
94 *_x = conout->Mode->CursorColumn;
95 if (_y != NULL)
96 *_y = conout->Mode->CursorRow;
99 /* Clear internal state of the terminal emulation code. */
100 void
101 end_term(void)
103 esc = 0;
104 argc = -1;
107 #endif
109 static void
110 efi_cons_probe(struct console *cp)
112 conout = ST->ConOut;
113 conin = ST->ConIn;
114 cp->c_flags |= C_PRESENTIN | C_PRESENTOUT;
117 static int
118 efi_cons_init(struct console *cp __attribute((unused)),
119 int arg __attribute((unused)))
121 conout->SetAttribute(conout, EFI_TEXT_ATTR(DEFAULT_FGCOLOR,
122 DEFAULT_BGCOLOR));
123 #ifdef TERM_EMU
124 end_term();
125 get_pos(&curx, &cury);
126 curs_move(&curx, &cury, curx, cury);
127 fg_c = DEFAULT_FGCOLOR;
128 bg_c = DEFAULT_BGCOLOR;
129 memset(keybuf, 0, KEYBUFSZ);
130 #endif
131 conout->EnableCursor(conout, TRUE);
132 return 0;
135 static void
136 efi_cons_rawputchar(int c)
138 int i;
139 UINTN x, y;
140 conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
141 static int ignorenl = 0;
143 if (c == '\t')
144 /* XXX lame tab expansion */
145 for (i = 0; i < 8; i++)
146 efi_cons_rawputchar(' ');
147 else {
148 #ifndef TERM_EMU
149 if (c == '\n')
150 efi_cons_efiputchar('\r');
151 else
152 efi_cons_efiputchar(c);
153 #else
154 switch (c) {
155 case '\r':
156 curx = 0;
157 break;
158 case '\n':
159 if (ignorenl)
160 ignorenl = 0;
161 else
162 cury++;
163 if ((efi_console.c_flags & C_MODERAW) == 0)
164 curx = 0;
165 if (cury >= y) {
166 efi_cons_efiputchar('\n');
167 cury--;
169 break;
170 case '\b':
171 if (curx > 0)
172 curx--;
173 break;
174 default:
175 if (curx > x) {
176 curx = 0;
177 cury++;
178 curs_move(&curx, &cury, curx, cury);
180 if ((efi_console.c_flags & C_MODERAW) == 0) {
181 if (cury > y-1) {
182 curx = 0;
183 efi_cons_efiputchar('\n');
184 cury--;
185 curs_move(&curx, &cury, curx, cury);
188 efi_cons_efiputchar(c);
189 curx++;
190 if ((efi_console.c_flags & C_MODERAW) == 0) {
191 if (curx == x) {
192 curx = 0;
193 ignorenl = 1;
195 } else if (curx == x) {
196 curx = 0;
197 if (cury == y)
198 efi_cons_efiputchar('\n');
199 else
200 cury++;
203 curs_move(&curx, &cury, curx, cury);
204 #endif
208 /* Gracefully exit ESC-sequence processing in case of misunderstanding. */
209 static void
210 bail_out(int c)
212 char buf[16], *ch;
213 int i;
215 if (esc) {
216 efi_cons_rawputchar('\033');
217 if (esc != '\033')
218 efi_cons_rawputchar(esc);
219 for (i = 0; i <= argc; ++i) {
220 sprintf(buf, "%d", args[i]);
221 ch = buf;
222 while (*ch)
223 efi_cons_rawputchar(*ch++);
226 efi_cons_rawputchar(c);
227 end_term();
230 /* Clear display from current position to end of screen. */
231 static void
232 CD(void) {
233 UINTN i, x, y;
235 get_pos(&curx, &cury);
236 if (curx == 0 && cury == 0) {
237 conout->ClearScreen(conout);
238 end_term();
239 return;
242 conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
243 CL(0); /* clear current line from cursor to end */
244 for (i = cury + 1; i < y-1; i++) {
245 curs_move(NULL, NULL, 0, i);
246 CL(0);
248 curs_move(NULL, NULL, curx, cury);
249 end_term();
253 * Absolute cursor move to args[0] rows and args[1] columns
254 * (the coordinates are 1-based).
256 static void
257 CM(void)
259 if (args[0] > 0)
260 args[0]--;
261 if (args[1] > 0)
262 args[1]--;
263 curs_move(&curx, &cury, args[1], args[0]);
264 end_term();
267 /* Home cursor (left top corner), also called from mode command. */
268 void
269 HO(void)
271 argc = 1;
272 args[0] = args[1] = 1;
273 CM();
276 /* Clear line from current position to end of line */
277 static void
278 CL(int direction)
280 int i, len;
281 UINTN x, y;
282 CHAR16 *line;
284 conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
285 switch (direction) {
286 case 0: /* from cursor to end */
287 len = x - curx + 1;
288 break;
289 case 1: /* from beginning to cursor */
290 len = curx;
291 break;
292 case 2: /* entire line */
293 default:
294 len = x;
295 break;
298 if (cury == y - 1)
299 len--;
301 line = malloc(len * sizeof (CHAR16));
302 if (line == NULL) {
303 printf("out of memory\n");
304 return;
306 for (i = 0; i < len; i++)
307 line[i] = ' ';
308 line[len-1] = 0;
310 if (direction != 0)
311 curs_move(NULL, NULL, 0, cury);
313 conout->OutputString(conout, line);
314 /* restore cursor position */
315 curs_move(NULL, NULL, curx, cury);
316 free(line);
317 end_term();
320 static void
321 get_arg(int c)
323 if (argc < 0)
324 argc = 0;
325 args[argc] *= 10;
326 args[argc] += c - '0';
329 /* Emulate basic capabilities of sun-color terminal */
330 static void
331 efi_term_emu(int c)
333 static int ansi_col[] = {
334 0, 4, 2, 6, 1, 5, 3, 7
336 int t, i;
338 switch (esc) {
339 case 0:
340 switch (c) {
341 case '\033':
342 esc = c;
343 break;
344 default:
345 efi_cons_rawputchar(c);
346 break;
348 break;
349 case '\033':
350 switch (c) {
351 case '[':
352 esc = c;
353 args[0] = 0;
354 argc = -1;
355 break;
356 default:
357 bail_out(c);
358 break;
360 break;
361 case '[':
362 switch (c) {
363 case ';':
364 if (argc < 0)
365 argc = 0;
366 else if (argc + 1 >= MAXARGS)
367 bail_out(c);
368 else
369 args[++argc] = 0;
370 break;
371 case 'A': /* UP = \E[%dA */
372 if (argc == 0) {
373 UINTN x, y;
374 get_pos(&x, &y);
375 args[1] = x + 1;
376 args[0] = y - args[0] + 1;
377 CM();
378 } else
379 bail_out(c);
380 break;
381 case 'B': /* DO = \E[%dB */
382 if (argc == 0) {
383 UINTN x, y;
384 get_pos(&x, &y);
385 args[1] = x + 1;
386 args[0] = y + args[0] + 1;
387 CM();
388 } else
389 bail_out(c);
390 break;
391 case 'C': /* RI = \E[%dC */
392 if (argc == 0) {
393 UINTN x, y;
394 get_pos(&x, &y);
395 args[1] = args[0] + 1;
396 args[0] = y + 1;
397 CM();
398 } else
399 bail_out(c);
400 break;
401 case 'H': /* ho = \E[H */
402 if (argc < 0)
403 HO();
404 else if (argc == 1)
405 CM();
406 else
407 bail_out(c);
408 break;
409 case 'J': /* cd = \E[J */
410 if (argc < 0)
411 CD();
412 else
413 bail_out(c);
414 break;
415 case 'K':
416 if (argc < 0)
417 CL(0);
418 else if (argc == 0)
419 switch (args[0]) {
420 case 0:
421 case 1:
422 case 2:
423 CL(args[0]);
424 break;
425 default:
426 bail_out(c);
428 else
429 bail_out(c);
430 break;
431 case 'm':
432 if (argc < 0) {
433 fg_c = DEFAULT_FGCOLOR;
434 bg_c = DEFAULT_BGCOLOR;
436 for (i = 0; i <= argc; ++i) {
437 switch (args[i]) {
438 case 0: /* back to normal */
439 fg_c = DEFAULT_FGCOLOR;
440 bg_c = DEFAULT_BGCOLOR;
441 break;
442 case 1: /* bold */
443 fg_c |= 0x8;
444 break;
445 case 4: /* underline */
446 case 5: /* blink */
447 bg_c |= 0x8;
448 break;
449 case 7: /* reverse */
450 t = fg_c;
451 fg_c = bg_c;
452 bg_c = t;
453 break;
454 case 30: case 31: case 32: case 33:
455 case 34: case 35: case 36: case 37:
456 fg_c = ansi_col[args[i] - 30];
457 break;
458 case 39: /* normal */
459 fg_c = DEFAULT_FGCOLOR;
460 break;
461 case 40: case 41: case 42: case 43:
462 case 44: case 45: case 46: case 47:
463 bg_c = ansi_col[args[i] - 40];
464 break;
465 case 49: /* normal */
466 bg_c = DEFAULT_BGCOLOR;
467 break;
470 conout->SetAttribute(conout, EFI_TEXT_ATTR(fg_c, bg_c));
471 end_term();
472 break;
473 default:
474 if (isdigit(c))
475 get_arg(c);
476 else
477 bail_out(c);
478 break;
480 break;
481 default:
482 bail_out(c);
483 break;
487 void
488 efi_cons_putchar(struct console *cp __attribute((unused)), int c)
490 #ifdef TERM_EMU
491 efi_term_emu(c);
492 #else
493 efi_cons_rawputchar(c);
494 #endif
498 efi_cons_getchar(struct console *cp __attribute((unused)))
500 EFI_INPUT_KEY key;
501 EFI_STATUS status;
502 int i, c;
504 for (i = 0; i < KEYBUFSZ; i++) {
505 if (keybuf[i] != 0) {
506 c = keybuf[i];
507 keybuf[i] = 0;
508 return (c);
512 pending = 0;
514 status = conin->ReadKeyStroke(conin, &key);
515 if (status == EFI_NOT_READY)
516 return (-1);
518 switch (key.ScanCode) {
519 case 0x1: /* UP */
520 keybuf[0] = '[';
521 keybuf[1] = 'A';
522 return (0x1b); /* esc */
523 case 0x2: /* DOWN */
524 keybuf[0] = '[';
525 keybuf[1] = 'B';
526 return (0x1b); /* esc */
527 case 0x3: /* RIGHT */
528 keybuf[0] = '[';
529 keybuf[1] = 'C';
530 return (0x1b); /* esc */
531 case 0x4: /* LEFT */
532 keybuf[0] = '[';
533 keybuf[1] = 'D';
534 return (0x1b); /* esc */
535 case 0x17: /* ESC */
536 return (0x1b); /* esc */
539 /* this can return */
540 return (key.UnicodeChar);
544 efi_cons_poll(struct console *cp __attribute((unused)))
546 int i;
548 for (i = 0; i < KEYBUFSZ; i++) {
549 if (keybuf[i] != 0)
550 return (1);
553 if (pending)
554 return (1);
556 /* This can clear the signaled state. */
557 pending = BS->CheckEvent(conin->WaitForKey) == EFI_SUCCESS;
559 return (pending);
562 /* Plain direct access to EFI OutputString(). */
563 void
564 efi_cons_efiputchar(int c)
566 CHAR16 buf[2];
569 * translate box chars to unicode
571 switch (c) {
572 /* single frame */
573 case 0xb3: buf[0] = BOXDRAW_VERTICAL; break;
574 case 0xbf: buf[0] = BOXDRAW_DOWN_LEFT; break;
575 case 0xc0: buf[0] = BOXDRAW_UP_RIGHT; break;
576 case 0xc4: buf[0] = BOXDRAW_HORIZONTAL; break;
577 case 0xda: buf[0] = BOXDRAW_DOWN_RIGHT; break;
578 case 0xd9: buf[0] = BOXDRAW_UP_LEFT; break;
580 /* double frame */
581 case 0xba: buf[0] = BOXDRAW_DOUBLE_VERTICAL; break;
582 case 0xbb: buf[0] = BOXDRAW_DOUBLE_DOWN_LEFT; break;
583 case 0xbc: buf[0] = BOXDRAW_DOUBLE_UP_LEFT; break;
584 case 0xc8: buf[0] = BOXDRAW_DOUBLE_UP_RIGHT; break;
585 case 0xc9: buf[0] = BOXDRAW_DOUBLE_DOWN_RIGHT; break;
586 case 0xcd: buf[0] = BOXDRAW_DOUBLE_HORIZONTAL; break;
588 default:
589 buf[0] = c;
591 buf[1] = 0; /* terminate string */
593 conout->OutputString(conout, buf);