aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2006-08-31 15:48:26 -0700
committerH. Peter Anvin <hpa@zytor.com>2006-08-31 15:48:26 -0700
commit6f00093c652fdbf1c9679100cc21906314b2a859 (patch)
tree57181cc3f0d3b5313046f5c7bd579824dee25888
parent0c04c3635c840e8959705900a89c072cb42a5864 (diff)
downloadsyslinux-elf-6f00093c652fdbf1c9679100cc21906314b2a859.tar.gz
syslinux-elf-6f00093c652fdbf1c9679100cc21906314b2a859.tar.xz
syslinux-elf-6f00093c652fdbf1c9679100cc21906314b2a859.zip
More work on actual VESA console and menu. Shadowing still doesn't work
quite right, however.
-rw-r--r--com32/include/console.h5
-rw-r--r--com32/lib/Makefile9
-rwxr-xr-xcom32/lib/sys/vesa/alphatbl.pl48
-rw-r--r--com32/lib/sys/vesa/drawtxt.c79
-rw-r--r--com32/lib/sys/vesa/initvesa.c78
-rw-r--r--com32/lib/sys/vesa/video.h9
-rw-r--r--com32/lib/sys/vesacon_write.c109
-rw-r--r--com32/lib/sys/vesaserial_write.c58
-rw-r--r--com32/modules/Makefile5
-rw-r--r--com32/modules/vesamenu.c856
-rw-r--r--com32/samples/vesa.c2
11 files changed, 1064 insertions, 194 deletions
diff --git a/com32/include/console.h b/com32/include/console.h
index 3b08e25a..a5e8ca26 100644
--- a/com32/include/console.h
+++ b/com32/include/console.h
@@ -55,4 +55,9 @@ extern const struct output_dev dev_ansicon_w;
/* ANSI plus serial port */
extern const struct output_dev dev_ansiserial_w;
+/* VESA graphics console (output only) */
+extern const struct output_dev dev_vesacon_w;
+/* VESA plus serial port */
+extern const struct output_dev dev_vesaserial_w;
+
#endif /* _CONSOLE_H */
diff --git a/com32/lib/Makefile b/com32/lib/Makefile
index 05b22793..e7f4ad7c 100644
--- a/com32/lib/Makefile
+++ b/com32/lib/Makefile
@@ -33,8 +33,8 @@ LIBOBJS = \
sys/null_read.o sys/null_write.o sys/serial_write.o \
sys/ansicon_write.o sys/ansiserial_write.o \
\
- sys/vesa/initvesa.o sys/vesa/alphatbl.o sys/vesa/drawtxt.o \
- sys/vesa/background.o \
+ sys/vesacon_write.o sys/vesaserial_write.o \
+ sys/vesa/initvesa.o sys/vesa/drawtxt.o sys/vesa/background.o \
\
pci/cfgtype.o \
pci/readb.o pci/readw.o pci/readl.o pci/readbios.o \
@@ -80,11 +80,6 @@ install: all
-rm -rf $(INSTALLROOT)$(COM32DIR)/include
cp -r ../include $(INSTALLROOT)$(COM32DIR)
-sys/vesa/alphatbl.o: sys/vesa/alphatbl.c
-
-sys/vesa/alphatbl.c: sys/vesa/alphatbl.pl
- $(PERL) $< > $@ || ( rm -f $@ ; exit 1 )
-
# This code is performance critical, and doesn't compile well with -Os
sys/vesa/drawtxt.o: sys/vesa/drawtxt.c
$(CC) $(CFLAGS) -O3 -c -o $@ $<
diff --git a/com32/lib/sys/vesa/alphatbl.pl b/com32/lib/sys/vesa/alphatbl.pl
deleted file mode 100755
index e49ff3e6..00000000
--- a/com32/lib/sys/vesa/alphatbl.pl
+++ /dev/null
@@ -1,48 +0,0 @@
-#!/usr/bin/perl
-#
-# Compute the alpha-blending table for 256 possible (R, G, B) value
-# compared with the 4-bit intensity value from the character attribute
-#
-
-#
-# Configurable parameters...
-#
-@text_intensity = (0.0, 0.3333, 0.6667, 1.0);
-$text_alpha = 0.5;
-$input_gamma = 1.7;
-$text_gamma = 1.7;
-$output_gamma = 1.7;
-
-sub ungamma($$) {
- my($v, $gamma) = @_;
-
- return $v**$gamma;
-}
-
-sub gamma($$) {
- my($v, $gamma) = @_;
- return $v**(1/$gamma);
-}
-
-print "unsigned char __vesacon_alpha_tbl[256][4] = {\n";
-
-for ($i = 0; $i <= 255; $i++) {
- $ival = ungamma($i/255, $input_gamma);
-
- $intro = "\t{";
-
- for ($j = 0; $j < 4; $j++) {
- $v = ($ival*(1-$text_alpha)) +
- ungamma($text_intensity[$j], $text_gamma)*$text_alpha;
-
- $d = int(gamma($v,$output_gamma)*255+0.5);
-
- $d = 0 if ($d < 0);
- $d = 255 if ($d > 255);
-
- printf "%s%3d", $intro, $d;
- $intro = ', ';
- }
- print "},\n";
-}
-print "};\n";
diff --git a/com32/lib/sys/vesa/drawtxt.c b/com32/lib/sys/vesa/drawtxt.c
index b121e679..f2c79519 100644
--- a/com32/lib/sys/vesa/drawtxt.c
+++ b/com32/lib/sys/vesa/drawtxt.c
@@ -174,10 +174,70 @@ static void vesacon_update_characters(int row, int col, int nrows, int ncols)
}
}
-void vesacon_write_at(int row, int col, const char *str, uint8_t attr, int rev)
+/* Fill a number of characters... */
+static inline struct vesa_char *vesacon_fill(struct vesa_char *ptr,
+ struct vesa_char fill,
+ unsigned int count)
+{
+ asm volatile("cld; rep; stosl"
+ : "+D" (ptr), "+c" (count)
+ : "a" (fill)
+ : "memory");
+
+ return ptr;
+}
+
+/* Erase a region of the screen */
+void __vesacon_erase(int x0, int y0, int x1, int y1, uint8_t attr, int rev)
+{
+ int y;
+ struct vesa_char *ptr = &__vesacon_text_display
+ [(y0+1)*(TEXT_PIXEL_COLS/FONT_WIDTH+2)+(x0+1)];
+ struct vesa_char fill = {
+ .ch = ' ',
+ .attr = attr,
+ .sha = rev
+ };
+ int ncols = x1-x0+1;
+
+ for (y = y0; y <= y1; y++) {
+ vesacon_fill(ptr, fill, ncols);
+ ptr += TEXT_PIXEL_COLS/FONT_WIDTH+2;
+ }
+
+ vesacon_update_characters(y0, x0, y1-y0+1, ncols);
+}
+
+/* Scroll the screen up */
+void __vesacon_scroll_up(int nrows, uint8_t attr, int rev)
+{
+ struct vesa_char *fromptr = &__vesacon_text_display
+ [nrows*(TEXT_PIXEL_COLS/FONT_WIDTH+2)];
+ struct vesa_char *toptr = __vesacon_text_display;
+ int dword_count = nrows*(TEXT_PIXEL_COLS/FONT_WIDTH+2);
+ struct vesa_char fill = {
+ .ch = ' ',
+ .attr = attr,
+ .sha = rev,
+ };
+
+ asm volatile("cld ; rep ; movsl"
+ : "+D" (toptr), "+S" (fromptr), "+c" (dword_count));
+
+ dword_count = (__vesacon_text_rows-nrows)*(TEXT_PIXEL_COLS/FONT_WIDTH+2);
+ vesacon_fill(toptr, fill, dword_count);
+
+ vesacon_update_characters(0, 0, __vesacon_text_rows,
+ TEXT_PIXEL_COLS/FONT_WIDTH);
+}
+
+/* Draw text at a specific area of the screen */
+void __vesacon_write_at(int x, int y, const char *str,
+ uint8_t attr, int rev)
{
int n = 0;
- struct vesa_char *ptr = &__vesacon_text_display[(row+1)*(TEXT_PIXEL_COLS/FONT_WIDTH+2)+(col+1)];
+ struct vesa_char *ptr = &__vesacon_text_display
+ [(y+1)*(TEXT_PIXEL_COLS/FONT_WIDTH+2)+(x+1)];
while (*str) {
ptr->ch = *str;
@@ -189,6 +249,19 @@ void vesacon_write_at(int row, int col, const char *str, uint8_t attr, int rev)
ptr++;
}
- vesacon_update_characters(row, col, 1, n);
+ vesacon_update_characters(y, x, 1, n);
+}
+
+/* Draw one character text at a specific area of the screen */
+void __vesacon_write_char(int x, int y, char ch, uint8_t attr, int rev)
+{
+ struct vesa_char *ptr = &__vesacon_text_display
+ [(y+1)*(TEXT_PIXEL_COLS/FONT_WIDTH+2)+(x+1)];
+
+ ptr->ch = ch;
+ ptr->attr = attr;
+ ptr->sha = rev;
+
+ vesacon_update_characters(y, x, 1, 1);
}
diff --git a/com32/lib/sys/vesa/initvesa.c b/com32/lib/sys/vesa/initvesa.c
index 7d294270..022fb321 100644
--- a/com32/lib/sys/vesa/initvesa.c
+++ b/com32/lib/sys/vesa/initvesa.c
@@ -45,7 +45,7 @@ struct vesa_info __vesa_info;
struct vesa_char *__vesacon_text_display;
-int __vesacon_font_height;
+int __vesacon_font_height, __vesacon_text_rows;
uint8_t __vesacon_graphics_font[FONT_MAX_CHARS][FONT_MAX_HEIGHT];
uint32_t __vesacon_background[VIDEO_Y_SIZE][VIDEO_X_SIZE];
@@ -159,6 +159,7 @@ static int vesacon_set_mode(void)
rom_font = MK_PTR(rm.es, rm.ebp.w[0]);
__vesacon_font_height = 16;
unpack_font((uint8_t *)__vesacon_graphics_font, rom_font, 16);
+ __vesacon_text_rows = (VIDEO_Y_SIZE-2*VIDEO_BORDER)/__vesacon_font_height;
/* Now set video mode */
rm.eax.w[0] = 0x4F02; /* Set SVGA video mode */
@@ -201,78 +202,11 @@ static int init_text_display(void)
int __vesacon_init(void)
{
- int i, j, r, g, b, n;
- const int step = 8;
-
- /* Fill the background image with a test pattern */
- for (i = 0; i < VIDEO_Y_SIZE; i++) {
- r = g = b = n = 0;
-
- for (j = 0; j < VIDEO_X_SIZE; j++) {
- switch (n) {
- case 0:
- r += step;
- if (r >= 255) {
- r = 255;
- n++;
- }
- break;
- case 1:
- g += step;
- if (g >= 255) {
- g = 255;
- n++;
- }
- break;
- case 2:
- r -= step;
- if (r <= 0) {
- r = 0;
- n++;
- }
- break;
- case 3:
- b += step;
- if (b >= 255) {
- b = 255;
- n++;
- }
- break;
- case 4:
- g -= step;
- if (g <= 0) {
- g = 0;
- n++;
- }
- break;
- case 5:
- r += step;
- if (r >= 255) {
- r = 255;
- n++;
- }
- break;
- case 6:
- g += step;
- if (g >= 255) {
- g = 255;
- n++;
- }
- break;
- case 7:
- r -= step;
- if (r < 0) {
- r = 0;
- n = 0;
- }
- g = b = r;
- break;
- }
- __vesacon_background[i][j] = (r << 16) + (g << 8) + b;
- }
- }
+ int rv;
- vesacon_set_mode();
+ rv = vesacon_set_mode();
+ if (rv)
+ return rv;
init_text_display();
diff --git a/com32/lib/sys/vesa/video.h b/com32/lib/sys/vesa/video.h
index 9237a85e..82b14a4b 100644
--- a/com32/lib/sys/vesa/video.h
+++ b/com32/lib/sys/vesa/video.h
@@ -53,14 +53,19 @@ struct vesa_char {
extern struct vesa_char *__vesacon_text_display;
-extern int __vesacon_font_height;
+extern int __vesacon_font_height, __vesacon_text_rows;
extern uint8_t __vesacon_graphics_font[FONT_MAX_CHARS][FONT_MAX_HEIGHT];
extern uint32_t __vesacon_background[VIDEO_Y_SIZE][VIDEO_X_SIZE];
extern uint32_t __vesacon_shadowfb[VIDEO_Y_SIZE][VIDEO_X_SIZE];
extern unsigned char __vesacon_alpha_tbl[256][4];
-extern int __vesacon_init_background(void);
+int __vesacon_init_background(void);
int vesacon_load_background(const char *);
+int __vesacon_init(void);
+void __vesacon_erase(int, int, int, int, uint8_t, int);
+void __vesacon_scroll_up(int, uint8_t, int);
+void __vesacon_write_at(int, int, const char *, uint8_t, int);
+void __vesacon_write_char(int, int, char, uint8_t, int);
#endif /* LIB_SYS_VESA_VIDEO_H */
diff --git a/com32/lib/sys/vesacon_write.c b/com32/lib/sys/vesacon_write.c
index 2b0fb709..d759a82e 100644
--- a/com32/lib/sys/vesacon_write.c
+++ b/com32/lib/sys/vesacon_write.c
@@ -28,7 +28,8 @@
/*
* vesacon_write.c
*
- * Write to the screen mapped using VESA BIOS extensions (VBE)
+ * Write to the screen using ANSI control codes (about as capable as
+ * DOS' ANSI.SYS.)
*/
#include <errno.h>
@@ -37,14 +38,11 @@
#include <minmax.h>
#include <klibc/compiler.h>
#include "file.h"
+#include "vesa/video.h"
struct curxy {
uint8_t x, y;
} __attribute__((packed));
-#define BIOS_CURXY ((struct curxy *)0x450) /* Array for each page */
-#define BIOS_ROWS (*(uint8_t *)0x484) /* Minus one; if zero use 24 (= 25 lines) */
-#define BIOS_COLS (*(uint16_t *)0x44A)
-#define BIOS_PAGE (*(uint8_t *)0x462)
enum ansi_state {
st_init, /* Normal (no ESC seen) */
@@ -71,6 +69,7 @@ struct term_state {
int pvt; /* Private code? */
int nparms; /* Number of parameters seen */
int parms[MAX_PARMS];
+ struct curxy xy;
};
static const struct term_state default_state =
@@ -90,6 +89,7 @@ static const struct term_state default_state =
.state = st_init,
.pvt = 0,
.nparms = 0,
+ .xy = { 0, 0 },
};
static struct term_state st;
@@ -99,66 +99,67 @@ static const char decvt_to_cp437[] =
{ 0004, 0261, 0007, 0007, 0007, 0007, 0370, 0361, 0007, 0007, 0331, 0277, 0332, 0300, 0305, 0304,
0304, 0304, 0137, 0137, 0303, 0264, 0301, 0302, 0263, 0363, 0362, 0343, 0330, 0234, 0007, 00 };
+/* Reference counter to the screen, to keep track of if we need reinitialization. */
+static int vesacon_counter = 0;
+
/* Common setup */
-static void vesacon_init(void)
+int __vesacon_open(struct file_info *fp)
{
static com32sys_t ireg; /* Auto-initalized to all zero */
com32sys_t oreg;
- /* Initial state */
- memcpy(&st, &default_state, sizeof st);
-
- /* Are we disabled? */
- ireg.eax.w[0] = 0x000b;
- __intcall(0x22, &ireg, &oreg);
+ (void)fp;
- if ( (signed char)oreg.ebx.b[1] < 0 ) {
- st.disabled = 1;
- return;
+ if (!vesacon_counter) {
+ /* Initial state */
+ memcpy(&st, &default_state, sizeof st);
+
+ /* Are we disabled? */
+ ireg.eax.w[0] = 0x000b;
+ __intcall(0x22, &ireg, &oreg);
+
+ if ( (signed char)oreg.ebx.b[1] < 0 ) {
+ st.disabled = 1;
+ } else {
+ /* Switch mode */
+ if (__vesacon_init())
+ return EIO;
+ }
}
- /* Force text mode */
- ireg.eax.w[0] = 0x0005;
- __intcall(0x22, &ireg, NULL);
+ vesacon_counter++;
+ return 0;
+}
+
+int __vesacon_close(struct file_info *fp)
+{
+ (void)fp;
- /* Get cursor shape */
- ireg.eax.b[1] = 0x03;
- ireg.ebx.b[1] = BIOS_PAGE;
- __intcall(0x10, &ireg, &oreg);
- st.cursor_type = oreg.ecx.w[0];
+ vesacon_counter--;
+ return 0;
}
/* Erase a region of the screen */
static void vesacon_erase(int x0, int y0, int x1, int y1)
{
- static com32sys_t ireg;
-
- ireg.eax.w[0] = 0x0600; /* Clear window */
- ireg.ebx.b[1] = st.attr; /* Fill with current attribute */
- ireg.ecx.b[0] = x0;
- ireg.ecx.b[1] = y0;
- ireg.edx.b[0] = x1;
- ireg.edx.b[1] = y1;
- __intcall(0x10, &ireg, NULL);
+ __vesacon_erase(x0, y0, x1, y1, st.attr,
+ st.reverse ? SHADOW_ALL : SHADOW_NORMAL);
}
/* Show or hide the cursor */
static void showcursor(int yes)
{
- static com32sys_t ireg;
-
- ireg.eax.b[1] = 0x01;
- ireg.ecx.w[0] = yes ? st.cursor_type : 0x2020;
- __intcall(0x10, &ireg, NULL);
+ (void)yes;
+ /* Do something here */
}
static void vesacon_putchar(int ch)
{
static com32sys_t ireg;
- const int rows = BIOS_ROWS ? BIOS_ROWS+1 : 25;
- const int cols = BIOS_COLS;
- const int page = BIOS_PAGE;
- struct curxy xy = BIOS_CURXY[page];
+ const int rows = __vesacon_text_rows;
+ const int cols = VIDEO_X_SIZE/FONT_WIDTH;
+ const int page = 0;
+ struct curxy xy = st.xy;
switch ( st.state ) {
case st_init:
@@ -201,12 +202,8 @@ static void vesacon_putchar(int ch)
if ( st.vtgraphics && (ch & 0xe0) == 0x60 )
ch = decvt_to_cp437[ch - 0x60];
- ireg.eax.b[1] = 0x09;
- ireg.eax.b[0] = ch;
- ireg.ebx.b[1] = page;
- ireg.ebx.b[0] = st.attr;
- ireg.ecx.w[0] = 1;
- __intcall(0x10, &ireg, NULL);
+ __vesacon_write_char(xy.x, xy.y, ch, st.attr,
+ st.reverse ? SHADOW_ALL : SHADOW_NORMAL);
xy.x++;
}
break;
@@ -492,20 +489,12 @@ static void vesacon_putchar(int ch)
}
while ( xy.y >= rows ) {
xy.y--;
- ireg.eax.w[0] = 0x0601;
- ireg.ebx.b[1] = st.attr;
- ireg.ecx.w[0] = 0;
- ireg.edx.b[1] = rows-1;
- ireg.edx.b[0] = cols-1;
- __intcall(0x10, &ireg, NULL); /* Scroll */
+ __vesacon_scroll_up(1, st.attr, st.reverse ? SHADOW_ALL : SHADOW_NORMAL);
}
/* Update cursor position */
- ireg.eax.b[1] = 0x02;
- ireg.ebx.b[1] = page;
- ireg.edx.b[1] = xy.y;
- ireg.edx.b[0] = xy.x;
- __intcall(0x10, &ireg, NULL);
+ /* vesacon_set_cursor(xy.x, xy.y); */
+ st.xy = xy;
}
@@ -532,6 +521,6 @@ const struct output_dev dev_vesacon_w = {
.flags = __DEV_TTY | __DEV_OUTPUT,
.fileflags = O_WRONLY | O_CREAT | O_TRUNC | O_APPEND,
.write = __vesacon_write,
- .close = NULL,
- .init = vesacon_init,
+ .close = __vesacon_close,
+ .open = __vesacon_open,
};
diff --git a/com32/lib/sys/vesaserial_write.c b/com32/lib/sys/vesaserial_write.c
new file mode 100644
index 00000000..da09a687
--- /dev/null
+++ b/com32/lib/sys/vesaserial_write.c
@@ -0,0 +1,58 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2004 H. Peter Anvin - All Rights Reserved
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall
+ * be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * vesaserial_write.c
+ *
+ * Write to both to the VESA console and the serial port
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <com32.h>
+#include <minmax.h>
+#include "file.h"
+
+extern int __vesacon_open(void);
+extern int __vesacon_close(struct file_info *);
+extern ssize_t __vesacon_write(struct file_info *, const void *, size_t);
+extern ssize_t __serial_write(struct file_info *, const void *, size_t);
+
+static ssize_t __vesaserial_write(struct file_info *fp, const void *buf, size_t count)
+{
+ __vesacon_write(fp, buf, count);
+ return __serial_write(fp, buf, count);
+}
+
+const struct output_dev dev_vesaserial_w = {
+ .dev_magic = __DEV_MAGIC,
+ .flags = __DEV_TTY | __DEV_OUTPUT,
+ .fileflags = O_WRONLY | O_CREAT | O_TRUNC | O_APPEND,
+ .write = __vesaserial_write,
+ .close = __vesacon_close,
+ .open = __vesacon_open,
+};
diff --git a/com32/modules/Makefile b/com32/modules/Makefile
index fc8c8619..971842e9 100644
--- a/com32/modules/Makefile
+++ b/com32/modules/Makefile
@@ -44,7 +44,7 @@ AUXDIR = $(LIBDIR)/syslinux
INCDIR = /usr/include
COM32DIR = $(AUXDIR)/com32
-MODULES = chain.c32 menu.c32 ethersel.c32 mboot.c32 dmitest.c32
+MODULES = chain.c32 menu.c32 vesamenu.c32 ethersel.c32 mboot.c32 dmitest.c32
TESTFILES = menu.lnx
all: $(MODULES) $(TESTFILES)
@@ -82,6 +82,9 @@ dmitest.elf : dmitest.o dmi_utils.o dmi.o $(LIBS)
menu.elf : menu.o readconfig.o $(LIBS)
$(LD) $(LDFLAGS) -o $@ $^
+vesamenu.elf : vesamenu.o readconfig.o $(LIBS)
+ $(LD) $(LDFLAGS) -o $@ $^
+
menu.lnx : menu.lo readconfig.lo $(LNXLIBS)
$(CC) $(LNXLDFLAGS) -o $@ $^
diff --git a/com32/modules/vesamenu.c b/com32/modules/vesamenu.c
new file mode 100644
index 00000000..f20fc788
--- /dev/null
+++ b/com32/modules/vesamenu.c
@@ -0,0 +1,856 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2004-2006 H. Peter Anvin - All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+ * Boston MA 02111-1307, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * menu.c
+ *
+ * Simple menu system which displays a list and allows the user to select
+ * a command line and/or edit it.
+ */
+
+#define _GNU_SOURCE /* Needed for asprintf() on Linux */
+#include <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <console.h>
+#include <getkey.h>
+#include <minmax.h>
+#include <setjmp.h>
+#include <limits.h>
+#include <sha1.h>
+#include <base64.h>
+#ifdef __COM32__
+#include <com32.h>
+#endif
+
+#include "menu.h"
+
+struct menu_attrib {
+ const char *border; /* Border area */
+ const char *title; /* Title bar */
+ const char *unsel; /* Unselected menu item */
+ const char *hotkey; /* Unselected hotkey */
+ const char *sel; /* Selected */
+ const char *hotsel; /* Selected hotkey */
+ const char *scrollbar; /* Scroll bar */
+ const char *tabmsg; /* Press [Tab] message */
+ const char *cmdmark; /* Command line marker */
+ const char *cmdline; /* Command line */
+ const char *screen; /* Rest of the screen */
+ const char *pwdborder; /* Password box border */
+ const char *pwdheader; /* Password box header */
+ const char *pwdentry; /* Password box contents */
+ const char *timeout_msg; /* Timeout message */
+ const char *timeout; /* Timeout counter */
+};
+
+static const struct menu_attrib default_attrib = {
+ .border = "\033[0;30;44m",
+ .title = "\033[1;36;44m",
+ .unsel = "\033[0;37;44m",
+ .hotkey = "\033[1;37;44m",
+ .sel = "\033[0;7;37;40m",
+ .hotsel = "\033[1;7;37;40m",
+ .scrollbar = "\033[0;30;44m",
+ .tabmsg = "\033[0;31;40m",
+ .cmdmark = "\033[1;36;40m",
+ .cmdline = "\033[0;37;40m",
+ .screen = "\033[0;37;40m",
+ .pwdborder = "\033[0;30;47m",
+ .pwdheader = "\033[0;31;47m",
+ .pwdentry = "\033[0;30;47m",
+ .timeout_msg = "\033[0;37;40m",
+ .timeout = "\033[1;37;40m",
+};
+
+static const struct menu_attrib *menu_attrib = &default_attrib;
+
+struct menu_parameter mparm[] = {
+ { "width", 80 },
+ { "margin", 10 },
+ { "passwordmargin", 3 },
+ { "rows", 12 },
+ { "tabmsgrow", 18 },
+ { "cmdlinerow", 18 },
+ { "endrow", 24 },
+ { "passwordrow", 11 },
+ { "timeoutrow", 20 },
+ { NULL, 0 }
+};
+
+#define WIDTH mparm[0].value
+#define MARGIN mparm[1].value
+#define PASSWD_MARGIN mparm[2].value
+#define MENU_ROWS mparm[3].value
+#define TABMSG_ROW mparm[4].value
+#define CMDLINE_ROW mparm[5].value
+#define END_ROW mparm[6].value
+#define PASSWD_ROW mparm[7].value
+#define TIMEOUT_ROW mparm[8].value
+
+static char *
+pad_line(const char *text, int align, int width)
+{
+ static char buffer[MAX_CMDLINE_LEN];
+ int n, p;
+
+ if ( width >= (int) sizeof buffer )
+ return NULL; /* Can't do it */
+
+ n = strlen(text);
+ if ( n >= width )
+ n = width;
+
+ memset(buffer, ' ', width);
+ buffer[width] = 0;
+ p = ((width-n)*align)>>1;
+ memcpy(buffer+p, text, n);
+
+ return buffer;
+}
+
+/* Display an entry, with possible hotkey highlight. Assumes
+ that the current attribute is the non-hotkey one, and will
+ guarantee that as an exit condition as well. */
+static void
+display_entry(const struct menu_entry *entry, const char *attrib,
+ const char *hotattrib, int width)
+{
+ const char *p = entry->displayname;
+
+ while ( width ) {
+ if ( *p ) {
+ if ( *p == '^' ) {
+ p++;
+ if ( *p && ((unsigned char)*p & ~0x20) == entry->hotkey ) {
+ fputs(hotattrib, stdout);
+ putchar(*p++);
+ fputs(attrib, stdout);
+ width--;
+ }
+ } else {
+ putchar(*p++);
+ width--;
+ }
+ } else {
+ putchar(' ');
+ width--;
+ }
+ }
+}
+
+static void
+draw_row(int y, int sel, int top, int sbtop, int sbbot)
+{
+ int i = (y-4)+top;
+
+ printf("\033[%d;%dH%s\016x\017%s ",
+ y, MARGIN+1, menu_attrib->border,
+ (i == sel) ? menu_attrib->sel : menu_attrib->unsel);
+
+ if ( i >= nentries ) {
+ fputs(pad_line("", 0, WIDTH-2*MARGIN-4), stdout);
+ } else {
+ display_entry(&menu_entries[i],
+ (i == sel) ? menu_attrib->sel : menu_attrib->unsel,
+ (i == sel) ? menu_attrib->hotsel : menu_attrib->hotkey,
+ WIDTH-2*MARGIN-4);
+ }
+
+ if ( nentries <= MENU_ROWS ) {
+ printf(" %s\016x\017", menu_attrib->border);
+ } else if ( sbtop > 0 ) {
+ if ( y >= sbtop && y <= sbbot )
+ printf(" %s\016a\017", menu_attrib->scrollbar);
+ else
+ printf(" %s\016x\017", menu_attrib->border);
+ } else {
+ putchar(' '); /* Don't modify the scrollbar */
+ }
+}
+
+static int
+passwd_compare(const char *passwd, const char *entry)
+{
+ const char *p;
+ SHA1_CTX ctx;
+ unsigned char sha1[20], pwdsha1[20];
+
+ if ( passwd[0] != '$' ) /* Plaintext passwd, yuck! */
+ return !strcmp(entry, passwd);
+
+ if ( strncmp(passwd, "$4$", 3) )
+ return 0; /* Only SHA-1 passwds supported */
+
+ SHA1Init(&ctx);
+
+ if ( (p = strchr(passwd+3, '$')) ) {
+ SHA1Update(&ctx, passwd+3, p-(passwd+3));
+ p++;
+ } else {
+ p = passwd+3; /* Assume no salt */
+ }
+
+ SHA1Update(&ctx, entry, strlen(entry));
+ SHA1Final(sha1, &ctx);
+
+ memset(pwdsha1, 0, 20);
+ unbase64(pwdsha1, 20, p);
+
+ return !memcmp(sha1, pwdsha1, 20);
+}
+
+static jmp_buf timeout_jump;
+
+static int mygetkey(clock_t timeout)
+{
+ clock_t t0, t;
+ clock_t tto, to;
+ int key;
+
+ if ( !totaltimeout )
+ return get_key(stdin, timeout);
+
+ for (;;) {
+ tto = min(totaltimeout, INT_MAX);
+ to = timeout ? min(tto, timeout) : tto;
+
+ t0 = times(NULL);
+ key = get_key(stdin, to);
+ t = times(NULL) - t0;
+
+ if ( totaltimeout <= t )
+ longjmp(timeout_jump, 1);
+
+ totaltimeout -= t;
+
+ if ( key != KEY_NONE )
+ return key;
+
+ if ( timeout ) {
+ if ( timeout <= t )
+ return KEY_NONE;
+
+ timeout -= t;
+ }
+ }
+}
+
+static int
+ask_passwd(const char *menu_entry)
+{
+ static const char title[] = "Password required";
+ char user_passwd[WIDTH], *p;
+ int done;
+ int key;
+ int x;
+
+ printf("\033[%d;%dH%s\016l", PASSWD_ROW, PASSWD_MARGIN+1,
+ menu_attrib->pwdborder);
+ for ( x = 2 ; x <= WIDTH-2*PASSWD_MARGIN-1 ; x++ )
+ putchar('q');
+
+ printf("k\033[%d;%dHx", PASSWD_ROW+1, PASSWD_MARGIN+1);
+ for ( x = 2 ; x <= WIDTH-2*PASSWD_MARGIN-1 ; x++ )
+ putchar(' ');
+
+ printf("x\033[%d;%dHm", PASSWD_ROW+2, PASSWD_MARGIN+1);
+ for ( x = 2 ; x <= WIDTH-2*PASSWD_MARGIN-1 ; x++ )
+ putchar('q');
+
+ printf("j\017\033[%d;%dH%s %s \033[%d;%dH%s",
+ PASSWD_ROW, (WIDTH-((int)sizeof(title)+1))/2,
+ menu_attrib->pwdheader, title,
+ PASSWD_ROW+1, PASSWD_MARGIN+3, menu_attrib->pwdentry);
+
+ /* Actually allow user to type a password, then compare to the SHA1 */
+ done = 0;
+ p = user_passwd;
+
+ while ( !done ) {
+ key = mygetkey(0);
+
+ switch ( key ) {
+ case KEY_ENTER:
+ case KEY_CTRL('J'):
+ done = 1;
+ break;
+
+ case KEY_ESC:
+ case KEY_CTRL('C'):
+ p = user_passwd; /* No password entered */
+ done = 1;
+ break;
+
+ case KEY_BACKSPACE:
+ case KEY_DEL:
+ case KEY_DELETE:
+ if ( p > user_passwd ) {
+ printf("\b \b");
+ p--;
+ }
+ break;
+
+ case KEY_CTRL('U'):
+ while ( p > user_passwd ) {
+ printf("\b \b");
+ p--;
+ }
+ break;
+
+ default:
+ if ( key >= ' ' && key <= 0xFF &&
+ (p-user_passwd) < WIDTH-2*PASSWD_MARGIN-5 ) {
+ *p++ = key;
+ putchar('*');
+ }
+ break;
+ }
+ }
+
+ if ( p == user_passwd )
+ return 0; /* No password entered */
+
+ *p = '\0';
+
+ return (menu_master_passwd && passwd_compare(menu_master_passwd, user_passwd))
+ || (menu_entry && passwd_compare(menu_entry, user_passwd));
+}
+
+
+static void
+draw_menu(int sel, int top, int edit_line)
+{
+ int x, y;
+ int sbtop = 0, sbbot = 0;
+
+ if ( nentries > MENU_ROWS ) {
+ int sblen = MENU_ROWS*MENU_ROWS/nentries;
+ sbtop = (MENU_ROWS-sblen+1)*top/(nentries-MENU_ROWS+1);
+ sbbot = sbtop + sblen - 1;
+
+ sbtop += 4; sbbot += 4; /* Starting row of scrollbar */
+ }
+
+ printf("\033[1;%dH%s\016l", MARGIN+1, menu_attrib->border);
+ for ( x = 2 ; x <= WIDTH-2*MARGIN-1 ; x++ )
+ putchar('q');
+
+ printf("k\033[2;%dH%sx\017%s %s %s\016x",
+ MARGIN+1,
+ menu_attrib->border,
+ menu_attrib->title,
+ pad_line(menu_title, 1, WIDTH-2*MARGIN-4),
+ menu_attrib->border);
+
+ printf("\033[3;%dH%st", MARGIN+1, menu_attrib->border);
+ for ( x = 2 ; x <= WIDTH-2*MARGIN-1 ; x++ )
+ putchar('q');
+ fputs("u\017", stdout);
+
+ for ( y = 4 ; y < 4+MENU_ROWS ; y++ )
+ draw_row(y, sel, top, sbtop, sbbot);
+
+ printf("\033[%d;%dH%s\016m", y, MARGIN+1, menu_attrib->border);
+ for ( x = 2 ; x <= WIDTH-2*MARGIN-1 ; x++ )
+ putchar('q');
+ fputs("j\017", stdout);
+
+ if ( edit_line && allowedit && !menu_master_passwd )
+ printf("%s\033[%d;1H%s", menu_attrib->tabmsg, TABMSG_ROW,
+ pad_line("Press [Tab] to edit options", 1, WIDTH));
+
+ printf("%s\033[%d;1H", menu_attrib->screen, END_ROW);
+}
+
+static const char *
+edit_cmdline(char *input, int top)
+{
+ static char cmdline[MAX_CMDLINE_LEN];
+ int key, len, prev_len, cursor;
+ int redraw = 1; /* We enter with the menu already drawn */
+
+ strncpy(cmdline, input, MAX_CMDLINE_LEN);
+ cmdline[MAX_CMDLINE_LEN-1] = '\0';
+
+ len = cursor = strlen(cmdline);
+ prev_len = 0;
+
+ for (;;) {
+ if ( redraw > 1 ) {
+ /* Clear and redraw whole screen */
+ /* Enable ASCII on G0 and DEC VT on G1; do it in this order
+ to avoid confusing the Linux console */
+ printf("\033e\033%%@\033)0\033(B%s\033[?25l\033[2J", menu_attrib->screen);
+ draw_menu(-1, top, 1);
+ prev_len = 0;
+ }
+
+ if ( redraw > 0 ) {
+ /* Redraw the command line */
+ printf("\033[?25l\033[%d;1H%s> %s%s",
+ CMDLINE_ROW, menu_attrib->cmdmark,
+ menu_attrib->cmdline, pad_line(cmdline, 0, prev_len));
+ printf("%s\033[%d;3H%s\033[?25h",
+ menu_attrib->cmdline, CMDLINE_ROW, pad_line(cmdline, 0, cursor));
+ prev_len = len;
+ redraw = 0;
+ }
+
+ key = mygetkey(0);
+
+ switch( key ) {
+ case KEY_CTRL('L'):
+ redraw = 2;
+ break;
+
+ case KEY_ENTER:
+ case KEY_CTRL('J'):
+ return cmdline;
+
+ case KEY_ESC:
+ case KEY_CTRL('C'):
+ return NULL;
+
+ case KEY_BACKSPACE:
+ case KEY_DEL:
+ if ( cursor ) {
+ memmove(cmdline+cursor-1, cmdline+cursor, len-cursor+1);
+ len--;
+ cursor--;
+ redraw = 1;
+ }
+ break;
+
+ case KEY_CTRL('D'):
+ case KEY_DELETE:
+ if ( cursor < len ) {
+ memmove(cmdline+cursor, cmdline+cursor+1, len-cursor);
+ len--;
+ redraw = 1;
+ }
+ break;
+
+ case KEY_CTRL('U'):
+ if ( len ) {
+ len = cursor = 0;
+ cmdline[len] = '\0';
+ redraw = 1;
+ }
+ break;
+
+ case KEY_CTRL('W'):
+ if ( cursor ) {
+ int prevcursor = cursor;
+
+ while ( cursor && my_isspace(cmdline[cursor-1]) )
+ cursor--;
+
+ while ( cursor && !my_isspace(cmdline[cursor-1]) )
+ cursor--;
+
+ memmove(cmdline+cursor, cmdline+prevcursor, len-prevcursor+1);
+ len -= (cursor-prevcursor);
+ redraw = 1;
+ }
+ break;
+
+ case KEY_LEFT:
+ case KEY_CTRL('B'):
+ if ( cursor ) {
+ cursor--;
+ redraw = 1;
+ }
+ break;
+
+ case KEY_RIGHT:
+ case KEY_CTRL('F'):
+ if ( cursor < len ) {
+ putchar(cmdline[cursor++]);
+ }
+ break;
+
+ case KEY_CTRL('K'):
+ if ( cursor < len ) {
+ cmdline[len = cursor] = '\0';
+ redraw = 1;
+ }
+ break;
+
+ case KEY_HOME:
+ case KEY_CTRL('A'):
+ if ( cursor ) {
+ cursor = 0;
+ redraw = 1;
+ }
+ break;
+
+ case KEY_END:
+ case KEY_CTRL('E'):
+ if ( cursor != len ) {
+ cursor = len;
+ redraw = 1;
+ }
+ break;
+
+ default:
+ if ( key >= ' ' && key <= 0xFF && len < MAX_CMDLINE_LEN-1 ) {
+ if ( cursor == len ) {
+ cmdline[len] = key;
+ cmdline[++len] = '\0';
+ cursor++;
+ putchar(key);
+ prev_len++;
+ } else {
+ memmove(cmdline+cursor+1, cmdline+cursor, len-cursor+1);
+ cmdline[cursor++] = key;
+ len++;
+ redraw = 1;
+ }
+ }
+ break;
+ }
+ }
+}
+
+static void
+clear_screen(void)
+{
+ printf("\033e\033%%@\033)0\033(B%s\033[?25l\033[2J", menu_attrib->screen);
+}
+
+static inline int
+shift_is_held(void)
+{
+ uint8_t shift_bits = *(uint8_t *)0x417;
+
+ return !!(shift_bits & 0x5d); /* Caps/Scroll/Alt/Shift */
+}
+
+static const char *
+run_menu(void)
+{
+ int key;
+ int done = 0;
+ volatile int entry = defentry, prev_entry = -1;
+ int top = 0, prev_top = -1;
+ int clear = 1, to_clear;
+ const char *cmdline = NULL;
+ volatile clock_t key_timeout, timeout_left, this_timeout;
+
+ /* Note: for both key_timeout and timeout == 0 means no limit */
+ timeout_left = key_timeout = timeout;
+
+ /* If we're in shiftkey mode, exit immediately unless a shift key is pressed */
+ if ( shiftkey && !shift_is_held() ) {
+ return menu_entries[defentry].cmdline;
+ }
+
+ /* Handle both local and global timeout */
+ if ( setjmp(timeout_jump) ) {
+ entry = defentry;
+
+ if ( top < 0 || top < entry-MENU_ROWS+1 )
+ top = max(0, entry-MENU_ROWS+1);
+ else if ( top > entry || top > max(0,nentries-MENU_ROWS) )
+ top = min(entry, max(0,nentries-MENU_ROWS));
+
+ draw_menu(ontimeout ? -1 : entry, top, 1);
+ cmdline = ontimeout ? ontimeout : menu_entries[entry].cmdline;
+ done = 1;
+ }
+
+ while ( !done ) {
+ if ( entry < 0 )
+ entry = 0;
+ else if ( entry >= nentries )
+ entry = nentries-1;
+
+ if ( top < 0 || top < entry-MENU_ROWS+1 )
+ top = max(0, entry-MENU_ROWS+1);
+ else if ( top > entry || top > max(0,nentries-MENU_ROWS) )
+ top = min(entry, max(0,nentries-MENU_ROWS));
+
+ /* Start with a clear screen */
+ if ( clear ) {
+ /* Clear and redraw whole screen */
+ /* Enable ASCII on G0 and DEC VT on G1; do it in this order
+ to avoid confusing the Linux console */
+ clear_screen();
+ clear = 0;
+ prev_entry = prev_top = -1;
+ }
+
+ if ( top != prev_top ) {
+ draw_menu(entry, top, 1);
+ } else if ( entry != prev_entry ) {
+ draw_row(prev_entry-top+4, entry, top, 0, 0);
+ draw_row(entry-top+4, entry, top, 0, 0);
+ }
+
+ prev_entry = entry; prev_top = top;
+
+ /* Cursor movement cancels timeout */
+ if ( entry != defentry )
+ key_timeout = 0;
+
+ if ( key_timeout ) {
+ int tol = timeout_left/CLK_TCK;
+ int nc = snprintf(NULL, 0, " Automatic boot in %d seconds ", tol);
+ printf("\033[%d;%dH%s Automatic boot in %s%d%s seconds ",
+ TIMEOUT_ROW, 1+((WIDTH-nc)>>1),
+ menu_attrib->timeout_msg,
+ menu_attrib->timeout, tol,
+ menu_attrib->timeout_msg);
+ to_clear = 1;
+ } else {
+ to_clear = 0;
+ }
+
+ this_timeout = min(min(key_timeout, timeout_left), CLK_TCK);
+ key = mygetkey(this_timeout);
+
+ if ( key != KEY_NONE ) {
+ timeout_left = key_timeout;
+ if ( to_clear )
+ printf("\033[%d;1H%s\033[K", TIMEOUT_ROW, menu_attrib->screen);
+ }
+
+ switch ( key ) {
+ case KEY_NONE: /* Timeout */
+ /* This is somewhat hacky, but this at least lets the user
+ know what's going on, and still deals with "phantom inputs"
+ e.g. on serial ports.
+
+ Warning: a timeout will boot the default entry without any
+ password! */
+ if ( key_timeout ) {
+ if ( timeout_left <= this_timeout )
+ longjmp(timeout_jump, 1);
+
+ timeout_left -= this_timeout;
+ }
+ break;
+
+ case KEY_CTRL('L'):
+ clear = 1;
+ break;
+
+ case KEY_ENTER:
+ case KEY_CTRL('J'):
+ key_timeout = 0; /* Cancels timeout */
+ if ( menu_entries[entry].passwd ) {
+ clear = 1;
+ done = ask_passwd(menu_entries[entry].passwd);
+ } else {
+ done = 1;
+ }
+ cmdline = menu_entries[entry].cmdline;
+ break;
+
+ case KEY_UP:
+ case KEY_CTRL('P'):
+ if ( entry > 0 ) {
+ entry--;
+ if ( entry < top )
+ top -= MENU_ROWS;
+ }
+ break;
+
+ case KEY_DOWN:
+ case KEY_CTRL('N'):
+ if ( entry < nentries-1 ) {
+ entry++;
+ if ( entry >= top+MENU_ROWS )
+ top += MENU_ROWS;
+ }
+ break;
+
+ case KEY_PGUP:
+ case KEY_LEFT:
+ case KEY_CTRL('B'):
+ case '<':
+ entry -= MENU_ROWS;
+ top -= MENU_ROWS;
+ break;
+
+ case KEY_PGDN:
+ case KEY_RIGHT:
+ case KEY_CTRL('F'):
+ case '>':
+ case ' ':
+ entry += MENU_ROWS;
+ top += MENU_ROWS;
+ break;
+
+ case '-':
+ entry--;
+ top--;
+ break;
+
+ case '+':
+ entry++;
+ top++;
+ break;
+
+ case KEY_CTRL('A'):
+ case KEY_HOME:
+ top = entry = 0;
+ break;
+
+ case KEY_CTRL('E'):
+ case KEY_END:
+ entry = nentries - 1;
+ top = max(0, nentries-MENU_ROWS);
+ break;
+
+ case KEY_TAB:
+ if ( allowedit ) {
+ int ok = 1;
+
+ key_timeout = 0; /* Cancels timeout */
+ draw_row(entry-top+4, -1, top, 0, 0);
+
+ if ( menu_master_passwd ) {
+ ok = ask_passwd(NULL);
+ clear_screen();
+ draw_menu(-1, top, 0);
+ } else {
+ /* Erase [Tab] message */
+ printf("\033[%d;1H%s\033[K", TABMSG_ROW, menu_attrib->screen);
+ }
+
+ if ( ok ) {
+ cmdline = edit_cmdline(menu_entries[entry].cmdline, top);
+ done = !!cmdline;
+ clear = 1; /* In case we hit [Esc] and done is null */
+ } else {
+ draw_row(entry-top+4, entry, top, 0, 0);
+ }
+ }
+ break;
+ case KEY_CTRL('C'): /* Ctrl-C */
+ case KEY_ESC: /* Esc */
+ if ( allowedit ) {
+ done = 1;
+ clear = 1;
+ key_timeout = 0;
+
+ draw_row(entry-top+4, -1, top, 0, 0);
+
+ if ( menu_master_passwd )
+ done = ask_passwd(NULL);
+ }
+ break;
+ default:
+ if ( key > 0 && key < 0xFF ) {
+ key &= ~0x20; /* Upper case */
+ if ( menu_hotkeys[key] ) {
+ key_timeout = 0;
+ entry = menu_hotkeys[key] - menu_entries;
+ /* Should we commit at this point? */
+ }
+ }
+ break;
+ }
+ }
+
+ printf("\033[?25h"); /* Show cursor */
+
+ /* Return the label name so localboot and ipappend work */
+ return cmdline;
+}
+
+
+static void
+execute(const char *cmdline)
+{
+#ifdef __COM32__
+ com32sys_t ireg;
+ const char *p;
+ char *q = __com32.cs_bounce;
+ const char *kernel, *args;
+
+ memset(&ireg, 0, sizeof ireg);
+
+ kernel = q;
+ p = cmdline;
+ while ( *p && !my_isspace(*p) ) {
+ *q++ = *p++;
+ }
+ *q++ = '\0';
+
+ args = q;
+ while ( *p && my_isspace(*p) )
+ p++;
+
+ strcpy(q, p);
+
+ if ( !strcmp(kernel, ".localboot") ) {
+ ireg.eax.w[0] = 0x0014; /* Local boot */
+ ireg.edx.w[0] = strtoul(args, NULL, 0);
+ } else {
+ ireg.eax.w[0] = 0x0016; /* Run kernel image */
+ ireg.esi.w[0] = OFFS(kernel);
+ ireg.ds = SEG(kernel);
+ ireg.ebx.w[0] = OFFS(args);
+ ireg.es = SEG(args);
+ /* ireg.ecx.l = 0; */ /* We do ipappend "manually" */
+ /* ireg.edx.l = 0; */
+ }
+
+ __intcall(0x22, &ireg, NULL);
+
+ /* If this returns, something went bad; return to menu */
+#else
+ /* For testing... */
+ printf("\n\033[0m>>> %s\n", cmdline);
+ exit(0);
+#endif
+}
+
+static void __attribute__((destructor)) console_cleanup(void)
+{
+ /* For the serial console, be nice and clean up */
+ fputs("\033[0m\033[20l", stdout);
+}
+
+int vesacon_load_background(const char *filename);
+
+int main(int argc, char *argv[])
+{
+ const char *cmdline;
+
+ (void)argc;
+
+ /* console_ansi_raw(); */
+ openconsole(&dev_rawcon_r, &dev_vesaserial_w);
+ vesacon_load_background("stacy.png");
+
+ parse_config(argv[1]);
+
+ if ( !nentries ) {
+ fputs("No LABEL entries found in configuration file!\n", stdout);
+ return 1; /* Error! */
+ }
+
+ for(;;) {
+ cmdline = run_menu();
+
+ printf("\033[?25h\033[%d;1H\033[0m", END_ROW);
+ if ( cmdline )
+ execute(cmdline);
+ else
+ return 0; /* Exit */
+ }
+}
diff --git a/com32/samples/vesa.c b/com32/samples/vesa.c
index 6d148ae6..159a55d7 100644
--- a/com32/samples/vesa.c
+++ b/com32/samples/vesa.c
@@ -18,7 +18,7 @@ int main(void)
for (attr = 0; attr < 256; attr++) {
snprintf(attr_buf, sizeof attr_buf, " [%02X] ", attr);
- vesacon_write_at(row, col, attr_buf, attr, attr & 3);
+ __vesacon_write_at(row, col, attr_buf, attr, attr & 3);
row++;
if (row >= 29) {
row = 0;