becaac3972
Calculate font size from 16 density independent pixels (dp) by using: size = 16 * ppi/160 * display_factor We are specifying font size 16dp, and assuming 1dp = 160ppi. Also apply scaling factor 2 (display_factor). MFC after: 1 week Differential Revision: https://reviews.freebsd.org/D28849
1396 lines
30 KiB
C
1396 lines
30 KiB
C
/*-
|
|
* Copyright (c) 2000 Doug Rabson
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
#include <sys/param.h>
|
|
#include <efi.h>
|
|
#include <efilib.h>
|
|
#include <teken.h>
|
|
#include <sys/reboot.h>
|
|
#include <machine/metadata.h>
|
|
#include <gfx_fb.h>
|
|
#include <framebuffer.h>
|
|
#include "bootstrap.h"
|
|
|
|
extern EFI_GUID gop_guid;
|
|
static EFI_GUID simple_input_ex_guid = EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID;
|
|
static SIMPLE_TEXT_OUTPUT_INTERFACE *conout;
|
|
static SIMPLE_INPUT_INTERFACE *conin;
|
|
static EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *coninex;
|
|
static bool efi_started;
|
|
|
|
static int mode; /* Does ConOut have serial console? */
|
|
|
|
static uint32_t utf8_left;
|
|
static uint32_t utf8_partial;
|
|
#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);
|
|
void HO(void);
|
|
void end_term(void);
|
|
#endif
|
|
|
|
#define TEXT_ROWS 24
|
|
#define TEXT_COLS 80
|
|
|
|
static tf_bell_t efi_cons_bell;
|
|
static tf_cursor_t efi_text_cursor;
|
|
static tf_putchar_t efi_text_putchar;
|
|
static tf_fill_t efi_text_fill;
|
|
static tf_copy_t efi_text_copy;
|
|
static tf_param_t efi_text_param;
|
|
static tf_respond_t efi_cons_respond;
|
|
|
|
static teken_funcs_t tf = {
|
|
.tf_bell = efi_cons_bell,
|
|
.tf_cursor = efi_text_cursor,
|
|
.tf_putchar = efi_text_putchar,
|
|
.tf_fill = efi_text_fill,
|
|
.tf_copy = efi_text_copy,
|
|
.tf_param = efi_text_param,
|
|
.tf_respond = efi_cons_respond,
|
|
};
|
|
|
|
static teken_funcs_t tfx = {
|
|
.tf_bell = efi_cons_bell,
|
|
.tf_cursor = gfx_fb_cursor,
|
|
.tf_putchar = gfx_fb_putchar,
|
|
.tf_fill = gfx_fb_fill,
|
|
.tf_copy = gfx_fb_copy,
|
|
.tf_param = gfx_fb_param,
|
|
.tf_respond = efi_cons_respond,
|
|
};
|
|
|
|
#define KEYBUFSZ 10
|
|
static unsigned keybuf[KEYBUFSZ]; /* keybuf for extended codes */
|
|
static int key_pending;
|
|
|
|
static const unsigned char teken_color_to_efi_color[16] = {
|
|
EFI_BLACK,
|
|
EFI_RED,
|
|
EFI_GREEN,
|
|
EFI_BROWN,
|
|
EFI_BLUE,
|
|
EFI_MAGENTA,
|
|
EFI_CYAN,
|
|
EFI_LIGHTGRAY,
|
|
EFI_DARKGRAY,
|
|
EFI_LIGHTRED,
|
|
EFI_LIGHTGREEN,
|
|
EFI_YELLOW,
|
|
EFI_LIGHTBLUE,
|
|
EFI_LIGHTMAGENTA,
|
|
EFI_LIGHTCYAN,
|
|
EFI_WHITE
|
|
};
|
|
|
|
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);
|
|
static void cons_draw_frame(teken_attr_t *);
|
|
|
|
struct console efi_console = {
|
|
"efi",
|
|
"EFI console",
|
|
C_WIDEOUT,
|
|
efi_cons_probe,
|
|
efi_cons_init,
|
|
efi_cons_putchar,
|
|
efi_cons_getchar,
|
|
efi_cons_poll
|
|
};
|
|
|
|
/*
|
|
* This function is used to mark a rectangular image area so the scrolling
|
|
* will know we need to copy the data from there.
|
|
*/
|
|
void
|
|
term_image_display(teken_gfx_t *state, const teken_rect_t *r)
|
|
{
|
|
teken_pos_t p;
|
|
int idx;
|
|
|
|
if (screen_buffer == NULL)
|
|
return;
|
|
|
|
for (p.tp_row = r->tr_begin.tp_row;
|
|
p.tp_row < r->tr_end.tp_row; p.tp_row++) {
|
|
for (p.tp_col = r->tr_begin.tp_col;
|
|
p.tp_col < r->tr_end.tp_col; p.tp_col++) {
|
|
idx = p.tp_col + p.tp_row * state->tg_tp.tp_col;
|
|
if (idx >= state->tg_tp.tp_col * state->tg_tp.tp_row)
|
|
return;
|
|
screen_buffer[idx].a.ta_format |= TF_IMAGE;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Not implemented.
|
|
*/
|
|
static void
|
|
efi_cons_bell(void *s __unused)
|
|
{
|
|
}
|
|
|
|
static void
|
|
efi_text_cursor(void *arg, const teken_pos_t *p)
|
|
{
|
|
teken_gfx_t *state = arg;
|
|
UINTN col, row;
|
|
|
|
row = p->tp_row;
|
|
if (p->tp_row >= state->tg_tp.tp_row)
|
|
row = state->tg_tp.tp_row - 1;
|
|
|
|
col = p->tp_col;
|
|
if (p->tp_col >= state->tg_tp.tp_col)
|
|
col = state->tg_tp.tp_col - 1;
|
|
|
|
conout->SetCursorPosition(conout, col, row);
|
|
}
|
|
|
|
static void
|
|
efi_text_printchar(teken_gfx_t *state, const teken_pos_t *p, bool autoscroll)
|
|
{
|
|
UINTN a, attr;
|
|
struct text_pixel *px;
|
|
teken_color_t fg, bg, tmp;
|
|
|
|
px = screen_buffer + p->tp_col + p->tp_row * state->tg_tp.tp_col;
|
|
a = conout->Mode->Attribute;
|
|
|
|
fg = teken_256to16(px->a.ta_fgcolor);
|
|
bg = teken_256to16(px->a.ta_bgcolor);
|
|
if (px->a.ta_format & TF_BOLD)
|
|
fg |= TC_LIGHT;
|
|
if (px->a.ta_format & TF_BLINK)
|
|
bg |= TC_LIGHT;
|
|
|
|
if (px->a.ta_format & TF_REVERSE) {
|
|
tmp = fg;
|
|
fg = bg;
|
|
bg = tmp;
|
|
}
|
|
|
|
attr = EFI_TEXT_ATTR(teken_color_to_efi_color[fg],
|
|
teken_color_to_efi_color[bg] & 0x7);
|
|
|
|
conout->SetCursorPosition(conout, p->tp_col, p->tp_row);
|
|
|
|
/* to prevent autoscroll, skip print of lower right char */
|
|
if (!autoscroll &&
|
|
p->tp_row == state->tg_tp.tp_row - 1 &&
|
|
p->tp_col == state->tg_tp.tp_col - 1)
|
|
return;
|
|
|
|
(void) conout->SetAttribute(conout, attr);
|
|
efi_cons_efiputchar(px->c);
|
|
(void) conout->SetAttribute(conout, a);
|
|
}
|
|
|
|
static void
|
|
efi_text_putchar(void *s, const teken_pos_t *p, teken_char_t c,
|
|
const teken_attr_t *a)
|
|
{
|
|
teken_gfx_t *state = s;
|
|
EFI_STATUS status;
|
|
int idx;
|
|
|
|
idx = p->tp_col + p->tp_row * state->tg_tp.tp_col;
|
|
if (idx >= state->tg_tp.tp_col * state->tg_tp.tp_row)
|
|
return;
|
|
|
|
screen_buffer[idx].c = c;
|
|
screen_buffer[idx].a = *a;
|
|
|
|
efi_text_printchar(s, p, false);
|
|
}
|
|
|
|
static void
|
|
efi_text_fill(void *arg, const teken_rect_t *r, teken_char_t c,
|
|
const teken_attr_t *a)
|
|
{
|
|
teken_gfx_t *state = arg;
|
|
teken_pos_t p;
|
|
|
|
if (state->tg_cursor_visible)
|
|
conout->EnableCursor(conout, FALSE);
|
|
for (p.tp_row = r->tr_begin.tp_row; p.tp_row < r->tr_end.tp_row;
|
|
p.tp_row++)
|
|
for (p.tp_col = r->tr_begin.tp_col;
|
|
p.tp_col < r->tr_end.tp_col; p.tp_col++)
|
|
efi_text_putchar(state, &p, c, a);
|
|
if (state->tg_cursor_visible)
|
|
conout->EnableCursor(conout, TRUE);
|
|
}
|
|
|
|
static void
|
|
efi_text_copy_line(teken_gfx_t *state, int ncol, teken_pos_t *s,
|
|
teken_pos_t *d, bool scroll)
|
|
{
|
|
unsigned soffset, doffset;
|
|
teken_pos_t sp, dp;
|
|
int x;
|
|
|
|
soffset = s->tp_col + s->tp_row * state->tg_tp.tp_col;
|
|
doffset = d->tp_col + d->tp_row * state->tg_tp.tp_col;
|
|
|
|
sp = *s;
|
|
dp = *d;
|
|
for (x = 0; x < ncol; x++) {
|
|
sp.tp_col = s->tp_col + x;
|
|
dp.tp_col = d->tp_col + x;
|
|
if (!is_same_pixel(&screen_buffer[soffset + x],
|
|
&screen_buffer[doffset + x])) {
|
|
screen_buffer[doffset + x] =
|
|
screen_buffer[soffset + x];
|
|
if (!scroll)
|
|
efi_text_printchar(state, &dp, false);
|
|
} else if (scroll) {
|
|
/* Draw last char and trigger scroll. */
|
|
if (dp.tp_col + 1 == state->tg_tp.tp_col &&
|
|
dp.tp_row + 1 == state->tg_tp.tp_row) {
|
|
efi_text_printchar(state, &dp, true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
efi_text_copy(void *arg, const teken_rect_t *r, const teken_pos_t *p)
|
|
{
|
|
teken_gfx_t *state = arg;
|
|
unsigned doffset, soffset;
|
|
teken_pos_t d, s;
|
|
int nrow, ncol, x, y; /* Has to be signed - >= 0 comparison */
|
|
bool scroll = false;
|
|
|
|
/*
|
|
* Copying is a little tricky. We must make sure we do it in
|
|
* correct order, to make sure we don't overwrite our own data.
|
|
*/
|
|
|
|
nrow = r->tr_end.tp_row - r->tr_begin.tp_row;
|
|
ncol = r->tr_end.tp_col - r->tr_begin.tp_col;
|
|
|
|
/*
|
|
* Check if we do copy whole screen.
|
|
*/
|
|
if (p->tp_row == 0 && p->tp_col == 0 &&
|
|
nrow == state->tg_tp.tp_row - 2 && ncol == state->tg_tp.tp_col - 2)
|
|
scroll = true;
|
|
|
|
soffset = r->tr_begin.tp_col + r->tr_begin.tp_row * state->tg_tp.tp_col;
|
|
doffset = p->tp_col + p->tp_row * state->tg_tp.tp_col;
|
|
|
|
/* remove the cursor */
|
|
if (state->tg_cursor_visible)
|
|
conout->EnableCursor(conout, FALSE);
|
|
|
|
/*
|
|
* Copy line by line.
|
|
*/
|
|
if (doffset <= soffset) {
|
|
s = r->tr_begin;
|
|
d = *p;
|
|
for (y = 0; y < nrow; y++) {
|
|
s.tp_row = r->tr_begin.tp_row + y;
|
|
d.tp_row = p->tp_row + y;
|
|
|
|
efi_text_copy_line(state, ncol, &s, &d, scroll);
|
|
}
|
|
} else {
|
|
for (y = nrow - 1; y >= 0; y--) {
|
|
s.tp_row = r->tr_begin.tp_row + y;
|
|
d.tp_row = p->tp_row + y;
|
|
|
|
efi_text_copy_line(state, ncol, &s, &d, false);
|
|
}
|
|
}
|
|
|
|
/* display the cursor */
|
|
if (state->tg_cursor_visible)
|
|
conout->EnableCursor(conout, TRUE);
|
|
}
|
|
|
|
static void
|
|
efi_text_param(void *arg, int cmd, unsigned int value)
|
|
{
|
|
teken_gfx_t *state = arg;
|
|
|
|
switch (cmd) {
|
|
case TP_SETLOCALCURSOR:
|
|
/*
|
|
* 0 means normal (usually block), 1 means hidden, and
|
|
* 2 means blinking (always block) for compatibility with
|
|
* syscons. We don't support any changes except hiding,
|
|
* so must map 2 to 0.
|
|
*/
|
|
value = (value == 1) ? 0 : 1;
|
|
/* FALLTHROUGH */
|
|
case TP_SHOWCURSOR:
|
|
if (value != 0) {
|
|
conout->EnableCursor(conout, TRUE);
|
|
state->tg_cursor_visible = true;
|
|
} else {
|
|
conout->EnableCursor(conout, FALSE);
|
|
state->tg_cursor_visible = false;
|
|
}
|
|
break;
|
|
default:
|
|
/* Not yet implemented */
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Not implemented.
|
|
*/
|
|
static void
|
|
efi_cons_respond(void *s __unused, const void *buf __unused,
|
|
size_t len __unused)
|
|
{
|
|
}
|
|
|
|
/*
|
|
* Set up conin/conout/coninex to make sure we have input ready.
|
|
*/
|
|
static void
|
|
efi_cons_probe(struct console *cp)
|
|
{
|
|
EFI_STATUS status;
|
|
|
|
conout = ST->ConOut;
|
|
conin = ST->ConIn;
|
|
|
|
/*
|
|
* Call SetMode to work around buggy firmware.
|
|
*/
|
|
status = conout->SetMode(conout, conout->Mode->Mode);
|
|
|
|
if (coninex == NULL) {
|
|
status = BS->OpenProtocol(ST->ConsoleInHandle,
|
|
&simple_input_ex_guid, (void **)&coninex,
|
|
IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
|
|
if (status != EFI_SUCCESS)
|
|
coninex = NULL;
|
|
}
|
|
|
|
cp->c_flags |= C_PRESENTIN | C_PRESENTOUT;
|
|
}
|
|
|
|
static bool
|
|
color_name_to_teken(const char *name, int *val)
|
|
{
|
|
if (strcasecmp(name, "black") == 0) {
|
|
*val = TC_BLACK;
|
|
return (true);
|
|
}
|
|
if (strcasecmp(name, "red") == 0) {
|
|
*val = TC_RED;
|
|
return (true);
|
|
}
|
|
if (strcasecmp(name, "green") == 0) {
|
|
*val = TC_GREEN;
|
|
return (true);
|
|
}
|
|
if (strcasecmp(name, "brown") == 0) {
|
|
*val = TC_BROWN;
|
|
return (true);
|
|
}
|
|
if (strcasecmp(name, "blue") == 0) {
|
|
*val = TC_BLUE;
|
|
return (true);
|
|
}
|
|
if (strcasecmp(name, "magenta") == 0) {
|
|
*val = TC_MAGENTA;
|
|
return (true);
|
|
}
|
|
if (strcasecmp(name, "cyan") == 0) {
|
|
*val = TC_CYAN;
|
|
return (true);
|
|
}
|
|
if (strcasecmp(name, "white") == 0) {
|
|
*val = TC_WHITE;
|
|
return (true);
|
|
}
|
|
return (false);
|
|
}
|
|
|
|
static int
|
|
efi_set_colors(struct env_var *ev, int flags, const void *value)
|
|
{
|
|
int val = 0;
|
|
char buf[2];
|
|
const void *evalue;
|
|
const teken_attr_t *ap;
|
|
teken_attr_t a;
|
|
|
|
if (value == NULL)
|
|
return (CMD_OK);
|
|
|
|
if (color_name_to_teken(value, &val)) {
|
|
snprintf(buf, sizeof (buf), "%d", val);
|
|
evalue = buf;
|
|
} else {
|
|
char *end;
|
|
|
|
errno = 0;
|
|
val = (int)strtol(value, &end, 0);
|
|
if (errno != 0 || *end != '\0') {
|
|
printf("Allowed values are either ansi color name or "
|
|
"number from range [0-7].\n");
|
|
return (CMD_OK);
|
|
}
|
|
evalue = value;
|
|
}
|
|
|
|
ap = teken_get_defattr(&gfx_state.tg_teken);
|
|
a = *ap;
|
|
if (strcmp(ev->ev_name, "teken.fg_color") == 0) {
|
|
/* is it already set? */
|
|
if (ap->ta_fgcolor == val)
|
|
return (CMD_OK);
|
|
a.ta_fgcolor = val;
|
|
}
|
|
if (strcmp(ev->ev_name, "teken.bg_color") == 0) {
|
|
/* is it already set? */
|
|
if (ap->ta_bgcolor == val)
|
|
return (CMD_OK);
|
|
a.ta_bgcolor = val;
|
|
}
|
|
|
|
/* Improve visibility */
|
|
if (a.ta_bgcolor == TC_WHITE)
|
|
a.ta_bgcolor |= TC_LIGHT;
|
|
|
|
teken_set_defattr(&gfx_state.tg_teken, &a);
|
|
cons_draw_frame(&a);
|
|
env_setenv(ev->ev_name, flags | EV_NOHOOK, evalue, NULL, NULL);
|
|
teken_input(&gfx_state.tg_teken, "\e[2J", 4);
|
|
return (CMD_OK);
|
|
}
|
|
|
|
#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_rawputchar(int c)
|
|
{
|
|
int i;
|
|
UINTN x, y;
|
|
conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
|
|
|
|
if (c == '\t') {
|
|
int n;
|
|
|
|
n = 8 - ((conout->Mode->CursorColumn + 8) % 8);
|
|
for (i = 0; i < n; i++)
|
|
efi_cons_rawputchar(' ');
|
|
} else {
|
|
#ifndef TERM_EMU
|
|
if (c == '\n')
|
|
efi_cons_efiputchar('\r');
|
|
efi_cons_efiputchar(c);
|
|
#else
|
|
switch (c) {
|
|
case '\r':
|
|
curx = 0;
|
|
efi_cons_efiputchar('\r');
|
|
return;
|
|
case '\n':
|
|
efi_cons_efiputchar('\n');
|
|
efi_cons_efiputchar('\r');
|
|
cury++;
|
|
if (cury >= y)
|
|
cury--;
|
|
curx = 0;
|
|
return;
|
|
case '\b':
|
|
if (curx > 0) {
|
|
efi_cons_efiputchar('\b');
|
|
curx--;
|
|
}
|
|
return;
|
|
default:
|
|
efi_cons_efiputchar(c);
|
|
curx++;
|
|
if (curx > x-1) {
|
|
curx = 0;
|
|
cury++;
|
|
}
|
|
if (cury > y-1) {
|
|
curx = 0;
|
|
cury--;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
conout->EnableCursor(conout, TRUE);
|
|
}
|
|
|
|
#ifdef TERM_EMU
|
|
/* 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;
|
|
default: /* NOTREACHED */
|
|
__unreachable();
|
|
}
|
|
|
|
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';
|
|
}
|
|
#endif
|
|
|
|
/* Emulate basic capabilities of cons25 terminal */
|
|
static void
|
|
efi_term_emu(int c)
|
|
{
|
|
#ifdef TERM_EMU
|
|
static int ansi_col[] = {
|
|
0, 4, 2, 6, 1, 5, 3, 7
|
|
};
|
|
int t, i;
|
|
EFI_STATUS status;
|
|
|
|
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 22: /* normal intensity */
|
|
fg_c &= ~0x8;
|
|
break;
|
|
case 24: /* not underline */
|
|
case 25: /* not blinking */
|
|
bg_c &= ~0x8;
|
|
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;
|
|
}
|
|
#else
|
|
efi_cons_rawputchar(c);
|
|
#endif
|
|
}
|
|
|
|
static int
|
|
env_screen_nounset(struct env_var *ev __unused)
|
|
{
|
|
if (gfx_state.tg_fb_type == FB_TEXT)
|
|
return (0);
|
|
return (EPERM);
|
|
}
|
|
|
|
static void
|
|
cons_draw_frame(teken_attr_t *a)
|
|
{
|
|
teken_attr_t attr = *a;
|
|
teken_color_t fg = a->ta_fgcolor;
|
|
|
|
attr.ta_fgcolor = attr.ta_bgcolor;
|
|
teken_set_defattr(&gfx_state.tg_teken, &attr);
|
|
|
|
gfx_fb_drawrect(0, 0, gfx_state.tg_fb.fb_width,
|
|
gfx_state.tg_origin.tp_row, 1);
|
|
gfx_fb_drawrect(0,
|
|
gfx_state.tg_fb.fb_height - gfx_state.tg_origin.tp_row - 1,
|
|
gfx_state.tg_fb.fb_width, gfx_state.tg_fb.fb_height, 1);
|
|
gfx_fb_drawrect(0, gfx_state.tg_origin.tp_row,
|
|
gfx_state.tg_origin.tp_col,
|
|
gfx_state.tg_fb.fb_height - gfx_state.tg_origin.tp_row - 1, 1);
|
|
gfx_fb_drawrect(
|
|
gfx_state.tg_fb.fb_width - gfx_state.tg_origin.tp_col - 1,
|
|
gfx_state.tg_origin.tp_row, gfx_state.tg_fb.fb_width,
|
|
gfx_state.tg_fb.fb_height, 1);
|
|
|
|
attr.ta_fgcolor = fg;
|
|
teken_set_defattr(&gfx_state.tg_teken, &attr);
|
|
}
|
|
|
|
bool
|
|
cons_update_mode(bool use_gfx_mode)
|
|
{
|
|
UINTN cols, rows;
|
|
const teken_attr_t *a;
|
|
teken_attr_t attr;
|
|
EFI_STATUS status;
|
|
char env[10], *ptr;
|
|
|
|
if (!efi_started)
|
|
return (false);
|
|
|
|
/*
|
|
* Despite the use_gfx_mode, we want to make sure we call
|
|
* efi_find_framebuffer(). This will populate the fb data,
|
|
* which will be passed to kernel.
|
|
*/
|
|
if (efi_find_framebuffer(&gfx_state) == 0 && use_gfx_mode) {
|
|
int roff, goff, boff;
|
|
|
|
roff = ffs(gfx_state.tg_fb.fb_mask_red) - 1;
|
|
goff = ffs(gfx_state.tg_fb.fb_mask_green) - 1;
|
|
boff = ffs(gfx_state.tg_fb.fb_mask_blue) - 1;
|
|
|
|
(void) generate_cons_palette(cmap, COLOR_FORMAT_RGB,
|
|
gfx_state.tg_fb.fb_mask_red >> roff, roff,
|
|
gfx_state.tg_fb.fb_mask_green >> goff, goff,
|
|
gfx_state.tg_fb.fb_mask_blue >> boff, boff);
|
|
} else {
|
|
/*
|
|
* Either text mode was asked by user or we failed to
|
|
* find frame buffer.
|
|
*/
|
|
gfx_state.tg_fb_type = FB_TEXT;
|
|
}
|
|
|
|
status = conout->QueryMode(conout, conout->Mode->Mode, &cols, &rows);
|
|
if (EFI_ERROR(status) || cols * rows == 0) {
|
|
cols = TEXT_COLS;
|
|
rows = TEXT_ROWS;
|
|
}
|
|
|
|
/*
|
|
* When we have serial port listed in ConOut, use pre-teken emulator,
|
|
* if built with.
|
|
* The problem is, we can not output text on efi and comconsole when
|
|
* efi also has comconsole bound. But then again, we need to have
|
|
* terminal emulator for efi text mode to support the menu.
|
|
* While teken is too expensive to be used on serial console, the
|
|
* pre-teken emulator is light enough to be used on serial console.
|
|
*
|
|
* When doing multiple consoles (both serial and video),
|
|
* also just use the old emulator. RB_MULTIPLE also implies
|
|
* we're using a serial console.
|
|
*/
|
|
mode = parse_uefi_con_out();
|
|
if ((mode & (RB_SERIAL | RB_MULTIPLE)) == 0) {
|
|
conout->EnableCursor(conout, FALSE);
|
|
gfx_state.tg_cursor_visible = false;
|
|
|
|
if (gfx_state.tg_fb_type == FB_TEXT) {
|
|
|
|
gfx_state.tg_functions = &tf;
|
|
/* ensure the following are not set for text mode */
|
|
unsetenv("screen.height");
|
|
unsetenv("screen.width");
|
|
unsetenv("screen.depth");
|
|
} else {
|
|
uint32_t fb_height, fb_width;
|
|
|
|
fb_height = gfx_state.tg_fb.fb_height;
|
|
fb_width = gfx_state.tg_fb.fb_width;
|
|
|
|
/*
|
|
* setup_font() can adjust terminal size.
|
|
* We can see two kind of bad happening.
|
|
* We either can get too small console font - requested
|
|
* terminal size is large, display resolution is
|
|
* large, and we get very small font.
|
|
* Or, we can get too large font - requested
|
|
* terminal size is small and this will cause large
|
|
* font to be selected.
|
|
* Now, the setup_font() is updated to consider
|
|
* display density and this should give us mostly
|
|
* acceptable font. However, the catch is, not all
|
|
* display devices will give us display density.
|
|
* Still, we do hope, external monitors do - this is
|
|
* where the display size will matter the most.
|
|
* And for laptop screens, we should still get good
|
|
* results by requesting 80x25 terminal.
|
|
*/
|
|
gfx_state.tg_tp.tp_row = 25;
|
|
gfx_state.tg_tp.tp_col = 80;
|
|
setup_font(&gfx_state, fb_height, fb_width);
|
|
rows = gfx_state.tg_tp.tp_row;
|
|
cols = gfx_state.tg_tp.tp_col;
|
|
/* Point of origin in pixels. */
|
|
gfx_state.tg_origin.tp_row = (fb_height -
|
|
(rows * gfx_state.tg_font.vf_height)) / 2;
|
|
gfx_state.tg_origin.tp_col = (fb_width -
|
|
(cols * gfx_state.tg_font.vf_width)) / 2;
|
|
|
|
/* UEFI gop has depth 32. */
|
|
gfx_state.tg_glyph_size = gfx_state.tg_font.vf_height *
|
|
gfx_state.tg_font.vf_width * 4;
|
|
free(gfx_state.tg_glyph);
|
|
gfx_state.tg_glyph = malloc(gfx_state.tg_glyph_size);
|
|
if (gfx_state.tg_glyph == NULL)
|
|
return (false);
|
|
|
|
gfx_state.tg_functions = &tfx;
|
|
snprintf(env, sizeof (env), "%d", fb_height);
|
|
env_setenv("screen.height", EV_VOLATILE | EV_NOHOOK,
|
|
env, env_noset, env_screen_nounset);
|
|
snprintf(env, sizeof (env), "%d", fb_width);
|
|
env_setenv("screen.width", EV_VOLATILE | EV_NOHOOK,
|
|
env, env_noset, env_screen_nounset);
|
|
snprintf(env, sizeof (env), "%d",
|
|
gfx_state.tg_fb.fb_bpp);
|
|
env_setenv("screen.depth", EV_VOLATILE | EV_NOHOOK,
|
|
env, env_noset, env_screen_nounset);
|
|
}
|
|
|
|
/* Record our terminal screen size. */
|
|
gfx_state.tg_tp.tp_row = rows;
|
|
gfx_state.tg_tp.tp_col = cols;
|
|
|
|
teken_init(&gfx_state.tg_teken, gfx_state.tg_functions,
|
|
&gfx_state);
|
|
|
|
free(screen_buffer);
|
|
screen_buffer = malloc(rows * cols * sizeof(*screen_buffer));
|
|
if (screen_buffer != NULL) {
|
|
teken_set_winsize(&gfx_state.tg_teken,
|
|
&gfx_state.tg_tp);
|
|
a = teken_get_defattr(&gfx_state.tg_teken);
|
|
attr = *a;
|
|
|
|
/*
|
|
* On first run, we set up the efi_set_colors()
|
|
* callback. If the env is already set, we
|
|
* pick up fg and bg color values from the environment.
|
|
*/
|
|
ptr = getenv("teken.fg_color");
|
|
if (ptr != NULL) {
|
|
attr.ta_fgcolor = strtol(ptr, NULL, 10);
|
|
ptr = getenv("teken.bg_color");
|
|
attr.ta_bgcolor = strtol(ptr, NULL, 10);
|
|
|
|
teken_set_defattr(&gfx_state.tg_teken, &attr);
|
|
} else {
|
|
snprintf(env, sizeof(env), "%d",
|
|
attr.ta_fgcolor);
|
|
env_setenv("teken.fg_color", EV_VOLATILE, env,
|
|
efi_set_colors, env_nounset);
|
|
snprintf(env, sizeof(env), "%d",
|
|
attr.ta_bgcolor);
|
|
env_setenv("teken.bg_color", EV_VOLATILE, env,
|
|
efi_set_colors, env_nounset);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (screen_buffer == NULL) {
|
|
conout->EnableCursor(conout, TRUE);
|
|
#ifdef TERM_EMU
|
|
conout->SetAttribute(conout, EFI_TEXT_ATTR(DEFAULT_FGCOLOR,
|
|
DEFAULT_BGCOLOR));
|
|
end_term();
|
|
get_pos(&curx, &cury);
|
|
curs_move(&curx, &cury, curx, cury);
|
|
fg_c = DEFAULT_FGCOLOR;
|
|
bg_c = DEFAULT_BGCOLOR;
|
|
#endif
|
|
} else {
|
|
/* Improve visibility */
|
|
if (attr.ta_bgcolor == TC_WHITE)
|
|
attr.ta_bgcolor |= TC_LIGHT;
|
|
teken_set_defattr(&gfx_state.tg_teken, &attr);
|
|
|
|
/* Draw frame around terminal area. */
|
|
cons_draw_frame(&attr);
|
|
/*
|
|
* Erase display, this will also fill our screen
|
|
* buffer.
|
|
*/
|
|
teken_input(&gfx_state.tg_teken, "\e[2J", 4);
|
|
gfx_state.tg_functions->tf_param(&gfx_state,
|
|
TP_SHOWCURSOR, 1);
|
|
}
|
|
|
|
snprintf(env, sizeof (env), "%u", (unsigned)rows);
|
|
setenv("LINES", env, 1);
|
|
snprintf(env, sizeof (env), "%u", (unsigned)cols);
|
|
setenv("COLUMNS", env, 1);
|
|
|
|
return (true);
|
|
}
|
|
|
|
static int
|
|
efi_cons_init(int arg)
|
|
{
|
|
EFI_STATUS status;
|
|
|
|
if (efi_started)
|
|
return (0);
|
|
|
|
efi_started = true;
|
|
|
|
gfx_framework_init();
|
|
if (cons_update_mode(gfx_state.tg_fb_type != FB_TEXT))
|
|
return (0);
|
|
|
|
return (1);
|
|
}
|
|
|
|
static void
|
|
input_partial(void)
|
|
{
|
|
unsigned i;
|
|
uint32_t c;
|
|
|
|
if (utf8_left == 0)
|
|
return;
|
|
|
|
for (i = 0; i < sizeof(utf8_partial); i++) {
|
|
c = (utf8_partial >> (24 - (i << 3))) & 0xff;
|
|
if (c != 0)
|
|
efi_term_emu(c);
|
|
}
|
|
utf8_left = 0;
|
|
utf8_partial = 0;
|
|
}
|
|
|
|
static void
|
|
input_byte(uint8_t c)
|
|
{
|
|
if ((c & 0x80) == 0x00) {
|
|
/* One-byte sequence. */
|
|
input_partial();
|
|
efi_term_emu(c);
|
|
return;
|
|
}
|
|
if ((c & 0xe0) == 0xc0) {
|
|
/* Two-byte sequence. */
|
|
input_partial();
|
|
utf8_left = 1;
|
|
utf8_partial = c;
|
|
return;
|
|
}
|
|
if ((c & 0xf0) == 0xe0) {
|
|
/* Three-byte sequence. */
|
|
input_partial();
|
|
utf8_left = 2;
|
|
utf8_partial = c;
|
|
return;
|
|
}
|
|
if ((c & 0xf8) == 0xf0) {
|
|
/* Four-byte sequence. */
|
|
input_partial();
|
|
utf8_left = 3;
|
|
utf8_partial = c;
|
|
return;
|
|
}
|
|
if ((c & 0xc0) == 0x80) {
|
|
/* Invalid state? */
|
|
if (utf8_left == 0) {
|
|
efi_term_emu(c);
|
|
return;
|
|
}
|
|
utf8_left--;
|
|
utf8_partial = (utf8_partial << 8) | c;
|
|
if (utf8_left == 0) {
|
|
uint32_t v, u;
|
|
uint8_t b;
|
|
|
|
v = 0;
|
|
u = utf8_partial;
|
|
b = (u >> 24) & 0xff;
|
|
if (b != 0) { /* Four-byte sequence */
|
|
v = b & 0x07;
|
|
b = (u >> 16) & 0xff;
|
|
v = (v << 6) | (b & 0x3f);
|
|
b = (u >> 8) & 0xff;
|
|
v = (v << 6) | (b & 0x3f);
|
|
b = u & 0xff;
|
|
v = (v << 6) | (b & 0x3f);
|
|
} else if ((b = (u >> 16) & 0xff) != 0) {
|
|
v = b & 0x0f; /* Three-byte sequence */
|
|
b = (u >> 8) & 0xff;
|
|
v = (v << 6) | (b & 0x3f);
|
|
b = u & 0xff;
|
|
v = (v << 6) | (b & 0x3f);
|
|
} else if ((b = (u >> 8) & 0xff) != 0) {
|
|
v = b & 0x1f; /* Two-byte sequence */
|
|
b = u & 0xff;
|
|
v = (v << 6) | (b & 0x3f);
|
|
}
|
|
/* Send unicode char directly to console. */
|
|
efi_cons_efiputchar(v);
|
|
utf8_partial = 0;
|
|
}
|
|
return;
|
|
}
|
|
/* Anything left is illegal in UTF-8 sequence. */
|
|
input_partial();
|
|
efi_term_emu(c);
|
|
}
|
|
|
|
void
|
|
efi_cons_putchar(int c)
|
|
{
|
|
unsigned char ch = c;
|
|
|
|
/*
|
|
* Don't use Teken when we're doing pure serial, or a multiple console
|
|
* with video "primary" because that's also serial.
|
|
*/
|
|
if ((mode & (RB_SERIAL | RB_MULTIPLE)) != 0 || screen_buffer == NULL) {
|
|
input_byte(ch);
|
|
return;
|
|
}
|
|
|
|
teken_input(&gfx_state.tg_teken, &ch, sizeof (ch));
|
|
}
|
|
|
|
static int
|
|
keybuf_getchar(void)
|
|
{
|
|
int i, c = 0;
|
|
|
|
for (i = 0; i < KEYBUFSZ; i++) {
|
|
if (keybuf[i] != 0) {
|
|
c = keybuf[i];
|
|
keybuf[i] = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return (c);
|
|
}
|
|
|
|
static bool
|
|
keybuf_ischar(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < KEYBUFSZ; i++) {
|
|
if (keybuf[i] != 0)
|
|
return (true);
|
|
}
|
|
return (false);
|
|
}
|
|
|
|
/*
|
|
* We are not reading input before keybuf is empty, so we are safe
|
|
* just to fill keybuf from the beginning.
|
|
*/
|
|
static void
|
|
keybuf_inschar(EFI_INPUT_KEY *key)
|
|
{
|
|
|
|
switch (key->ScanCode) {
|
|
case SCAN_UP: /* UP */
|
|
keybuf[0] = 0x1b; /* esc */
|
|
keybuf[1] = '[';
|
|
keybuf[2] = 'A';
|
|
break;
|
|
case SCAN_DOWN: /* DOWN */
|
|
keybuf[0] = 0x1b; /* esc */
|
|
keybuf[1] = '[';
|
|
keybuf[2] = 'B';
|
|
break;
|
|
case SCAN_RIGHT: /* RIGHT */
|
|
keybuf[0] = 0x1b; /* esc */
|
|
keybuf[1] = '[';
|
|
keybuf[2] = 'C';
|
|
break;
|
|
case SCAN_LEFT: /* LEFT */
|
|
keybuf[0] = 0x1b; /* esc */
|
|
keybuf[1] = '[';
|
|
keybuf[2] = 'D';
|
|
break;
|
|
case SCAN_DELETE:
|
|
keybuf[0] = CHAR_BACKSPACE;
|
|
break;
|
|
case SCAN_ESC:
|
|
keybuf[0] = 0x1b; /* esc */
|
|
break;
|
|
default:
|
|
keybuf[0] = key->UnicodeChar;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static bool
|
|
efi_readkey(void)
|
|
{
|
|
EFI_STATUS status;
|
|
EFI_INPUT_KEY key;
|
|
|
|
status = conin->ReadKeyStroke(conin, &key);
|
|
if (status == EFI_SUCCESS) {
|
|
keybuf_inschar(&key);
|
|
return (true);
|
|
}
|
|
return (false);
|
|
}
|
|
|
|
static bool
|
|
efi_readkey_ex(void)
|
|
{
|
|
EFI_STATUS status;
|
|
EFI_INPUT_KEY *kp;
|
|
EFI_KEY_DATA key_data;
|
|
uint32_t kss;
|
|
|
|
status = coninex->ReadKeyStrokeEx(coninex, &key_data);
|
|
if (status == EFI_SUCCESS) {
|
|
kss = key_data.KeyState.KeyShiftState;
|
|
kp = &key_data.Key;
|
|
if (kss & EFI_SHIFT_STATE_VALID) {
|
|
|
|
/*
|
|
* quick mapping to control chars, replace with
|
|
* map lookup later.
|
|
*/
|
|
if (kss & EFI_RIGHT_CONTROL_PRESSED ||
|
|
kss & EFI_LEFT_CONTROL_PRESSED) {
|
|
if (kp->UnicodeChar >= 'a' &&
|
|
kp->UnicodeChar <= 'z') {
|
|
kp->UnicodeChar -= 'a';
|
|
kp->UnicodeChar++;
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
* The shift state and/or toggle state may not be valid,
|
|
* but we still can have ScanCode or UnicodeChar.
|
|
*/
|
|
if (kp->ScanCode == 0 && kp->UnicodeChar == 0)
|
|
return (false);
|
|
keybuf_inschar(kp);
|
|
return (true);
|
|
}
|
|
return (false);
|
|
}
|
|
|
|
int
|
|
efi_cons_getchar(void)
|
|
{
|
|
int c;
|
|
|
|
if ((c = keybuf_getchar()) != 0)
|
|
return (c);
|
|
|
|
key_pending = 0;
|
|
|
|
if (coninex == NULL) {
|
|
if (efi_readkey())
|
|
return (keybuf_getchar());
|
|
} else {
|
|
if (efi_readkey_ex())
|
|
return (keybuf_getchar());
|
|
}
|
|
|
|
return (-1);
|
|
}
|
|
|
|
int
|
|
efi_cons_poll(void)
|
|
{
|
|
EFI_STATUS status;
|
|
|
|
if (keybuf_ischar() || key_pending)
|
|
return (1);
|
|
|
|
/*
|
|
* Some EFI implementation (u-boot for example) do not support
|
|
* WaitForKey().
|
|
* CheckEvent() can clear the signaled state.
|
|
*/
|
|
if (coninex != NULL) {
|
|
if (coninex->WaitForKeyEx == NULL) {
|
|
key_pending = efi_readkey_ex();
|
|
} else {
|
|
status = BS->CheckEvent(coninex->WaitForKeyEx);
|
|
key_pending = status == EFI_SUCCESS;
|
|
}
|
|
} else {
|
|
if (conin->WaitForKey == NULL) {
|
|
key_pending = efi_readkey();
|
|
} else {
|
|
status = BS->CheckEvent(conin->WaitForKey);
|
|
key_pending = status == EFI_SUCCESS;
|
|
}
|
|
}
|
|
|
|
return (key_pending);
|
|
}
|
|
|
|
/* Plain direct access to EFI OutputString(). */
|
|
void
|
|
efi_cons_efiputchar(int c)
|
|
{
|
|
CHAR16 buf[2];
|
|
EFI_STATUS status;
|
|
|
|
buf[0] = c;
|
|
buf[1] = 0; /* terminate string */
|
|
|
|
status = conout->TestString(conout, buf);
|
|
if (EFI_ERROR(status))
|
|
buf[0] = '?';
|
|
conout->OutputString(conout, buf);
|
|
}
|