loader.efi: add terminal emulation support
This is based on the vidconsole implementation. Submitted by: Toomas Soome <tsoome@me.com> Reviewed by: adrian MFC after: 2 weeks Relnotes: Yes Differential Revision: https://reviews.freebsd.org/D4797
This commit is contained in:
parent
35c3347b0a
commit
89b04b0f99
@ -21,5 +21,6 @@ CFLAGS+= -I${.CURDIR}/../../common
|
||||
|
||||
# Handle FreeBSD specific %b and %D printf format specifiers
|
||||
CFLAGS+= ${FORMAT_EXTENSIONS}
|
||||
CFLAGS+= -DTERM_EMU
|
||||
|
||||
.include <bsd.lib.mk>
|
||||
|
@ -35,6 +35,69 @@ __FBSDID("$FreeBSD$");
|
||||
static SIMPLE_TEXT_OUTPUT_INTERFACE *conout;
|
||||
static SIMPLE_INPUT_INTERFACE *conin;
|
||||
|
||||
#ifdef TERM_EMU
|
||||
#define DEFAULT_FGCOLOR EFI_LIGHTGRAY
|
||||
#define DEFAULT_BGCOLOR EFI_BLACK
|
||||
|
||||
#define MAXARGS 8
|
||||
static int args[MAXARGS], argc;
|
||||
static int fg_c, bg_c, curx, cury;
|
||||
static int esc;
|
||||
|
||||
void get_pos(int *x, int *y);
|
||||
void curs_move(int *_x, int *_y, int x, int y);
|
||||
static void CL(int);
|
||||
#endif
|
||||
|
||||
static void efi_cons_probe(struct console *);
|
||||
static int efi_cons_init(int);
|
||||
void efi_cons_putchar(int);
|
||||
int efi_cons_getchar(void);
|
||||
void efi_cons_efiputchar(int);
|
||||
int efi_cons_poll(void);
|
||||
|
||||
struct console efi_console = {
|
||||
"efi",
|
||||
"EFI console",
|
||||
0,
|
||||
efi_cons_probe,
|
||||
efi_cons_init,
|
||||
efi_cons_putchar,
|
||||
efi_cons_getchar,
|
||||
efi_cons_poll
|
||||
};
|
||||
|
||||
#ifdef TERM_EMU
|
||||
|
||||
/* Get cursor position. */
|
||||
void
|
||||
get_pos(int *x, int *y)
|
||||
{
|
||||
*x = conout->Mode->CursorColumn;
|
||||
*y = conout->Mode->CursorRow;
|
||||
}
|
||||
|
||||
/* Move cursor to x rows and y cols (0-based). */
|
||||
void
|
||||
curs_move(int *_x, int *_y, int x, int y)
|
||||
{
|
||||
conout->SetCursorPosition(conout, x, y);
|
||||
if (_x != NULL)
|
||||
*_x = conout->Mode->CursorColumn;
|
||||
if (_y != NULL)
|
||||
*_y = conout->Mode->CursorRow;
|
||||
}
|
||||
|
||||
/* Clear internal state of the terminal emulation code. */
|
||||
void
|
||||
end_term(void)
|
||||
{
|
||||
esc = 0;
|
||||
argc = -1;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static void
|
||||
efi_cons_probe(struct console *cp)
|
||||
{
|
||||
@ -46,22 +109,314 @@ efi_cons_probe(struct console *cp)
|
||||
static int
|
||||
efi_cons_init(int arg)
|
||||
{
|
||||
conout->SetAttribute(conout, EFI_TEXT_ATTR(EFI_LIGHTGRAY, EFI_BLACK));
|
||||
conout->SetAttribute(conout, EFI_TEXT_ATTR(DEFAULT_FGCOLOR,
|
||||
DEFAULT_BGCOLOR));
|
||||
#ifdef TERM_EMU
|
||||
end_term();
|
||||
get_pos(&curx, &cury);
|
||||
curs_move(&curx, &cury, curx, cury);
|
||||
fg_c = DEFAULT_FGCOLOR;
|
||||
bg_c = DEFAULT_BGCOLOR;
|
||||
#endif
|
||||
conout->EnableCursor(conout, TRUE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
efi_cons_rawputchar(int c)
|
||||
{
|
||||
int i;
|
||||
UINTN x, y;
|
||||
conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
|
||||
|
||||
if (c == '\t')
|
||||
/* XXX lame tab expansion */
|
||||
for (i = 0; i < 8; i++)
|
||||
efi_cons_rawputchar(' ');
|
||||
else {
|
||||
#ifndef TERM_EMU
|
||||
if (c == '\n')
|
||||
efi_cons_efiputchar('\r');
|
||||
else
|
||||
efi_cons_efiputchar(c);
|
||||
#else
|
||||
switch (c) {
|
||||
case '\r':
|
||||
curx = 0;
|
||||
curs_move(&curx, &cury, curx, cury);
|
||||
return;
|
||||
case '\n':
|
||||
cury++;
|
||||
if (cury >= y) {
|
||||
efi_cons_efiputchar('\n');
|
||||
cury--;
|
||||
} else
|
||||
curs_move(&curx, &cury, curx, cury);
|
||||
return;
|
||||
case '\b':
|
||||
if (curx > 0) {
|
||||
curx--;
|
||||
curs_move(&curx, &cury, curx, cury);
|
||||
}
|
||||
return;
|
||||
default:
|
||||
efi_cons_efiputchar(c);
|
||||
curx++;
|
||||
if (curx > x-1) {
|
||||
curx = 0;
|
||||
cury++;
|
||||
}
|
||||
if (cury > y-1) {
|
||||
curx = 0;
|
||||
cury--;
|
||||
}
|
||||
}
|
||||
curs_move(&curx, &cury, curx, cury);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/* Gracefully exit ESC-sequence processing in case of misunderstanding. */
|
||||
static void
|
||||
bail_out(int c)
|
||||
{
|
||||
char buf[16], *ch;
|
||||
int i;
|
||||
|
||||
if (esc) {
|
||||
efi_cons_rawputchar('\033');
|
||||
if (esc != '\033')
|
||||
efi_cons_rawputchar(esc);
|
||||
for (i = 0; i <= argc; ++i) {
|
||||
sprintf(buf, "%d", args[i]);
|
||||
ch = buf;
|
||||
while (*ch)
|
||||
efi_cons_rawputchar(*ch++);
|
||||
}
|
||||
}
|
||||
efi_cons_rawputchar(c);
|
||||
end_term();
|
||||
}
|
||||
|
||||
/* Clear display from current position to end of screen. */
|
||||
static void
|
||||
CD(void) {
|
||||
int i;
|
||||
UINTN x, y;
|
||||
|
||||
get_pos(&curx, &cury);
|
||||
if (curx == 0 && cury == 0) {
|
||||
conout->ClearScreen(conout);
|
||||
end_term();
|
||||
return;
|
||||
}
|
||||
|
||||
conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
|
||||
CL(0); /* clear current line from cursor to end */
|
||||
for (i = cury + 1; i < y-1; i++) {
|
||||
curs_move(NULL, NULL, 0, i);
|
||||
CL(0);
|
||||
}
|
||||
curs_move(NULL, NULL, curx, cury);
|
||||
end_term();
|
||||
}
|
||||
|
||||
/*
|
||||
* Absolute cursor move to args[0] rows and args[1] columns
|
||||
* (the coordinates are 1-based).
|
||||
*/
|
||||
static void
|
||||
CM(void)
|
||||
{
|
||||
if (args[0] > 0)
|
||||
args[0]--;
|
||||
if (args[1] > 0)
|
||||
args[1]--;
|
||||
curs_move(&curx, &cury, args[1], args[0]);
|
||||
end_term();
|
||||
}
|
||||
|
||||
/* Home cursor (left top corner), also called from mode command. */
|
||||
void
|
||||
HO(void)
|
||||
{
|
||||
argc = 1;
|
||||
args[0] = args[1] = 1;
|
||||
CM();
|
||||
}
|
||||
|
||||
/* Clear line from current position to end of line */
|
||||
static void
|
||||
CL(int direction)
|
||||
{
|
||||
int i, len;
|
||||
UINTN x, y;
|
||||
CHAR16 *line;
|
||||
|
||||
conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
|
||||
switch (direction) {
|
||||
case 0: /* from cursor to end */
|
||||
len = x - curx + 1;
|
||||
break;
|
||||
case 1: /* from beginning to cursor */
|
||||
len = curx;
|
||||
break;
|
||||
case 2: /* entire line */
|
||||
len = x;
|
||||
break;
|
||||
}
|
||||
|
||||
if (cury == y - 1)
|
||||
len--;
|
||||
|
||||
line = malloc(len * sizeof (CHAR16));
|
||||
if (line == NULL) {
|
||||
printf("out of memory\n");
|
||||
return;
|
||||
}
|
||||
for (i = 0; i < len; i++)
|
||||
line[i] = ' ';
|
||||
line[len-1] = 0;
|
||||
|
||||
if (direction != 0)
|
||||
curs_move(NULL, NULL, 0, cury);
|
||||
|
||||
conout->OutputString(conout, line);
|
||||
/* restore cursor position */
|
||||
curs_move(NULL, NULL, curx, cury);
|
||||
free(line);
|
||||
end_term();
|
||||
}
|
||||
|
||||
static void
|
||||
get_arg(int c)
|
||||
{
|
||||
if (argc < 0)
|
||||
argc = 0;
|
||||
args[argc] *= 10;
|
||||
args[argc] += c - '0';
|
||||
}
|
||||
|
||||
/* Emulate basic capabilities of cons25 terminal */
|
||||
static void
|
||||
efi_term_emu(int c)
|
||||
{
|
||||
static int ansi_col[] = {
|
||||
0, 4, 2, 6, 1, 5, 3, 7
|
||||
};
|
||||
int t, i;
|
||||
|
||||
switch (esc) {
|
||||
case 0:
|
||||
switch (c) {
|
||||
case '\033':
|
||||
esc = c;
|
||||
break;
|
||||
default:
|
||||
efi_cons_rawputchar(c);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case '\033':
|
||||
switch (c) {
|
||||
case '[':
|
||||
esc = c;
|
||||
args[0] = 0;
|
||||
argc = -1;
|
||||
break;
|
||||
default:
|
||||
bail_out(c);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case '[':
|
||||
switch (c) {
|
||||
case ';':
|
||||
if (argc < 0)
|
||||
argc = 0;
|
||||
else if (argc + 1 >= MAXARGS)
|
||||
bail_out(c);
|
||||
else
|
||||
args[++argc] = 0;
|
||||
break;
|
||||
case 'H': /* ho = \E[H */
|
||||
if (argc < 0)
|
||||
HO();
|
||||
else if (argc == 1)
|
||||
CM();
|
||||
else
|
||||
bail_out(c);
|
||||
break;
|
||||
case 'J': /* cd = \E[J */
|
||||
if (argc < 0)
|
||||
CD();
|
||||
else
|
||||
bail_out(c);
|
||||
break;
|
||||
case 'm':
|
||||
if (argc < 0) {
|
||||
fg_c = DEFAULT_FGCOLOR;
|
||||
bg_c = DEFAULT_BGCOLOR;
|
||||
}
|
||||
for (i = 0; i <= argc; ++i) {
|
||||
switch (args[i]) {
|
||||
case 0: /* back to normal */
|
||||
fg_c = DEFAULT_FGCOLOR;
|
||||
bg_c = DEFAULT_BGCOLOR;
|
||||
break;
|
||||
case 1: /* bold */
|
||||
fg_c |= 0x8;
|
||||
break;
|
||||
case 4: /* underline */
|
||||
case 5: /* blink */
|
||||
bg_c |= 0x8;
|
||||
break;
|
||||
case 7: /* reverse */
|
||||
t = fg_c;
|
||||
fg_c = bg_c;
|
||||
bg_c = t;
|
||||
break;
|
||||
case 30: case 31: case 32: case 33:
|
||||
case 34: case 35: case 36: case 37:
|
||||
fg_c = ansi_col[args[i] - 30];
|
||||
break;
|
||||
case 39: /* normal */
|
||||
fg_c = DEFAULT_FGCOLOR;
|
||||
break;
|
||||
case 40: case 41: case 42: case 43:
|
||||
case 44: case 45: case 46: case 47:
|
||||
bg_c = ansi_col[args[i] - 40];
|
||||
break;
|
||||
case 49: /* normal */
|
||||
bg_c = DEFAULT_BGCOLOR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
conout->SetAttribute(conout, EFI_TEXT_ATTR(fg_c, bg_c));
|
||||
end_term();
|
||||
break;
|
||||
default:
|
||||
if (isdigit(c))
|
||||
get_arg(c);
|
||||
else
|
||||
bail_out(c);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
bail_out(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
efi_cons_putchar(int c)
|
||||
{
|
||||
CHAR16 buf[2];
|
||||
|
||||
if (c == '\n')
|
||||
efi_cons_putchar('\r');
|
||||
|
||||
buf[0] = c;
|
||||
buf[1] = 0;
|
||||
|
||||
conout->OutputString(conout, buf);
|
||||
#ifdef TERM_EMU
|
||||
efi_term_emu(c);
|
||||
#else
|
||||
efi_cons_rawputchar(c);
|
||||
#endif
|
||||
}
|
||||
|
||||
int
|
||||
@ -77,6 +432,12 @@ efi_cons_getchar()
|
||||
BS->WaitForEvent(1, &conin->WaitForKey, &junk);
|
||||
status = conin->ReadKeyStroke(conin, &key);
|
||||
}
|
||||
switch (key.ScanCode) {
|
||||
case 0x17: /* ESC */
|
||||
return (0x1b); /* esc */
|
||||
}
|
||||
|
||||
/* this can return */
|
||||
return (key.UnicodeChar);
|
||||
}
|
||||
|
||||
@ -87,13 +448,36 @@ efi_cons_poll()
|
||||
return (BS->CheckEvent(conin->WaitForKey) == EFI_SUCCESS);
|
||||
}
|
||||
|
||||
struct console efi_console = {
|
||||
"efi",
|
||||
"EFI console",
|
||||
0,
|
||||
efi_cons_probe,
|
||||
efi_cons_init,
|
||||
efi_cons_putchar,
|
||||
efi_cons_getchar,
|
||||
efi_cons_poll
|
||||
};
|
||||
/* Plain direct access to EFI OutputString(). */
|
||||
void
|
||||
efi_cons_efiputchar(int c)
|
||||
{
|
||||
CHAR16 buf[2];
|
||||
|
||||
/*
|
||||
* translate box chars to unicode
|
||||
*/
|
||||
switch (c) {
|
||||
/* single frame */
|
||||
case 0xb3: buf[0] = BOXDRAW_VERTICAL; break;
|
||||
case 0xbf: buf[0] = BOXDRAW_DOWN_LEFT; break;
|
||||
case 0xc0: buf[0] = BOXDRAW_UP_RIGHT; break;
|
||||
case 0xc4: buf[0] = BOXDRAW_HORIZONTAL; break;
|
||||
case 0xda: buf[0] = BOXDRAW_DOWN_RIGHT; break;
|
||||
case 0xd9: buf[0] = BOXDRAW_UP_LEFT; break;
|
||||
|
||||
/* double frame */
|
||||
case 0xba: buf[0] = BOXDRAW_DOUBLE_VERTICAL; break;
|
||||
case 0xbb: buf[0] = BOXDRAW_DOUBLE_DOWN_LEFT; break;
|
||||
case 0xbc: buf[0] = BOXDRAW_DOUBLE_UP_LEFT; break;
|
||||
case 0xc8: buf[0] = BOXDRAW_DOUBLE_UP_RIGHT; break;
|
||||
case 0xc9: buf[0] = BOXDRAW_DOUBLE_DOWN_RIGHT; break;
|
||||
case 0xcd: buf[0] = BOXDRAW_DOUBLE_HORIZONTAL; break;
|
||||
|
||||
default:
|
||||
buf[0] = c;
|
||||
}
|
||||
buf[1] = 0; /* terminate string */
|
||||
|
||||
conout->OutputString(conout, buf);
|
||||
}
|
||||
|
@ -334,6 +334,7 @@ command_mode(int argc, char *argv[])
|
||||
char rowenv[8];
|
||||
EFI_STATUS status;
|
||||
SIMPLE_TEXT_OUTPUT_INTERFACE *conout;
|
||||
extern void HO(void);
|
||||
|
||||
conout = ST->ConOut;
|
||||
|
||||
@ -355,7 +356,7 @@ command_mode(int argc, char *argv[])
|
||||
}
|
||||
sprintf(rowenv, "%u", (unsigned)rows);
|
||||
setenv("LINES", rowenv, 1);
|
||||
|
||||
HO(); /* set cursor */
|
||||
return (CMD_OK);
|
||||
}
|
||||
|
||||
|
@ -55,7 +55,7 @@ void ficlTextOut(FICL_VM *pVM, char *msg, int fNewline)
|
||||
IGNORE(pVM);
|
||||
|
||||
while(*msg != 0)
|
||||
putchar(*(msg++));
|
||||
putchar((unsigned char)*(msg++));
|
||||
if (fNewline)
|
||||
putchar('\n');
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user