diff options
author | H. Peter Anvin <hpa@zytor.com> | 2006-08-31 15:48:26 -0700 |
---|---|---|
committer | H. Peter Anvin <hpa@zytor.com> | 2006-08-31 15:48:26 -0700 |
commit | 6f00093c652fdbf1c9679100cc21906314b2a859 (patch) | |
tree | 57181cc3f0d3b5313046f5c7bd579824dee25888 | |
parent | 0c04c3635c840e8959705900a89c072cb42a5864 (diff) | |
download | syslinux-6f00093c652fdbf1c9679100cc21906314b2a859.tar.gz |
More work on actual VESA console and menu. Shadowing still doesn't work
quite right, however.
-rw-r--r-- | com32/include/console.h | 5 | ||||
-rw-r--r-- | com32/lib/Makefile | 9 | ||||
-rwxr-xr-x | com32/lib/sys/vesa/alphatbl.pl | 48 | ||||
-rw-r--r-- | com32/lib/sys/vesa/drawtxt.c | 79 | ||||
-rw-r--r-- | com32/lib/sys/vesa/initvesa.c | 78 | ||||
-rw-r--r-- | com32/lib/sys/vesa/video.h | 9 | ||||
-rw-r--r-- | com32/lib/sys/vesacon_write.c | 109 | ||||
-rw-r--r-- | com32/lib/sys/vesaserial_write.c | 58 | ||||
-rw-r--r-- | com32/modules/Makefile | 5 | ||||
-rw-r--r-- | com32/modules/vesamenu.c | 856 | ||||
-rw-r--r-- | com32/samples/vesa.c | 2 |
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; |