From 09e69605b16070de8ce317d86ad736d665a58906 Mon Sep 17 00:00:00 2001 From: Marcus Brinkmann Date: Tue, 17 Sep 2002 12:26:10 +0000 Subject: 2002-09-17 Marcus Brinkmann * Makefile (prog-subdirs): Add console-client. sutils/ 2002-09-17 Marcus Brinkmann * MAKEDEV.sh (mkdev: vcs): New console device. (mkdev: tty[0-9a-f]|tty[0-9][0-9a-f]): Replaced with new rules for tty[1-9][0-9]. utils/ 2002-09-17 Marcus Brinkmann * console-ncurses.c: File removed (the ncursesw console client is now a driver in the console-client). * Makefile: Revert 2002-08-22 change: Do not include`../config.make'. (targets) [LIBNCURSES]: Removed. (SRCS) [LIBNCURSES]: Likewise. (HURDLIBS) [LIBNCURSES]: Likewise. (console-ncurses): Target removed. (console-ncurses-CPPFLAGS): Removed. (console-ncurses-LDLIBS): Likewise. console-client/ 2002-09-17 Marcus Brinkmann * Makefile, bdf.c, bdf.h, bell.h, console.c, display.h, driver.c, driver.h, generic-speaker.c, input.h, pc-kbd.c, timer.c, timer.h, unicode.h, vga.c, vga-dynacolor.c, vga-dynacolor.h, vga-dynafont.c, vga-dynafont.h, vga-hw.h, vga-support.c, vga-support.h: New file. --- console-client/ChangeLog | 6 + console-client/Makefile | 75 +++ console-client/bdf.c | 990 +++++++++++++++++++++++++++++++++++ console-client/bdf.h | 272 ++++++++++ console-client/bell.h | 56 ++ console-client/console.c | 468 +++++++++++++++++ console-client/display.h | 138 +++++ console-client/driver.c | 353 +++++++++++++ console-client/driver.h | 282 ++++++++++ console-client/generic-speaker.c | 488 ++++++++++++++++++ console-client/input.h | 83 +++ console-client/ncursesw.c | 611 ++++++++++++++++++++++ console-client/pc-kbd.c | 824 ++++++++++++++++++++++++++++++ console-client/timer.c | 210 ++++++++ console-client/timer.h | 72 +++ console-client/unicode.h | 350 +++++++++++++ console-client/vga-dynacolor.c | 322 ++++++++++++ console-client/vga-dynacolor.h | 108 ++++ console-client/vga-dynafont.c | 1047 ++++++++++++++++++++++++++++++++++++++ console-client/vga-dynafont.h | 83 +++ console-client/vga-hw.h | 139 +++++ console-client/vga-support.c | 480 +++++++++++++++++ console-client/vga-support.h | 86 ++++ console-client/vga.c | 573 +++++++++++++++++++++ 24 files changed, 8116 insertions(+) create mode 100644 console-client/ChangeLog create mode 100644 console-client/Makefile create mode 100644 console-client/bdf.c create mode 100644 console-client/bdf.h create mode 100644 console-client/bell.h create mode 100644 console-client/console.c create mode 100644 console-client/display.h create mode 100644 console-client/driver.c create mode 100644 console-client/driver.h create mode 100644 console-client/generic-speaker.c create mode 100644 console-client/input.h create mode 100644 console-client/ncursesw.c create mode 100644 console-client/pc-kbd.c create mode 100644 console-client/timer.c create mode 100644 console-client/timer.h create mode 100644 console-client/unicode.h create mode 100644 console-client/vga-dynacolor.c create mode 100644 console-client/vga-dynacolor.h create mode 100644 console-client/vga-dynafont.c create mode 100644 console-client/vga-dynafont.h create mode 100644 console-client/vga-hw.h create mode 100644 console-client/vga-support.c create mode 100644 console-client/vga-support.h create mode 100644 console-client/vga.c (limited to 'console-client') diff --git a/console-client/ChangeLog b/console-client/ChangeLog new file mode 100644 index 00000000..c43a5dfa --- /dev/null +++ b/console-client/ChangeLog @@ -0,0 +1,6 @@ +2002-09-17 Marcus Brinkmann + + * Makefile, bdf.c, bdf.h, bell.h, console.c, display.h, driver.c, + driver.h, generic-speaker.c, input.h, pc-kbd.c, timer.c, timer.h, + unicode.h, vga.c, vga-dynacolor.c, vga-dynacolor.h, vga-dynafont.c, + vga-dynafont.h, vga-hw.h, vga-support.c, vga-support.h: New file. diff --git a/console-client/Makefile b/console-client/Makefile new file mode 100644 index 00000000..836ec514 --- /dev/null +++ b/console-client/Makefile @@ -0,0 +1,75 @@ +# +# Copyright (C) 1994,95,96,97,98,99,2000,01,02 Free Software Foundation, Inc. +# +# 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; either version 2, or (at +# your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +dir := console-client +makemode := utility + +target = console +SRCS = console.c timer.c driver.c +LCLHDRS = timer.h driver.h display.h input.h bell.h \ + unicode.h bdf.h \ + vga-dynafont.h vga-dynacolor.h vga-hw.h vga.h + +OBJS = $(filter-out %.sh,$(SRCS:.c=.o)) +HURDLIBS = cons threads ports +LDLIBS = -ldl +module-dir = $(libdir)/hurd/console +console-LDFLAGS = -Wl,-E + +# In seeking, thou shalt find it! +CPPFLAGS += -DQUAERENDO_INVENIETIS + +include ../Makeconf + +driver-CPPFLAGS = -D'CONSOLE_DEFPATH="$(module-dir)\0"' \ + -D'CONSOLE_SONAME_SUFFIX=".so.$(hurd-version)"' + +console: ../libcons/libcons.a ../libports/libports.a \ + ../libthreads/libthreads.a ../libshouldbeinlibc/libshouldbeinlibc.a + +modules = vga pc_kbd generic_speaker + +vga.so.$(hurd-version): $(patsubst %.c,%_pic.o,bdf.c vga-dynafont.c \ + vga-dynacolor.c vga-support.c vga.c) +pc_kbd.so.$(hurd-version): $(patsubst %.c,%_pic.o,pc-kbd.c) +generic_speaker.so.$(hurd-version): $(patsubst %.c,%_pic.o,generic-speaker.c) + +ifneq ($(LIBNCURSESW),) +modules += ncursesw +ncursesw.so.$(hurd-version): $(patsubst %.c,%_pic.o,ncursesw.c) +ncursesw-CPPFLAGS = $(NCURSESW_INCLUDE) +ncursesw-LDLIBS = $(LIBNCURSESW) +endif + +all: $(addsuffix .so.$(hurd-version), $(modules)) + +cleantarg += $(addsuffix .so.$(hurd-version), $(modules)) + +install: $(module-dir) $(addprefix $(module-dir)/,$(addsuffix .so.$(hurd-version),$(modules))) + +$(module-dir): + @$(MKINSTALLDIRS) $@ + +$(module-dir)/%: % + $(INSTALL_DATA) $< $@ + +# You can use this rule to make a dynamically-loadable version of any +# of the modules. +%.so.$(hurd-version): + $(CC) -shared -Wl,-soname=$@ -o $@ $(rpath) \ + $(CFLAGS) $($*-CFLAGS) $(LDFLAGS) \ + '-Wl,-(' $($*-LDLIBS) '-Wl,-)' $^ diff --git a/console-client/bdf.c b/console-client/bdf.c new file mode 100644 index 00000000..6c67c409 --- /dev/null +++ b/console-client/bdf.c @@ -0,0 +1,990 @@ +/* bdf.c - Parser for the Adobe Glyph Bitmap Distribution Format (BDF). + Copyright (C) 2002 Free Software Foundation, Inc. + Written by Marcus Brinkmann . + + This file is part of the GNU Hurd. + + The GNU Hurd 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; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#ifdef HAVE_CONFIG_H +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bdf.h" + + +/* Return a statically allocated string describing the BDF error value + ERR. */ +const char * +bdf_strerror (bdf_error_t err) +{ + switch (err) + { + case BDF_NO_ERROR: + return "Success"; + case BDF_SYSTEM_ERROR: + return "System error"; + case BDF_SYNTAX_ERROR: + return "Syntax error"; + case BDF_INVALID_ARGUMENT: + return "Invalid Argument"; + case BDF_COUNT_MISMATCH: + return "Count mismatch"; + default: + return "Unknown error"; + } +} + + +/* Copy the string starting from ARG and return the pointer to it in + STRING. If QUOTED is true, outer double quotes are stripped, and + two consecutive double quotes within the string are replaced by one + douple quotes. */ +static bdf_error_t +parse_string (char *arg, char **string, int quoted) +{ + if (quoted) + { + char *value = ++arg; + do + { + value = strchr (value, '"'); + if (!value) + return BDF_INVALID_ARGUMENT; + else if (*(value + 1) == '"') + { + char *copyp = value++; + while (*(++copyp)) + *(copyp - 1) = *copyp; + *(copyp - 1) = 0; + } + } + while (*value != '"' || *(value + 1) == '"'); + *value = 0; + } + + *string = strdup (arg); + if (!*string) + { + errno = ENOMEM; + return BDF_SYSTEM_ERROR; + } + return 0; +} + + +/* Parse the string STR for format TEMPLATE, and require an exact + match. Set err if a parsing error occurs. TEMPLATE must be a + string constant. */ +#define parse_template(str, template, rest...) \ + do \ + { \ + int parse_template_count = -1; \ + sscanf (str, template " %n", rest, &parse_template_count); \ + if (parse_template_count == -1 || *(str + parse_template_count)) \ + err = BDF_SYNTAX_ERROR; \ + } \ + while (0) + + +#define hex2nr(c) (((c) >= '0' && (c) <= '9') ? (c) - '0' \ + : (((c) >= 'a' && (c) <= 'f') ? (c) - 'a' + 10 \ + : (((c) >= 'A' && (c) <= 'F') ? (c) - 'A' + 10 : 0))) + +/* Convert a two-digit hex number starting from LINE to a char and + return the result in BYTE. */ +static bdf_error_t +parse_hexbyte (char *line, unsigned char *byte) +{ + if (!isxdigit (*line) || !isxdigit(*(line + 1))) + return BDF_SYNTAX_ERROR; + else + *byte = (hex2nr (*line) << 4) + hex2nr (*(line + 1)); + return 0; +} + + +/* Like getline(), but keeps track of line count in COUNT, skips + COMMENT lines, and removes whitespace at the beginning and end of a + line. */ +static int +next_line (char **line, int *size, FILE *file, int *count) +{ + int len; + + do + { + len = getline (line, size, file); + if (len >= 0) + { + char *cline = *line; + if (count) + (*count)++; + if (!strncmp (cline, "COMMENT", 7)) + len = 0; + else + while (len > 0 && (cline[len - 1] == '\n' || cline[len - 1] == '\r' + || cline[len - 1] == ' ' + || cline[len - 1] == '\t')) + cline[--len] = 0; + } + } + while (len <= 0 && !feof (file) && !ferror (file)); + return len; +} + + +/* Isolate the next white-space seperated argument from the current + line, and set ARGP to the beginning of the next argument. It is an + error if there is no further argument. */ +static bdf_error_t +find_arg (char **argp) +{ + char *arg = *argp; + + arg = strchr (arg, ' '); + if (arg) + { + *(arg++) = 0; + while (*arg == ' ' || *arg == '\t') + arg++; + } + if (!arg || !*arg) + return BDF_SYNTAX_ERROR; + *argp = arg; + return 0; +} + + +/* Read the font from stream FILE, and return it in FONT. If + LINECOUNT is not zero, it will contain the number of lines in the + file at success, and the line an error occured at failure. */ +bdf_error_t +bdf_read (FILE *filep, bdf_font_t *font, int *linecount) +{ + bdf_error_t err = 0; + char *line = 0; + int line_size = 0; + int len; + int done = 0; + bdf_font_t bdf; + struct + { + /* Current line. */ + enum { START, FONT, PROPERTIES, GLYPHS, GLYPH, BITMAP } location; + /* The number of properties parsed so far. */ + int properties; + /* The number of glyphs parsed so far. */ + int glyphs; + + /* True if we have seen a SIZE keyword so far. */ + int has_size : 1; + /* True if we have seen a FONTBOUNDINGBOX keyword so far. */ + int has_fbbx : 1; + /* True if we have seen a METRICSSET keyword so far. */ + int has_metricsset : 1; + + /* Current glyph. */ + struct bdf_glyph *glyph; + /* True if we have seen an ENCODING keyword for the glyph. */ + int glyph_has_encoding : 1; + /* True if we have seen an BBX keyword for the glyph. */ + int glyph_has_bbx : 1; + /* Width of the glyph in bytes. */ + int glyph_bwidth; + /* Height of the glyph in pixel. */ + int glyph_bheight; + /* How many bitmap lines have been parsed already. */ + int glyph_blines; + } parser = { location: START, properties: 0, glyphs: 0, + has_size: 0, has_fbbx: 0 }; + + bdf = calloc (1, sizeof *bdf); + if (!bdf) + { + errno = ENOMEM; + return BDF_SYSTEM_ERROR; + } + + if (linecount) + *linecount = 0; + + while (!err && (len = next_line (&line, &line_size, filep, linecount)) >= 0) + { + switch (parser.location) + { + case START: + { + /* This is the start of the file, only comments are allowed + until STARTFONT is encountered. */ + char *arg = line; + err = find_arg (&arg); + + if (err) + continue; + if (!strcmp (line, "STARTFONT")) + { + char *minor = strchr (arg, '.'); + if (minor) + *(minor++) = '\0'; + parser.location = FONT; + parse_template (arg, "%i", &bdf->version_maj); + if (minor) + parse_template (minor, "%i", &bdf->version_min); + else + bdf->version_min = 0; + } + else + err = BDF_SYNTAX_ERROR; + } + break; + + case FONT: + { + /* This is the global header before the CHARS. */ + char *arg = line; + err = find_arg (&arg); + + if (err) + continue; + else if (!bdf->has_content_version + && !strcmp (line, "CONTENTVERSION")) + { + bdf->has_content_version = 1; + parse_template (arg, "%i", &bdf->content_version); + } + else if (!bdf->name && !strcmp (line, "FONT")) + err = parse_string (arg, &bdf->name, 0); + else if (!parser.has_size + && !strcmp (line, "SIZE")) + { + parser.has_size = 1; + parse_template (arg, "%i%i%i", &bdf->point_size, + &bdf->res_x, &bdf->res_y); + } + else if (!parser.has_fbbx && !strcmp (line, "FONTBOUNDINGBOX")) + { + parser.has_fbbx = 1; + parse_template (arg, "%i%i%i%i", &bdf->bbox.width, + &bdf->bbox.height, &bdf->bbox.offx, + &bdf->bbox.offy); + } + else if (!parser.has_metricsset && !strcmp (line, "METRICSSET")) + { + parser.has_metricsset = 1; + parse_template (arg, "%i", &bdf->metricsset); + if (!err && (bdf->metricsset < 0 + || bdf->metricsset > 2)) + err = BDF_INVALID_ARGUMENT; + } + else if (!bdf->properties && !strcmp (line, "STARTPROPERTIES")) + { + parser.location = PROPERTIES; + parse_template (arg, "%i", &bdf->properties_count); + if (!err && (bdf->properties_count <= 0)) + err = BDF_INVALID_ARGUMENT; + if (err) + goto leave; + bdf->__properties_allocated = bdf->properties_count; + bdf->properties = calloc (bdf->properties_count, + sizeof (struct bdf_property)); + if (!bdf->properties) + { + errno = ENOMEM; + err = BDF_SYSTEM_ERROR; + } + } + else if (!strcmp (line, "CHARS")) + { + /* This marks the end of the first section, so check + for mandatory global options. */ + if (!bdf->name || !parser.has_size || !parser.has_fbbx) + err = BDF_SYNTAX_ERROR; + else + { + parser.location = GLYPHS; + parse_template (arg, "%i", &bdf->glyphs_count); + if (!err && (bdf->glyphs_count < 0)) + err = BDF_INVALID_ARGUMENT; + if (!err) + { + bdf->__glyphs_allocated = bdf->glyphs_count; + bdf->glyphs = calloc (bdf->glyphs_count, + sizeof (struct bdf_glyph)); + if (!bdf->glyphs) + { + errno = ENOMEM; + err = BDF_SYSTEM_ERROR; + } + } + } + } + else if (!bdf->has_swidth && !strcmp (line, "SWIDTH")) + { + bdf->has_swidth = 1; + parse_template (arg, "%i%i", &bdf->swidth.x, &bdf->swidth.y); + } + else if (!bdf->has_dwidth && !strcmp (line, "DWIDTH")) + { + bdf->has_dwidth = 1; + parse_template (arg, "%i%i", &bdf->dwidth.x, &bdf->dwidth.y); + } + else if (!bdf->has_swidth1 && !strcmp (line, "SWIDTH1")) + { + bdf->has_swidth1 = 1; + parse_template (arg, "%i%i", &bdf->swidth1.x, &bdf->swidth1.y); + } + else if (!bdf->has_dwidth1 && !strcmp (line, "DWIDTH1")) + { + bdf->has_dwidth1 = 1; + parse_template (arg, "%i%i", &bdf->dwidth1.x, &bdf->dwidth1.y); + } + else if (!bdf->has_vvector && !strcmp (line, "VVECTOR")) + { + bdf->has_vvector = 1; + parse_template (arg, "%i%i", &bdf->vvector.x, &bdf->vvector.y); + } + else + err = BDF_SYNTAX_ERROR; + } + break; + + case PROPERTIES: + /* This is the property list in the global header, between + STARTPROPERTIES and ENDPROPERTIES. */ + if (!strcmp (line, "ENDPROPERTIES")) + { + parser.location = FONT; + if (parser.properties != bdf->properties_count) + err = BDF_COUNT_MISMATCH; + } + else + { + if (parser.properties == bdf->properties_count) + err = BDF_COUNT_MISMATCH; + else + { + struct bdf_property *prop + = &bdf->properties[parser.properties++]; + char *arg = line; + + err = find_arg (&arg); + if (err) + continue; + + err = parse_string (line, &prop->name, 0); + if (!err) + { + if (*arg == '"') + { + prop->type = BDF_PROPERTY_STRING; + err = parse_string (arg, &prop->value.string, 1); + } + else + { + prop->type = BDF_PROPERTY_NUMBER; + parse_template (arg, "%i", &prop->value.number); + } + } + } + } + break; + + case GLYPHS: + /* This is the second section of the file, containing the + glyphs. */ + if (!strcmp (line, "ENDFONT")) + { + if (parser.glyphs != bdf->glyphs_count) + err = BDF_COUNT_MISMATCH; + done = 1; + } + else + { + char *arg = line; + + err = find_arg (&arg); + if (err) + continue; + else if (!strcmp (line, "STARTCHAR")) + { + if (parser.glyphs == bdf->glyphs_count) + err = BDF_COUNT_MISMATCH; + + parser.location = GLYPH; + parser.glyph = &bdf->glyphs[parser.glyphs++]; + parser.glyph_has_encoding = 0; + parser.glyph_has_bbx = 0; + parser.glyph_blines = 0; + err = parse_string (arg, &(parser.glyph->name), 0); + } + else + err = BDF_SYNTAX_ERROR; + } + break; + + case GLYPH: + /* This is a glyph, but not its bitmap yet. */ + if (!strcmp (line, "BITMAP")) + { + if (!parser.glyph_has_encoding + || !parser.glyph_has_bbx + + /* In writing mode 0, SWIDTH and DWIDTH are mandatory. */ + || (bdf->metricsset != 1 + && (!(parser.glyph->has_swidth || bdf->has_swidth) + || !(parser.glyph->has_dwidth || bdf->has_dwidth))) + + /* In writing mode 1, SWIDTH1, DWIDTH1 and VVECTOR + are mandatory. */ + || (bdf->metricsset != 0 + && (!(parser.glyph->has_swidth1 || bdf->has_swidth1) + || !(parser.glyph->has_dwidth1 || bdf->has_dwidth1) + || !(parser.glyph->has_vvector + || bdf->has_vvector)))) + err = BDF_SYNTAX_ERROR; + + parser.location = BITMAP; + parser.glyph->bitmap = malloc (parser.glyph_bwidth + * parser.glyph_bheight); + if (!parser.glyph->bitmap) + { + errno = ENOMEM; + err = BDF_SYSTEM_ERROR; + } + } + else + { + char *arg = line; + + err = find_arg (&arg); + if (err) + continue; + else if (!parser.glyph_has_encoding + && !strcmp (line, "ENCODING")) + { + parser.glyph_has_encoding = 1; + parse_template (arg, "%i", &parser.glyph->encoding); + if (err == BDF_SYNTAX_ERROR) + { + err = 0; + parse_template (arg, "%i%i", &parser.glyph->encoding, + &parser.glyph->internal_encoding); + if (!err && parser.glyph->encoding != -1) + err = BDF_SYNTAX_ERROR; + } + } + else if (!parser.glyph_has_bbx && !strcmp (line, "BBX")) + { + parser.glyph_has_bbx = 1; + parse_template (arg, "%i%i%i%i", &parser.glyph->bbox.width, + &parser.glyph->bbox.height, + &parser.glyph->bbox.offx, + &parser.glyph->bbox.offy); + if (!err) + { + parser.glyph_bwidth = (parser.glyph->bbox.width + 7) / 8; + parser.glyph_bheight = parser.glyph->bbox.height; + } + } + else if (!parser.glyph->has_swidth && !strcmp (line, "SWIDTH")) + { + parser.glyph->has_swidth = 1; + parse_template (arg, "%i%i", &parser.glyph->swidth.x, + &parser.glyph->swidth.y); + } + else if (!parser.glyph->has_dwidth && !strcmp (line, "DWIDTH")) + { + parser.glyph->has_dwidth = 1; + parse_template (arg, "%i%i", &parser.glyph->dwidth.x, + &parser.glyph->dwidth.y); + } + else if (!parser.glyph->has_swidth1 && !strcmp (line, "SWIDTH1")) + { + parser.glyph->has_swidth1 = 1; + parse_template (arg, "%i%i", &parser.glyph->swidth1.x, + &parser.glyph->swidth1.y); + } + else if (!parser.glyph->has_dwidth1 && !strcmp (line, "DWIDTH1")) + { + parser.glyph->has_dwidth1 = 1; + parse_template (arg, "%i%i", &parser.glyph->dwidth1.x, + &parser.glyph->dwidth1.y); + } + else if (!parser.glyph->has_vvector && !strcmp (line, "VVECTOR")) + { + parser.glyph->has_vvector = 1; + parse_template (arg, "%i%i", &parser.glyph->vvector.x, + &parser.glyph->vvector.y); + } + else + err = BDF_SYNTAX_ERROR; + } + break; + + case BITMAP: + /* This is the bitmap of a glyph. */ + if (!strcmp (line, "ENDCHAR")) + { + if (parser.glyph_blines != parser.glyph_bheight) + err = BDF_COUNT_MISMATCH; + parser.location = GLYPHS; + parser.glyph = 0; + } + else + { + if (strlen (line) != 2 * parser.glyph_bwidth) + err = BDF_SYNTAX_ERROR; + else if (parser.glyph_blines == parser.glyph_bheight) + err = BDF_COUNT_MISMATCH; + else + { + char *number = line; + unsigned char *bline = parser.glyph->bitmap + + parser.glyph_bwidth * parser.glyph_blines++; + + do + { + err = parse_hexbyte (number, bline); + number += 2; + bline++; + } + while (!err && *number); + } + } + break; + } + } + while (!err && !done && !feof (filep) && !ferror (filep)); + + leave: + if (ferror (filep)) + err = ferror (filep); + if (err) + free (bdf); + else + *font = bdf; + return err; +} + + +/* Destroy the BDF font object and release all associated + resources. */ +void +bdf_destroy (bdf_font_t font) +{ + int i; + for (i = 0; i < font->glyphs_count; i++) + free (font->glyphs[i].name); + free (font->glyphs); + for (i = 0; i < font->properties_count; i++) + { + free (font->properties[i].name); + if (font->properties[i].type == BDF_PROPERTY_STRING) + free (font->properties[i].value.string); + } + free (font->properties); + free (font->name); +} + + +bdf_error_t +bdf_new (bdf_font_t *font, int version_maj, int version_min, + const char *name, int point_size, int res_x, int res_y, + int bbox_width, int bbox_height, int bbox_offx, int bbox_offy, + int metricsset) +{ + bdf_font_t bdf; + + if (!font + || (version_maj != 2) + || (version_min != 1 && version_min != 2) + || !name) + return BDF_INVALID_ARGUMENT; + + bdf = calloc (1, sizeof *bdf); + if (!bdf) + { + errno = ENOMEM; + return BDF_SYSTEM_ERROR; + } + + bdf->version_maj = version_maj; + bdf->version_min = version_min; + bdf->name = strdup (name); + if (!name) + { + free (bdf); + errno = ENOMEM; + return BDF_SYSTEM_ERROR; + } + + bdf->point_size = point_size; + bdf->res_x = res_x; + bdf->res_y = res_y; + bdf->bbox.width = bbox_width; + bdf->bbox.height = bbox_height; + bdf->bbox.offx = bbox_offx; + bdf->bbox.offy = bbox_offy; + bdf->metricsset = metricsset; + *font = bdf; + return 0; +} + + +#define bdf_set_something(what) \ +bdf_error_t \ +bdf_set_##what (bdf_font_t font, int glyph, int x, int y) \ +{ \ + if (x < 0 || y < 0 || glyph > font->glyphs_count - 1) \ + return BDF_INVALID_ARGUMENT; \ + if (glyph < 0) \ + { \ + font->has_##what = 1; \ + font->what.x = x; \ + font->what.y = y; \ + } \ + else \ + { \ + font->glyphs[glyph].has_##what = 1; \ + font->glyphs[glyph].what.x = x; \ + font->glyphs[glyph].what.y = y; \ + } \ + return 0; \ +} + +bdf_set_something (swidth) +bdf_set_something (dwidth) +bdf_set_something (swidth1) +bdf_set_something (dwidth1) +bdf_set_something (vvector) + + +static bdf_error_t +expand_properties (bdf_font_t font, int count) +{ + if (font->__properties_allocated == font->properties_count) + { + struct bdf_property *new; + new = realloc (font->properties, + (font->__properties_allocated + count) + * sizeof (struct bdf_property)); + if (!new) + { + errno = ENOMEM; + return BDF_SYSTEM_ERROR; + } + font->__properties_allocated += count; + font->properties = new; + } + return 0; +} + + +/* Add a new string property to the font FONT. */ +bdf_error_t +bdf_add_string_property (bdf_font_t font, const char *name, const char *value) +{ + bdf_error_t err; + struct bdf_property *prop; + + err = expand_properties (font, 16); + if (err) + { + err = expand_properties (font, 1); + if (err) + return err; + } + + prop = &font->properties[font->properties_count]; + prop->type = BDF_PROPERTY_STRING; + prop->name = strdup (name); + if (prop->name) + prop->value.string = strdup (value); + if (!prop->name || !prop->value.string) + { + errno = ENOMEM; + return BDF_SYSTEM_ERROR; + } + font->properties_count++; + return 0; +} + + +/* Add a new number property to the font FONT. */ +bdf_error_t +bdf_add_number_property (bdf_font_t font, const char *name, int value) +{ + bdf_error_t err; + struct bdf_property *prop; + + err = expand_properties (font, 16); + if (err) + { + err = expand_properties (font, 1); + if (err) + return err; + } + + prop = &font->properties[font->properties_count]; + prop->type = BDF_PROPERTY_NUMBER; + prop->name = strdup (name); + if (!prop->name) + { + errno = ENOMEM; + return BDF_SYSTEM_ERROR; + } + prop->value.number = value; + font->properties_count++; + return 0; +} + + +static bdf_error_t +expand_glyphs (bdf_font_t font, int count) +{ + if (font->__glyphs_allocated == font->glyphs_count) + { + struct bdf_glyph *new; + new = realloc (font->glyphs, + (font->__glyphs_allocated + count) + * sizeof (struct bdf_glyph)); + if (!new) + { + errno = ENOMEM; + return BDF_SYSTEM_ERROR; + } + font->__glyphs_allocated += count; + font->glyphs = new; + } + return 0; +} + + +/* Add a new glyph with the specified paramters to the font FONT. If + encoding is -1, internal_encoding specifies the internal + encoding. All other parameters are mandatory. */ +bdf_error_t +bdf_add_glyph (bdf_font_t font, const char *name, int encoding, + int internal_encoding, int bbox_width, int bbox_height, + int bbox_offx, int bbox_offy, const unsigned char *bitmap) +{ + bdf_error_t err; + struct bdf_glyph *glyph; + int bsize; + + err = expand_glyphs (font, 64); + if (err) + { + err = expand_glyphs (font, 1); + if (err) + return err; + } + + glyph = &font->glyphs[font->glyphs_count]; + memset (glyph, 0, sizeof (*glyph)); + + glyph->name = strdup (name); + if (!glyph->name) + { + errno = ENOMEM; + return BDF_SYSTEM_ERROR; + } + glyph->encoding = encoding; + if (encoding == -1) + glyph->internal_encoding = internal_encoding; + glyph->bbox.width = bbox_width; + glyph->bbox.height = bbox_height; + glyph->bbox.offx = bbox_offx; + glyph->bbox.offy = bbox_offy; + bsize = ((bbox_width + 7) / 8) * bbox_height; + glyph->bitmap = malloc (bsize); + if (!glyph->bitmap) + { + free (glyph->name); + errno = ENOMEM; + return BDF_SYSTEM_ERROR; + } + memcpy (glyph->bitmap, bitmap, bsize); + font->glyphs_count++; + return 0; +} + + +/* Write the font FONT in BDF format to stream FILEP. */ +bdf_error_t +bdf_write (FILE *filep, bdf_font_t font) +{ + int index; + + if (font->version_maj != 2 + || (font->version_min != 1 && font->version_min != 2)) + return BDF_INVALID_ARGUMENT; + fprintf (filep, "STARTFONT %i.%i\n", font->version_maj, font->version_min); + + if (!font->name) + return BDF_INVALID_ARGUMENT; + fprintf (filep, "FONT %s\n", font->name); + fprintf (filep, "SIZE %i %i %i\n", font->point_size, + font->res_x, font->res_y); + fprintf (filep, "FONTBOUNDINGBOX %i %i %i %i\n", + font->bbox.width, font->bbox.height, + font->bbox.offx, font->bbox.offy); + if (font->has_swidth) + fprintf (filep, "SWIDTH %i %i\n", font->swidth.x, font->swidth.y); + if (font->has_dwidth) + fprintf (filep, "DWIDTH %i %i\n", font->dwidth.x, font->dwidth.y); + if (font->has_swidth1) + fprintf (filep, "SWIDTH1 %i %i\n", font->swidth1.x, font->swidth1.y); + if (font->has_dwidth1) + fprintf (filep, "DWIDTH1 %i %i\n", font->dwidth1.x, font->dwidth1.y); + if (font->has_vvector) + fprintf (filep, "VVECTOR %i %i\n", font->vvector.x, font->vvector.y); + if (font->properties_count < 0) + return BDF_INVALID_ARGUMENT; + /* XXX We always print out a properties block for xmbdfed's sake. */ + fprintf (filep, "STARTPROPERTIES %i\n", font->properties_count); + if (font->properties_count > 0) + { + fprintf (filep, "STARTPROPERTIES %i\n", font->properties_count); + for (index = 0; index < font->properties_count; index++) + { + struct bdf_property *prop = &font->properties[index]; + + if (prop->type == BDF_PROPERTY_NUMBER) + fprintf (filep, "%s %i\n", prop->name, prop->value.number); + else + { + char *val = prop->value.string; + + fprintf (filep, "%s \"", prop->name); + while (*val) + { + fputc (*val, filep); + if (*(val++) == '"') + fputc ('"', filep); + } + fprintf (filep, "\"\n"); + } + } + } + fprintf (filep, "ENDPROPERTIES\n"); + if (font->glyphs_count <= 0) + return BDF_INVALID_ARGUMENT; + fprintf (filep, "CHARS %i\n", font->glyphs_count); + + for (index = 0; index < font->glyphs_count; index++) + { + struct bdf_glyph *glyph = &font->glyphs[index]; + unsigned char *bitmap; + int row, col; + + fprintf (filep, "STARTCHAR %s\n", glyph->name); + if (glyph->encoding != -1) + fprintf (filep, "ENCODING %i\n", glyph->encoding); + else + fprintf (filep, "ENCODING %i %i\n", glyph->encoding, + glyph->internal_encoding); + if (glyph->has_swidth) + fprintf (filep, "SWIDTH %i %i\n", glyph->swidth.x, glyph->swidth.y); + if (glyph->has_dwidth) + fprintf (filep, "DWIDTH %i %i\n", glyph->dwidth.x, glyph->dwidth.y); + if (glyph->has_swidth1) + fprintf (filep, "SWIDTH1 %i %i\n", glyph->swidth1.x, glyph->swidth1.y); + if (glyph->has_dwidth1) + fprintf (filep, "DWIDTH1 %i %i\n", glyph->dwidth1.x, glyph->dwidth1.y); + if (glyph->has_vvector) + fprintf (filep, "VVECTOR %i %i\n", glyph->vvector.x, glyph->vvector.y); + fprintf (filep, "BBX %i %i %i %i\n", glyph->bbox.width, + glyph->bbox.height, glyph->bbox.offx, glyph->bbox.offy); + fprintf (filep, "BITMAP\n"); + bitmap = glyph->bitmap; + for (row = 0; row < glyph->bbox.height; row++) + { + for (col = 0; col < (glyph->bbox.width + 7) / 8; col++) + fprintf (filep, "%02X", *(bitmap++)); + fputc ('\n', filep); + } + fprintf (filep, "ENDCHAR\n"); + } + fprintf (filep, "ENDFONT\n"); + if (ferror (filep)) + { + errno = ferror (filep); + return BDF_SYSTEM_ERROR; + } + return 0; +} + + +/* The function returns -1 if the encoding of glyph A is lower than + the encoding of glyph B, 1 if it is the other way round, and 0 if + the encoding of both glyphs is the same. */ +int +bdf_compare_glyphs (const void *a, const void *b) +{ + struct bdf_glyph *first = (struct bdf_glyph *) a; + struct bdf_glyph *second = (struct bdf_glyph *) b; + + if (first->encoding < second->encoding) + return -1; + else if (first->encoding > second->encoding) + return 1; + else + { + if (first->encoding == -1) + { + if (first->internal_encoding < second->internal_encoding) + return -1; + else if (first->internal_encoding > second->internal_encoding) + return 1; + else + return 0; + } + else + return 0; + } +} + + +/* Sort the glyphs in the font FONT. This must be called before using + bdf_find_glyphs, after the font has been created or after new + glyphs have been added to the font. */ +void +bdf_sort_glyphs (bdf_font_t font) +{ + qsort (font->glyphs, font->glyphs_count, sizeof (struct bdf_glyph), + bdf_compare_glyphs); +} + + +/* Find the glyph with the encoding ENC (and INTERNAL_ENC, if ENC is + -1) in the font FONT. Requires that the glyphs in the font are + sorted. */ +struct bdf_glyph * +bdf_find_glyph (bdf_font_t font, int enc, int internal_enc) +{ + struct bdf_glyph key = { encoding: enc, internal_encoding: internal_enc }; + return bsearch (&key, font->glyphs, font->glyphs_count, + sizeof (struct bdf_glyph), bdf_compare_glyphs); +} diff --git a/console-client/bdf.h b/console-client/bdf.h new file mode 100644 index 00000000..1f0bce41 --- /dev/null +++ b/console-client/bdf.h @@ -0,0 +1,272 @@ +/* bdf.h - Parser for the Adobe Glyph Bitmap Distribution Format (BDF). + Copyright (C) 2002 Free Software Foundation, Inc. + Written by Marcus Brinkmann . + + This file is part of the GNU Hurd. + + The GNU Hurd 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; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#ifndef _BDF_H_ +#define _BDF_H_ + +#include + +/* Version 2.2 of the Glyph Bitmap Distribution Format Specification + was released by Adobe 22 March 1993 and is a superset of version 2.1. + + The format of a BDF file is a human-readable ASCII text file. + Every line consists of a keyword, which may be followed by one or + more arguments. The specification is vague about the exact data + types of the arguments, so we treat a string as an 8-bit string + which must not contain a binary null, and a number like an integer + as an int. Leading and trailing white space are removed, multiple + spaces that seperate arguments are replaced by a single white + space, and empty lines are ignored. */ + + +/* Possible error values returned by the BDF functions. */ +typedef enum +{ + /* No error occured. This is guaranteed to be zero. */ + BDF_NO_ERROR = 0, + + /* A system error occured. The caller should consult errno. */ + BDF_SYSTEM_ERROR, + + /* All following errors indicate that the file is not a valid BDF + file. */ + BDF_SYNTAX_ERROR, + + /* An argument is out of range or of an invalid type. */ + BDF_INVALID_ARGUMENT, + + /* The number of properties, glyphs or bitmap lines is + incorrect. */ + BDF_COUNT_MISMATCH +} bdf_error_t; + +/* Return a statically allocated string describing the error value ERR. */ +const char *bdf_strerror (bdf_error_t err); + + +/* A property in the BDF font file is an unspecified KEYWORD VALUE + pair in a line after STARTPROPERTIES and before ENDPROPERTIES. + VALUE can be an integer or a string in double quotes (two + consecutives double quotes can be used to include a double quote in + the string. */ +struct bdf_property +{ + /* The property name. */ + char *name; + + /* The type indicates which member of the union is valid. */ + enum { BDF_PROPERTY_NUMBER, BDF_PROPERTY_STRING } type; + union + { + int number; + char *string; + } value; +}; +typedef struct bdf_property *bdf_property_t; + +/* The bounding box of a font or a glyph within. */ +struct bdf_bbox +{ + int width; + int height; + int offx; + int offy; +}; + +/* A vector, used for displacement and resolution. */ +struct bdf_vector +{ + int x; + int y; +}; + +/* A single glyph in the font. */ +struct bdf_glyph +{ + /* The name of the glyph. */ + char *name; + + /* The Adode Standard Encoding of the glyph, or -1 if not + available. */ + int encoding; + + /* If encoding is -1, internal_encoding contains the internal + encoding of the glyph. The internal encoding is private to the + font and the application. */ + int internal_encoding; + + /* The bounding box of the glyph. The width of the bounding box, + divided by 8 (rounding up), specifies how many bytes are used in + the bitmap for each row of the glyph. The height specifies the + number of rows. */ + struct bdf_bbox bbox; + + /* The bitmap of the glyph, row-by-row from top to bottom, and + byte-by-byte within a row from left to right. Within a byte, the + most significant bit is left in the glyph. */ + unsigned char *bitmap; + + /* If the writing direction is horizontal, SWIDTH and DWIDTH are + relevant. If the writing direction is vertical, SWIDTH1, DWIDTH1 + and VVECTOR are relevant. Relevant values have to be specified + here, or font-wide in the global section. A global value can be + overridden for individual glyphs. */ + int has_swidth : 1; + int has_dwidth : 1; + int has_swidth1 : 1; + int has_dwidth1 : 1; + int has_vvector : 1; + struct bdf_vector swidth; + struct bdf_vector dwidth; + struct bdf_vector swidth1; + struct bdf_vector dwidth1; + struct bdf_vector vvector; +}; + +/* A font is a set of glyphs with some font-wide attributes. */ +struct bdf_font +{ + /* The version of the font format. Should be 2.1 or 2.2. It is + split up into major and minor component to make precise + comparison possible. */ + int version_maj; + int version_min; + + /* The font name. */ + char *name; + + /* The content version, if available. The content version indicates + the revision of the appearance of the glyphs within the font. */ + int has_content_version : 1; + int content_version; + + /* The point size of the font in device pixels. */ + int point_size; + + /* The resolution of the display the font is intended for (dpi). + This is needed if you want to scale the glyphs for a different + device than they were intended for. */ + int res_x; + int res_y; + + /* The global bounding box parameters. */ + struct bdf_bbox bbox; + + int __properties_allocated; + int properties_count; + struct bdf_property *properties; + int __glyphs_allocated; + int glyphs_count; + struct bdf_glyph *glyphs; + + /* The metricsset. If 0, the font has a horizontal writing + direction. If 1, the font has a vertical writing direction. If + 2, the font has a mixed writing direction. */ + int metricsset; + + /* The following have the same meaning as the corresponding members + in the glyph structure and specify a font wide default which must + be used if the glyph does not provide its own values. */ + int has_swidth : 1; + int has_dwidth : 1; + int has_swidth1 : 1; + int has_dwidth1 : 1; + int has_vvector : 1; + struct bdf_vector swidth; + struct bdf_vector dwidth; + struct bdf_vector swidth1; + struct bdf_vector dwidth1; + struct bdf_vector vvector; +}; +typedef struct bdf_font *bdf_font_t; + + +/* Read the font from stream FILE, and return it in FONT. If + LINECOUNT is not zero, it will contain the number of lines in the + file at success, and the current line an error occured at + failure. */ +bdf_error_t bdf_read (FILE *file, bdf_font_t *font, int *linecount); + +/* Destroy the BDF font object and release all associated + resources. */ +void bdf_destroy (bdf_font_t font); + +/* Create a new font object with the specified mandatory + parameters. */ +bdf_error_t bdf_new (bdf_font_t *font, int version_maj, int version_min, + const char *name, int point_size, int res_x, int res_y, + int bbox_width, int bbox_height, int bbox_offx, + int bbox_offy, int metricsset); + +/* Set the SWIDTH of the glyph GLYPH in font FONT to X and Y. If + glyph is negativ, the default for the font will be set. */ +bdf_error_t bdf_set_swidth (bdf_font_t font, int glyph, int x, int y); + +/* Set the DWIDTH of the glyph GLYPH in font FONT to X and Y. If + glyph is negativ, the default for the font will be set. */ +bdf_error_t bdf_set_dwidth (bdf_font_t font, int glyph, int x, int y); + +/* Set the SWIDTH1 of the glyph GLYPH in font FONT to X and Y. If + glyph is negativ, the default for the font will be set. */ +bdf_error_t bdf_set_swidth1 (bdf_font_t font, int glyph, int x, int y); + +/* Set the DWIDTH1 of the glyph GLYPH in font FONT to X and Y. If + glyph is negativ, the default for the font will be set. */ +bdf_error_t bdf_set_dwidth1 (bdf_font_t font, int glyph, int x, int y); + +/* Set the VVECTOR of the glyph GLYPH in font FONT to X and Y. If + glyph is negativ, the default for the font will be set. */ +bdf_error_t bdf_set_vvector (bdf_font_t font, int glyph, int x, int y); + +/* Add a new string property to the font FONT. */ +bdf_error_t bdf_add_string_property (bdf_font_t font, const char *name, + const char *value); + +/* Add a new number property to the font FONT. */ +bdf_error_t bdf_add_number_property (bdf_font_t font, const char *name, + int value); + +/* Add a new glyph with the specified paramters to the font FONT. If + encoding is -1, internal_encoding specifies the internal + encoding. All other parameters are mandatory. */ +bdf_error_t bdf_add_glyph (bdf_font_t font, const char *name, int encoding, + int internal_encoding, int bbox_width, + int bbox_height, int bbox_offx, int bbox_offy, + const unsigned char *bitmap); + +/* Write the font FONT in BDF format to stream FILEP. */ +bdf_error_t bdf_write (FILE *filep, bdf_font_t font); + +/* The function returns -1 if the encoding of glyph A is lower than + the encoding of glyph B, 1 if it is the other way round, and 0 if + the encoding of both glyphs is the same. */ +int bdf_compare_glyphs (const void *a, const void *b); + +/* Sort the glyphs in the font FONT. This must be called before using + bdf_find_glyphs, after the font has been created or after new + glyphs have been added to the font. */ +void bdf_sort_glyphs (bdf_font_t font); + +/* Find the glyph with the encoding ENC (and INTERNAL_ENC, if ENC is + -1) in the font FONT. Requires that the glyphs in the font are + sorted. */ +struct bdf_glyph *bdf_find_glyph (bdf_font_t font, int enc, int internal_enc); + +#endif /* _BDF_H_ */ diff --git a/console-client/bell.h b/console-client/bell.h new file mode 100644 index 00000000..3ff40bde --- /dev/null +++ b/console-client/bell.h @@ -0,0 +1,56 @@ +/* bell.h - The interface to and for a bell driver. + Copyright (C) 2002 Free Software Foundation, Inc. + Written by Marcus Brinkmann. + + This file is part of the GNU Hurd. + + The GNU Hurd 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; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#ifndef _BELL_H_ +#define _BELL_H_ 1 + +#include + + +/* The bell drivers are set up by the driver's initialization routine + and added to the console client with driver_add_bell. All + subsequent operations on the display are fully synchronized by the + caller. The driver deinitialization routine should call + driver_remove_bell. */ + +/* Forward declaration. */ +struct bell_ops; +typedef struct bell_ops *bell_ops_t; + +/* Add the bell HANDLE with the operations OPS to the console client. + As soon as this is called, operations on this bell may be + performed, even before the function returns. */ +error_t driver_add_bell (bell_ops_t ops, void *handle); + +/* Remove the bell HANDLE with the operations OPS from the console + client. As soon as this function returns, no operations will be + performed on the bell anymore. */ +error_t driver_remove_bell (bell_ops_t ops, void *handle); + +struct bell_ops +{ + /* Beep! the bell HANDLE. */ + error_t (*beep) (void *handle); + + /* Do not use, do not remove. */ + void (*deprecated) (void *handle, int key); +}; + +#endif /* _BELL_H_ */ diff --git a/console-client/console.c b/console-client/console.c new file mode 100644 index 00000000..32c7fc1e --- /dev/null +++ b/console-client/console.c @@ -0,0 +1,468 @@ +/* console.c -- A pluggable console client. + Copyright (C) 2002 Free Software Foundation, Inc. + Written by Marcus Brinkmann. + + 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; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +#include "driver.h" +#include "timer.h" + +const char *cons_client_name = "console"; +const char *cons_client_version = HURD_VERSION; + + +/* The global lock protects the active_vcons variable, and thus all + operations on the virtual console that is currently active. */ +static struct mutex global_lock; + +/* The active virtual console. This is the one currently + displayed. */ +static vcons_t active_vcons = NULL; + + +/* Callbacks for input source drivers. */ + +/* Switch the active console to console ID or DELTA (relative to the + active console). */ +error_t +console_switch (int id, int delta) +{ + error_t err = 0; + vcons_t vcons; + vcons_t new_vcons; + + /* We must give up our global lock before we can call back into + libcons. This is because cons_switch will lock CONS, and as + other functions in libcons lock CONS while calling back into our + functions which take the global lock (like cons_vcons_add), we + would deadlock. So we acquire a reference for VCONS to make sure + it isn't deallocated while we are outside of the global lock. We + also know that */ + + mutex_lock (&global_lock); + vcons = active_vcons; + if (!vcons) + { + mutex_unlock (&global_lock); + return EINVAL; + } + ports_port_ref (vcons); + mutex_unlock (&global_lock); + + err = cons_switch (vcons, id, delta, &new_vcons); + if (!err) + { + mutex_lock (&global_lock); + if (active_vcons != new_vcons) + { + cons_vcons_close (active_vcons); + active_vcons = new_vcons; + } + mutex_unlock (&new_vcons->lock); + ports_port_deref (vcons); + mutex_unlock (&global_lock); + } + return err; +} + + +/* Enter SIZE bytes from the buffer BUF into the currently active + console. This can be called by the input driver at any time. */ +error_t +console_input (char *buf, size_t size) +{ + error_t err = 0; + vcons_t vcons; + + mutex_lock (&global_lock); + vcons = active_vcons; + if (!vcons) + { + mutex_unlock (&global_lock); + return EINVAL; + } + ports_port_ref (vcons); + mutex_unlock (&global_lock); + + if (vcons) + { + err = cons_vcons_input (vcons, buf, size); + ports_port_deref (vcons); + } + return err; +} + + +/* Scroll the active console by TYPE and VALUE as specified by + cons_vcons_scrollback. */ +int +console_scrollback (cons_scroll_t type, float value) +{ + int nr = 0; + vcons_t vcons; + + mutex_lock (&global_lock); + vcons = active_vcons; + if (!vcons) + { + mutex_unlock (&global_lock); + return EINVAL; + } + ports_port_ref (vcons); + mutex_unlock (&global_lock); + + if (vcons) + { + nr = cons_vcons_scrollback (vcons, type, value); + ports_port_deref (vcons); + } + return nr; +} + + +/* Exit the console client. Does not return. */ +void +console_exit (void) +{ + driver_fini (); + exit (0); +} + +/* Signal an error to the user. */ +void console_error (const wchar_t *const err_msg) +{ + mutex_lock (&global_lock); + bell_iterate + if (bell->ops->beep) + bell->ops->beep (bell->handle); + mutex_unlock (&global_lock); +} + +#if QUAERENDO_INVENIETIS +void +console_deprecated (int key) +{ + mutex_lock (&global_lock); + input_iterate + if (input->ops->deprecated) + (*input->ops->deprecated) (input->handle, key); + display_iterate + if (display->ops->deprecated) + (*display->ops->deprecated) (display->handle, key); + bell_iterate + if (bell->ops->deprecated) + (*bell->ops->deprecated) (bell->handle, key); + mutex_unlock (&global_lock); +} +#endif /* QUAERENDO_INVENIETIS */ + + +/* Callbacks for libcons. */ + +/* The user may define this function. It is called after a + virtual console entry was added. CONS is locked. */ +void +cons_vcons_add (cons_t cons, vcons_list_t vcons_entry) +{ + error_t err = 0; + mutex_lock (&global_lock); + if (!active_vcons) + { + vcons_t vcons; + + /* The first virtual console added to the list is automatically + opened after startup. */ + err = cons_vcons_open (cons, vcons_entry, &vcons); + if (!err) + { + vcons_entry->vcons = vcons; + active_vcons = vcons; + mutex_unlock (&vcons->lock); + } + } + mutex_unlock (&global_lock); +} + + +/* The user may define this function. Make the changes from + cons_vcons_write, cons_vcons_set_cursor_pos, + cons_vcons_set_cursor_status and cons_vcons_scroll active. VCONS + is locked and will have been continuously locked from the first + change since the last update on. This is the latest possible point + the user must make the changes visible from. The user can always + make the changes visible at a more convenient, earlier time. */ +void +cons_vcons_update (vcons_t vcons) +{ + mutex_lock (&global_lock); + if (vcons == active_vcons) + display_iterate + if (display->ops->update) + display->ops->update (display->handle); + mutex_unlock (&global_lock); +} + + +/* The user must define this function. Set the cursor on virtual + console VCONS, which is locked, to position COL and ROW. */ +void +cons_vcons_set_cursor_pos (vcons_t vcons, uint32_t col, uint32_t row) +{ + mutex_lock (&global_lock); + if (vcons == active_vcons) + display_iterate + if (display->ops->set_cursor_pos) + display->ops->set_cursor_pos (display->handle, col, row); + mutex_unlock (&global_lock); +} + + +/* The user must define this function. Set the cursor status of + virtual console VCONS, which is locked, to STATUS. */ +void +cons_vcons_set_cursor_status (vcons_t vcons, uint32_t status) +{ + mutex_lock (&global_lock); + if (vcons == active_vcons) + display_iterate + if (display->ops->set_cursor_status) + display->ops->set_cursor_status (display->handle, status); + mutex_unlock (&global_lock); +} + + +/* The user must define this function. Scroll the content of virtual + console VCONS, which is locked, up by DELTA if DELTA is positive or + down by -DELTA if DELTA is negative. DELTA will never be zero, and + the absolute value if DELTA will be smaller than or equal to the + height of the screen matrix. + + See for more information about this function. */ +void +cons_vcons_scroll (vcons_t vcons, int delta) +{ + mutex_lock (&global_lock); + if (vcons == active_vcons) + display_iterate + if (display->ops->scroll) + display->ops->scroll (display->handle, delta); + mutex_unlock (&global_lock); +} + + +/* The user must define this function. Deallocate the scarce + resources (like font glyph slots, colors etc) in the LENGTH entries + of the screen matrix starting from position COL and ROW. This call + is immediately followed by a subsequent cons_vcons_write call with + the same LENGTH, COL and ROW arguments, and should help to make the + write successful. If there are no scarce resources, the caller + might do nothing. */ +void cons_vcons_clear (vcons_t vcons, size_t length, + uint32_t col, uint32_t row) +{ + mutex_lock (&global_lock); + if (vcons == active_vcons) + display_iterate + if (display->ops->clear) + display->ops->clear (display->handle, length, col, row); + mutex_unlock (&global_lock); +} + + +/* The user must define this function. Write LENGTH characters + starting from STR on the virtual console VCONS, which is locked, + starting from position COL and ROW. */ +void +cons_vcons_write (vcons_t vcons, conchar_t *str, size_t length, + uint32_t col, uint32_t row) +{ + mutex_lock (&global_lock); + if (vcons == active_vcons) + display_iterate + if (display->ops->write) + display->ops->write (display->handle, str, length, col, row); + mutex_unlock (&global_lock); +} + + +/* The user must define this function. Make the virtual console + VCONS, which is locked, beep audibly. */ +void +cons_vcons_beep (vcons_t vcons) +{ + mutex_lock (&global_lock); + if (vcons == active_vcons) + bell_iterate + if (bell->ops->beep) + bell->ops->beep (bell->handle); + mutex_unlock (&global_lock); +} + + +/* The user must define this function. Make the virtual console + VCONS, which is locked, flash visibly. */ +void +cons_vcons_flash (vcons_t vcons) +{ + mutex_lock (&global_lock); + if (vcons == active_vcons) + display_iterate + if (display->ops->flash) + display->ops->flash (display->handle); + mutex_unlock (&global_lock); +} + + +/* The user must define this function. Notice the current status of + the scroll lock flag. */ +void +cons_vcons_set_scroll_lock (vcons_t vcons, int onoff) +{ + mutex_lock (&global_lock); + if (vcons == active_vcons) + input_iterate + if (input->ops->set_scroll_lock_status) + input->ops->set_scroll_lock_status (input->handle, onoff); + mutex_unlock (&global_lock); +} + + +/* Console-specific options. */ +static const struct argp_option +options[] = + { + {"driver-path", 'D', "PATH", 0, "Specify search path for driver modules" }, + {"driver", 'd', "NAME", 0, "Add driver NAME to the console" }, + {0} + }; + +/* Parse a command line option. */ +static error_t +parse_opt (int key, char *arg, struct argp_state *state) +{ + static int devcount = 0; + error_t err; + + switch (key) + { + case 'D': + { + char *s; + char *d; + + if (driver_path) + free (driver_path); + driver_path = malloc (strlen (arg) + 2); + if (!driver_path) + { + argp_failure (state, 1, ENOMEM, "adding driver path failed"); + return EINVAL; + } + s = arg; + d = driver_path; + while (*s) + { + *(d++) = (*s == ':') ? '\0' : *s; + s++; + } + *(d++) = '\0'; + *d = '\0'; + } + break; + + case 'd': + err = driver_add (arg /* XXX */, arg, + state->argc, state->argv, &state->next, 0); + if (err) + { + argp_failure (state, 1, err, "loading driver `%s' failed", arg); + return EINVAL; + } + devcount++; + break; + + case ARGP_KEY_SUCCESS: + if (!devcount) + { + argp_error (state, "at least one --driver argument required"); + return EINVAL; + } + break; + + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + +/* Add our startup arguments to the standard cons set. */ +static const struct argp_child startup_children[] = + { { &cons_startup_argp }, { 0 } }; +static struct argp startup_argp = {options, parse_opt, 0, + 0, startup_children}; + +int +main (int argc, char *argv[]) +{ + error_t err; + char *errname; + + driver_init (); + + /* Parse our command line. This shouldn't ever return an error. */ + argp_parse (&startup_argp, argc, argv, 0, 0, 0); + + err = driver_start (&errname); + if (err) + error (1, 0, "Starting driver %s failed", errname); + + mutex_init (&global_lock); + + err = cons_init (); + if (err) + { + driver_fini (); + error (1, err, "Console library initialization failed"); + } + + err = timer_init (); + if (err) + { + driver_fini (); + error (1, err, "Timer thread initialization failed"); + } + + cons_server_loop (); + + /* Never reached. */ + driver_fini (); + return 0; +} diff --git a/console-client/display.h b/console-client/display.h new file mode 100644 index 00000000..44e62773 --- /dev/null +++ b/console-client/display.h @@ -0,0 +1,138 @@ +/* display.h - The interface to and for a display driver. + Copyright (C) 2002 Free Software Foundation, Inc. + Written by Marcus Brinkmann. + + This file is part of the GNU Hurd. + + The GNU Hurd 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; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#ifndef _DISPLAY_H_ +#define _DISPLAY_H_ 1 + +#include +#include + +#include + + +/* The display drivers are set up by the driver's initialization + routine and added to the console client with driver_add_display. + All subsequent operations on the display are fully synchronized by + the caller. The driver deinitialization routine should call + driver_remove_display. */ + +/* Forward declaration. */ +struct display_ops; +typedef struct display_ops *display_ops_t; + +/* Add the display HANDLE with the operations OPS to the console + client. As soon as this is called, operations on this display may + be performed, even before the function returns. */ +error_t driver_add_display (display_ops_t ops, void *handle); + +/* Remove the display HANDLE with the operations OPS from the console + client. As soon as this function returns, no operations will be + performed on the display anymore. */ +error_t driver_remove_display (display_ops_t ops, void *handle); + + +struct display_ops +{ + /* Set the cursor's position on display HANDLE to column COL and row + ROW. The home position (COL and ROW both being 0) is the upper + left corner of the screen matrix. COL will be smaller than the + width and ROW will be smaller than the height of the display. + + The driver is allowed to delay the effect of this operation until + the UPDATE function is called. */ + error_t (*set_cursor_pos) (void *handle, uint32_t col, uint32_t row); + + + /* Set the cursor's state to STATE on display HANDLE. The possible + values for STATE are defined by the CONS_CURSOR_* symbols in + . + + The driver is allowed to delay the effect of this operation until + the UPDATE function is called. */ + error_t (*set_cursor_status) (void *handle, uint32_t state); + + + /* Scroll the display by DELTA lines up if DELTA is positive, and by + -DELTA lines down if DELTA is negative. DELTA will never be + zero, and the absolute value if DELTA will be smaller than or + equal to the height of the screen matrix. + + The area that becomes free will be filled in a subsequent write + call. The purpose of the function is two-fold: It is called with + an absolute value of DELTA smaller than the screen height to + perform scrolling. It is called with an absolute value of DELTA + equal to the screen height to prepare a full refresh of the + screen. In the latter case the driver should not really perform + any scrolling. Instead it might deallocate limited resources + (like display glyph slots and palette colors) if that helps to + perform the subsequent write. It goes without saying that the + same deallocation, if any, should be performed on the area that + will be filled with the scrolled in content. + + XXX Possibly need a function to invalidate scrollback buffer, or + in general to signal a switch of the console so state can be + reset. Only do this if we make guarantees about validity of + scrollback buffer, of course. + + The driver is allowed to delay the effect of this operation until + the UPDATE function is called. */ + error_t (*scroll) (void *handle, int delta); + + /* Deallocate the scarce resources (like font glyph slots, colors + etc) in the LENGTH entries of the screen matrix starting from + position COL and ROW. This call is immediately followed by calls + to write which cover the same area. If there are no scarce + resources, the caller might do nothing. */ + error_t (*clear) (void *handle, size_t length, uint32_t col, uint32_t row); + + /* Write the text STR with LENGTH characters to column COL and row + ROW on display HANDLE. LENGTH can be longer than the width of + the screen matrix minus COL. In this case the driver should + automatically wrap around the edge. However, LENGTH will never + be so huge that the whole text starting from COL and ROW will go + beyond the lower right corner of the screen matrix. + + The driver is allowed to delay the effect of this operation until + the UPDATE function is called. */ + error_t (*write) (void *handle, conchar_t *str, size_t length, + uint32_t col, uint32_t row); + + /* Flush all the past changes on display HANDLE that have not been + flushed yet. This should make all past changes visible to the + user. The driver is free to make them visible earlier, but it + must happen before returning from this call. + + The purpose of this function is to group several related change + operations together, but also several change operations which + occur in rapid succession. The first grouping is essential to + make it possible for a driver to implement double buffering. The + second grouping is important to keep up performance if there is a + lot of activity on the screen matrix. */ + error_t (*update) (void *handle); + + /* Flash the display HANDLE once. This should be done + immediately. */ + error_t (*flash) (void *handle); + + /* Do not use, do not remove. */ + void (*deprecated) (void *handle, int key); +}; + +#endif /* _DISPLAY_H_ */ diff --git a/console-client/driver.c b/console-client/driver.c new file mode 100644 index 00000000..74df1907 --- /dev/null +++ b/console-client/driver.c @@ -0,0 +1,353 @@ +/* driver.c - The console client driver code. + Copyright (C) 2002 Free Software Foundation, Inc. + Written by Marcus Brinkmann. + + This file is part of the GNU Hurd. + + The GNU Hurd 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; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#include +#include +#include +#include + +#include + +#include + +#include "driver.h" + + +/* The number of entries by which we grow a driver or component list + if we need more space. */ +#define LIST_GROW 4 + +/* The path where we search for drivers, in addition to the default + path. The directories are separated by '\0' and terminated with an + empty string. XXX Should use argz or something. XXX Should get a + protective lock. */ +char *driver_path; + +/* The driver list lock, the list itself, its current length and the + total number of entries in the list. */ +struct mutex driver_list_lock; +driver_t driver_list; +size_t driver_list_len; +size_t driver_list_alloc; + + +/* Initialize the driver framework. */ +error_t +driver_init (void) +{ + mutex_init (&driver_list_lock); + mutex_init (&display_list_lock); + mutex_init (&input_list_lock); + mutex_init (&bell_list_lock); + return 0; +} + + +/* Deinitialize and unload all loaded drivers and deinitialize the + driver framework. */ +error_t +driver_fini (void) +{ + int i; + + mutex_lock (&driver_list_lock); + for (i = 0; i < driver_list_len; i++) + { + driver_list[i].ops->fini (driver_list[i].handle, 1); + dlclose (driver_list[i].module); + free (driver_list[i].name); + free (driver_list[i].driver); + } + driver_list_len = 0; + mutex_unlock (&driver_list_lock); + return 0; +} + + +/* Load, intialize and (if START is non-zero) start the driver DRIVER + under the given NAME (which must be unique among all loaded + drivers) with arguments ARGZ with length ARGZ_LEN. This funtion + will grab the driver list lock. The driver itself might try to + grab the display, input source and bell list locks as well. */ +error_t driver_add (const char *const name, const char *const driver, + int argc, char *argv[], int *next, int start) +{ + error_t err; + static char cons_defpath[] = CONSOLE_DEFPATH; + driver_ops_t ops; + char *filename = NULL; + char *modname; + void *shobj = NULL; + driver_t drv; + int i; + char *dir = driver_path; + int defpath = 0; + + mutex_lock (&driver_list_lock); + for (i = 0; i < driver_list_len; i++) + if (driver_list[i].name && !strcmp (driver_list[i].name, name)) + { + mutex_unlock (&driver_list_lock); + return EEXIST; + } + + if (!dir || !*dir) + { + dir = cons_defpath; + defpath = 1; + } + + while (dir) + { + if (filename) + free (filename); + if (asprintf (&filename, + "%s/%s%s", dir, driver, CONSOLE_SONAME_SUFFIX) < 0) + { + mutex_unlock (&driver_list_lock); + return ENOMEM; + } + + errno = 0; + shobj = dlopen (filename, RTLD_LAZY); + if (!shobj) + { + const char *errstring = dlerror (); /* Must always call or it leaks! */ + if (errno != ENOENT) + { + free (filename); + mutex_unlock (&driver_list_lock); + return errno ?: EGRATUITOUS; + } + } + else + break; + + dir += strlen (dir) + 1; + if (!*dir) + { + if (defpath) + break; + else + { + dir = cons_defpath; + defpath = 1; + } + } + } + + if (!shobj) + { + if (filename) + free (filename); + mutex_unlock (&driver_list_lock); + return ENOENT; + } + + if (asprintf (&modname, "driver_%s_ops", driver) < 0) + { + dlclose (shobj); + free (filename); + mutex_unlock (&driver_list_lock); + return ENOMEM; + } + + ops = dlsym (shobj, modname); + free (modname); + if (!ops || !ops->init) + { + dlclose (shobj); + free (filename); + mutex_unlock (&driver_list_lock); + return EGRATUITOUS; + } + + if (driver_list_len == driver_list_alloc) + { + size_t new_alloc = driver_list_alloc + LIST_GROW; + driver_t new_list = realloc (driver_list, + new_alloc * sizeof (*driver_list)); + if (!new_list) + { + dlclose (shobj); + free (filename); + mutex_unlock (&driver_list_lock); + return errno; + } + driver_list = new_list; + driver_list_alloc = new_alloc; + } + drv = &driver_list[driver_list_len]; + + drv->name = strdup (name); + drv->driver = strdup (driver); + drv->filename = filename; + drv->ops = ops; + drv->module = shobj; + if (!drv->name || !drv->driver) + { + if (drv->name) + free (drv->name); + if (drv->driver) + free (drv->driver); + dlclose (shobj); + free (filename); + mutex_unlock (&driver_list_lock); + return ENOMEM; + } + + /* If we will start the driver, the init function must not exit. */ + err = (*drv->ops->init) (&drv->handle, start, argc, argv, next); + if (!err && start && drv->ops->start) + err = (*drv->ops->start) (drv->handle); + if (err) + { + free (drv->name); + free (drv->driver); + dlclose (shobj); + free (filename); + mutex_unlock (&driver_list_lock); + return err; + } + + driver_list_len++; + mutex_unlock (&driver_list_lock); + return 0; +} + + +/* Start all drivers. Only used once at start up, after all the + option parsing and driver initialization. + + Returns 0 on success, and the name of a driver if it initializing + that driver fails. */ +error_t +driver_start (char **name) +{ + error_t err = 0; + int i; + + mutex_lock (&driver_list_lock); + for (i = 0; i < driver_list_len; i++) + { + if (driver_list[i].ops->start) + err = (*driver_list[i].ops->start) (driver_list[i].handle); + if (err) + { + *name = driver_list[i].name; + while (--i >= 0) + (*driver_list[i].ops->fini) (driver_list[i].handle, 1); + break; + } + } + mutex_unlock (&driver_list_lock); + return err; +} + + +/* Deinitialize and unload the driver with the name NAME. This + function will grab the driver list lock. The driver might try to + grab the display, input source and bell list locks as well. */ +error_t driver_remove (const char *const name) +{ + error_t err; + int i; + + mutex_lock (&driver_list_lock); + for (i = 0; i < driver_list_len; i++) + if (driver_list[i].name && !strcmp (driver_list[i].name, name)) + { + err = driver_list[i].ops->fini (driver_list[i].handle, 0); + if (!err) + { + dlclose (driver_list[i].module); + free (driver_list[i].name); + free (driver_list[i].driver); + free (driver_list[i].filename); + while (i + 1 < driver_list_len) + { + driver_list[i] = driver_list[i + 1]; + i++; + } + driver_list_len--; + } + mutex_unlock (&driver_list_lock); + return err; + } + mutex_unlock (&driver_list_lock); + return ESRCH; +} + + +#define ADD_REMOVE_COMPONENT(component) \ +struct mutex component##_list_lock; \ +component##_t component##_list; \ +size_t component##_list_len; \ +size_t component##_list_alloc; \ + \ +error_t \ +driver_add_##component (component##_ops_t ops, void *handle) \ +{ \ + mutex_lock (&component##_list_lock); \ + if (component##_list_len == component##_list_alloc) \ + { \ + size_t new_alloc = component##_list_alloc + LIST_GROW; \ + component##_t new_list = realloc (component##_list, \ + new_alloc \ + * sizeof (*component##_list)); \ + if (!new_list) \ + { \ + mutex_unlock (&component##_list_lock); \ + return errno; \ + } \ + component##_list = new_list; \ + component##_list_alloc = new_alloc; \ + } \ + component##_list[component##_list_len].ops = ops; \ + component##_list[component##_list_len].handle = handle; \ + component##_list_len++; \ + mutex_unlock (&component##_list_lock); \ + return 0; \ +} \ + \ +error_t \ +driver_remove_##component (component##_ops_t ops, void *handle) \ +{ \ + int i; \ + \ + mutex_lock (&component##_list_lock); \ + for (i = 0; i < component##_list_len; i++) \ + if (component##_list[i].ops == ops \ + && component##_list[i].handle == handle) \ + { \ + while (i + 1 < component##_list_len) \ + { \ + component##_list[i] = component##_list[i + 1]; \ + i++; \ + } \ + component##_list_len--; \ + } \ + mutex_unlock (&component##_list_lock); \ + return 0; \ +} + +ADD_REMOVE_COMPONENT (display) +ADD_REMOVE_COMPONENT (input) +ADD_REMOVE_COMPONENT (bell) diff --git a/console-client/driver.h b/console-client/driver.h new file mode 100644 index 00000000..ac8f5c98 --- /dev/null +++ b/console-client/driver.h @@ -0,0 +1,282 @@ +/* driver.h - The interface to and for a console client driver. + Copyright (C) 2002 Free Software Foundation, Inc. + Written by Marcus Brinkmann. + + This file is part of the GNU Hurd. + + The GNU Hurd 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; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#ifndef _CONSOLE_DRIVER_H_ +#define _CONSOLE_DRIVER_H_ 1 + +#include +#include + +#include "display.h" +#include "input.h" +#include "bell.h" + + +/* The path where we search for drivers, in addition to the default + path. The directories are separated by '\0' and terminated with an + empty string. XXX Should use argz or something. XXX Should get a + protective lock. */ +extern char *driver_path; + + +/* The driver framework allows loading and unloading of new drivers. + It also provides some operations on the loaded drivers. The device + framework module does its own locking, so all operations can be run + at any time by any thread. */ + +/* Initialize the driver framework. */ +error_t driver_init (void); + +/* Deinitialize and unload all loaded drivers and deinitialize the + driver framework. */ +error_t driver_fini (void); + +/* Forward declaration. */ +struct driver_ops; +typedef struct driver_ops *driver_ops_t; + +/* Load, initialize and (if START is non-zero) start the driver DRIVER + under the given NAME (which must be unique among all loaded + drivers) with arguments ARGC, ARGV and NEXT (see + parse_startup_args). This function will grab the driver list lock. + The driver itself might try to grab the display, input source and + bell list locks as well. */ +error_t driver_add (const char *const name, const char *const driver, + int argc, char *argv[], int *next, int start); + +/* Start all drivers. Only used once at start up, after all the + option parsing and driver initialization. + + Returns 0 on success, and the error if it initializing that driver + fails (NAME points to the driver name then). */ +error_t driver_start (char **name); + +/* Deinitialize and unload the driver with the name NAME. This + function will grab the driver list lock. The driver might try to + grab the display, input source and bell list locks as well. */ +error_t driver_remove (const char *const name); + +/* Iterate over all loaded drivers. This macro will grab the driver + list lock. You use it with a block: + + driver_iterate + { + printf ("%s\n", driver->ops->name); + } + + Or even just: + + driver_iterate printf ("%s\n", driver->ops->name); + + The variable DRIVER is provided by the macro. */ +#define driver_iterate \ + for (driver_t driver = (mutex_lock (&driver_list_lock), \ + &driver_list[0]); \ + driver < &driver_list[driver_list_len] \ + || (mutex_unlock (&driver_list_lock), 0); \ + driver++) + + +struct driver_ops +{ + /* Initialize an instance of the driver and return a handle for it + in HANDLE. The options in ARGC, ARGV and NEXT should be + processed and validated. + + If NO_EXIT is zero, the function might exit on fatal errors or + invalid arguments. The drawback is that it must NOT allocate any + resources that need to be freed or deallocated explicitely before + exiting the program either, because other driver instances are + also allowed to exit without prior notice at some later time. + Allocation and initialization of such resources (like the video + card) must be delayed until the start() function is called (see + below). + + If NO_EXIT is non-zero, the function must not exit, but report + all errors back to the caller. In this case, it is guaranteed + that the START function is called immediately after this function + returns, and that the driver is properly unloaded with fini() at + some later time. + + The above behaviour, and the split into an init() and a start() + function, was carefully designed to allow the init() function the + optimal use of argp at startup and at run time to parse options. + + ARGV[*NEXT] is the next argument to be parsed. ARGC is the + number of total arguments in ARGV. The function should increment + *NEXT for each argument parsed. The function should not reorder + arguments, nor should it parse non-option arguments. It should + also not parse over any single "--" argument. + + Every driver must implement this function. + + If NO_EXIT is zero, the function should return zero on success + and otherwise either terminate or return an appropriate error + value. If it returns, either the program terminates because of + other errors, or the function start() is called. + + If NO_EXIT is non-zero, the function should return zero on + success and an appropriate error value otherwise. If it returns + success, the function start() will be called next, otherwise + nothing happens. */ + error_t (*init) (void **handle, int no_exit, + int argc, char *argv[], int *next); + + /* Activate the driver instance. This function should load all the + display, input and bell river components for this driver + instance. + + If successful, the function should return zero. In this case it + is guaranteed that fini() will be called before the program + terminates. If not successful, the function should free all + resources associated with HANDLE and return non-zero. */ + error_t (*start) (void *handle); + + /* Deinitialize the driver. This should remove all the individual + drivers installed by init() and release all resources. It should + also reset all hardware devices into the state they had before + calling init(), as far as applicable. HANDLE is provided as + returned by init(). + + The function is allowed to fail if FORCE is 0. If FORCE is not + 0, the driver should remove itself no matter what. */ + error_t (*fini) (void *handle, int force); +}; + + +/* The driver structure. */ +struct driver +{ + /* The unique name of the driver. */ + char *name; + + /* The plugin name. */ + char *driver; + + /* The filename that was identified as providing the plugin. */ + char *filename; + + driver_ops_t ops; + void *handle; + + /* The following members are private to the driver support code. Do + not use. */ + + /* The shared object handle as returned by dlopen(). */ + void *module; +}; +typedef struct driver *driver_t; + + +/* Forward declarations needed by the macro above. Don't use these + variables directly. */ +extern struct mutex driver_list_lock; +extern driver_t driver_list; +extern size_t driver_list_len; + + +/* Iterate over all loaded displays. This macro will grab the display + list lock. You use it with a block, just like driver_iterate. + + display_iterate display->ops->flash (display->handle); + + The variable DISPLAY is provided by the macro. */ +#define display_iterate \ + for (display_t display = (mutex_lock (&display_list_lock), \ + &display_list[0]); \ + display < &display_list[display_list_len] \ + || (mutex_unlock (&display_list_lock), 0); \ + display++) + + +/* The display structure. */ +struct display +{ + display_ops_t ops; + void *handle; +}; +typedef struct display *display_t; + + +/* Forward declarations needed by the macro above. Don't use these + variables directly. */ +extern struct mutex display_list_lock; +extern display_t display_list; +extern size_t display_list_len; + + +/* Iterate over all loaded inputs. This macro will grab the input + list lock. You use it with a block, just like driver_iterate. + + input_iterate input->ops->set_scroll_lock_status (input->handle, 0); + + The variable INPUT is provided by the macro. */ +#define input_iterate \ + for (input_t input = (mutex_lock (&input_list_lock), &input_list[0]); \ + input < &input_list[input_list_len] \ + || (mutex_unlock (&input_list_lock), 0); \ + input++) + + +/* The input structure. */ +struct input +{ + input_ops_t ops; + void *handle; +}; +typedef struct input *input_t; + + +/* Forward declarations needed by the macro above. Don't use these + variables directly. */ +extern struct mutex input_list_lock; +extern input_t input_list; +extern size_t input_list_len; + + +/* Iterate over all loaded bells. This macro will grab the bell list + lock. You use it with a block, just like driver_iterate. + + bell_iterate bell->ops->beep (bell->handle); + + The variable BELL is provided by the macro. */ +#define bell_iterate \ + for (bell_t bell = (mutex_lock (&bell_list_lock), &bell_list[0]); \ + bell < &bell_list[bell_list_len] \ + || (mutex_unlock (&bell_list_lock), 0); \ + bell++) + + +/* The bell structure, needed by the macro above. Don't use it + directly. */ +struct bell +{ + bell_ops_t ops; + void *handle; +}; +typedef struct bell *bell_t; + +/* Forward declarations needed by the macro above. Don't use these + variables directly. */ +extern struct mutex bell_list_lock; +extern bell_t bell_list; +extern size_t bell_list_len; + +#endif /* _CONSOLE_DRIVER_H_ */ diff --git a/console-client/generic-speaker.c b/console-client/generic-speaker.c new file mode 100644 index 00000000..9288cd24 --- /dev/null +++ b/console-client/generic-speaker.c @@ -0,0 +1,488 @@ +/* generic-speaker.c - The simple speaker bell driver. + Copyright (C) 2002 Free Software Foundation, Inc. + Written by Marcus Brinkmann. + + This file is part of the GNU Hurd. + + The GNU Hurd 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; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#include +#include + +#include + +#include "driver.h" +#include "timer.h" + +/* This driver should work on IA32, IA64, Alpha, PowerPC, MIPS and ARM + architectures. It requires a Programmable Interrupt Timer (PIT) at + I/O ports 0x40 - 0x43 and a speaker that can be connected to timer + 2 at I/O port 0x61. */ + +/* The beep timer. */ +static struct timer_list generic_speaker_timer; + +/* Forward declaration. */ +static struct bell_ops generic_speaker_ops; + + +/* The speaker port. */ +#define SPEAKER 0x61 + +/* If 0, follow state of SPEAKER_DATA bit, otherwise enable output + from timer 2. */ +#define SPEAKER_TMR2 0x01 + +/* If SPEAKER_TMR2 is not set, this provides direct input into the + speaker. Otherwise, this enables or disables the output from the + timer. */ +#define SPEAKER_DATA 0x02 + + +/* The PIT channel value ports. You can write to and read from them. + Do not mess with timer 0 or 1. */ +#define PIT_COUNTER_0 0x40 +#define PIT_COUNTER_1 0x41 +#define PIT_COUNTER_2 0x42 + +/* The frequency of the PIT clock. */ +#define PIT_FREQUENCY 0x1234dd + +/* The PIT control port. You can only write to it. Do not mess with + timer 0 or 1. */ +#define PIT_CTRL 0x43 +#define PIT_CTRL_SELECT_MASK 0xc0 +#define PIT_CTRL_SELECT_0 0x00 +#define PIT_CTRL_SELECT_1 0x40 +#define PIT_CTRL_SELECT_2 0x80 + +/* Read and load control. */ +#define PIT_CTRL_READLOAD_MASK 0x30 +#define PIT_CTRL_COUNTER_LATCH 0x00 /* Hold timer value until read. */ +#define PIT_CTRL_READLOAD_LSB 0x10 /* Read/load the LSB. */ +#define PIT_CTRL_READLOAD_MSB 0x20 /* Read/load the MSB. */ +#define PIT_CTRL_READLOAD_WORD 0x30 /* Read/load the LSB then the MSB. */ + +/* Mode control. */ +#define PIT_CTRL_MODE_MASK 0x0e + +/* Interrupt on terminal count. Setting the mode sets output to low. + When counter is set and terminated, output is set to high. */ +#define PIT_CTRL_INTR_ON_TERM 0x00 + +/* Programmable one-shot. When loading counter, output is set to + high. When counter terminated, output is set to low. Can be + triggered again from that point on by setting the gate pin to + high. */ +#define PIT_CTRL_PROGR_ONE_SHOT 0x02 + +/* Rate generator. Output is low for one period of the counter, and + high for the other. */ +#define PIT_CTRL_RATE_GEN 0x04 + +/* Square wave generator. Output is low for one half of the period, + and high for the other half. */ +#define PIT_CTRL_SQUAREWAVE_GEN 0x06 + +/* Software triggered strobe. Setting the mode sets output to high. + When counter is set and terminated, output is set to low. */ +#define PIT_CTRL_SOFTSTROBE 0x08 + +/* Hardware triggered strobe. Like software triggered strobe, but + only starts the counter when the gate pin is set to high. */ +#define PIT_CTRL_HARDSTROBE 0x0a + + +/* Count mode. */ +#define PIT_CTRL_COUNT_MASK 0x01 +#define PIT_CTRL_COUNT_BINARY 0x00 /* 16-bit binary counter. */ +#define PIT_CTRL_COUNT_BCD 0x01 /* 4-decade BCD counter. */ + + +/* Let's be loud! */ + +/* The 12th root of 2. Sort of. */ +#define T_TEMP_SCALE 1.0594630943592952645 +#define T_DOWN_ONE_HALF(x) ((short) (x / T_TEMP_SCALE)) +#define T_UP_ONE_HALF(x) ((short) (x * T_TEMP_SCALE)) +#define T_DOWN_ONE_OCTAVE(x) ((short) (x / 2)) +#define T_UP_ONE_OCTAVE(x) ((short) (x * 2)) +#define T_REST ((short) 0) +#define T_FINE ((short) (-1)) + +#define T_b_3 T_UP_ONE_OCTAVE (T_b_2) +#define T_b_F_3 T_UP_ONE_OCTAVE (T_b_F_2) +#define T_a_S_3 T_b_F_3 +#define T_a_3 T_UP_ONE_OCTAVE (T_a_2) +#define T_a_F_3 T_UP_ONE_OCTAVE (T_a_F_2) +#define T_g_S_3 T_a_F_3 +#define T_g_3 T_UP_ONE_OCTAVE (T_g_2) +#define T_g_F_3 T_f_S_3 +#define T_f_S_3 T_UP_ONE_OCTAVE (T_f_S_2) +#define T_f_3 T_UP_ONE_OCTAVE (T_f_2) +#define T_e_3 T_UP_ONE_OCTAVE (T_e_2) +#define T_e_F_3 T_UP_ONE_OCTAVE (T_e_F_2) +#define T_d_S_3 T_e_F_3 +#define T_d_3 T_UP_ONE_OCTAVE (T_d_2) +#define T_d_F_3 T_c_S_3 +#define T_c_S_3 T_UP_ONE_OCTAVE (T_c_S_2) +#define T_c_3 T_UP_ONE_OCTAVE (T_c_2) + +#define T_b_2 T_UP_ONE_OCTAVE (T_b_1) +#define T_b_F_2 T_UP_ONE_OCTAVE (T_b_F_1) +#define T_a_S_2 T_b_F_2 +#define T_a_2 T_UP_ONE_OCTAVE (T_a_1) +#define T_a_F_2 T_UP_ONE_OCTAVE (T_a_F_1) +#define T_g_S_2 T_a_F_2 +#define T_g_2 T_UP_ONE_OCTAVE (T_g_1) +#define T_g_F_2 T_f_S_2 +#define T_f_S_2 T_UP_ONE_OCTAVE (T_f_S_1) +#define T_f_2 T_UP_ONE_OCTAVE (T_f_1) +#define T_e_2 T_UP_ONE_OCTAVE (T_e_1) +#define T_e_F_2 T_UP_ONE_OCTAVE (T_e_F_1) +#define T_d_S_2 T_e_F_2 +#define T_d_2 T_UP_ONE_OCTAVE (T_d_1) +#define T_d_F_2 T_c_S_2 +#define T_c_S_2 T_UP_ONE_OCTAVE (T_c_S_1) +#define T_c_2 T_UP_ONE_OCTAVE (T_c_1) + +#define T_b_1 T_UP_ONE_HALF (T_b_F_1) +#define T_b_F_1 T_UP_ONE_HALF (T_a_1) +#define T_a_S_1 T_b_F_1 +#define T_a_1 ((short) (440)) +#define T_a_F_1 T_DOWN_ONE_HALF (T_a_1) +#define T_g_S_1 T_a_F_1 +#define T_g_1 T_DOWN_ONE_HALF (T_a_F_1) +#define T_g_F_1 T_f_S_1 +#define T_f_S_1 T_DOWN_ONE_HALF (T_g_1) +#define T_f_1 T_DOWN_ONE_HALF (T_f_S_1) +#define T_e_1 T_DOWN_ONE_HALF (T_f_1) +#define T_e_F_1 T_DOWN_ONE_HALF (T_e_1) +#define T_d_S_1 T_e_F_1 +#define T_d_1 T_DOWN_ONE_HALF (T_e_F_1) +#define T_d_F_1 T_c_S_1 +#define T_c_S_1 T_DOWN_ONE_HALF (T_d_1) +#define T_c_1 T_DOWN_ONE_HALF (T_c_S_1) + +#define T_b T_DOWN_ONE_OCTAVE (T_b_1) +#define T_b_F T_DOWN_ONE_OCTAVE (T_b_F_1) +#define T_a_S T_b_F +#define T_a T_DOWN_ONE_OCTAVE (T_a_1) +#define T_a_F T_DOWN_ONE_OCTAVE (T_a_F_1) +#define T_g_S T_a_F +#define T_g T_DOWN_ONE_OCTAVE (T_g_1) +#define T_g_F T_f_S +#define T_f_S T_DOWN_ONE_OCTAVE (T_f_S_1) +#define T_f T_DOWN_ONE_OCTAVE (T_f_1) +#define T_e T_DOWN_ONE_OCTAVE (T_e_1) +#define T_e_F T_DOWN_ONE_OCTAVE (T_e_F_1) +#define T_d_S T_e_F +#define T_d T_DOWN_ONE_OCTAVE (T_d_1) +#define T_d_F T_c_S +#define T_c_S T_DOWN_ONE_OCTAVE (T_c_S_1) +#define T_c T_DOWN_ONE_OCTAVE (T_c_1) + +struct note +{ + short pitch; + short duration; +}; + +struct melody +{ + int measure; + struct note *next; + struct note note[]; +}; + +static struct melody beep1 = + { 160, NULL, { + /* The classical bell. */ + { T_a_1, 4 }, { T_FINE, 0 } + } }; + +static struct melody beep2 = + { 60, NULL, { + /* The Linux bell. */ + { 750, 1 }, { T_FINE, 0 } + } }; + +static struct melody beep3 = + { 160, NULL, { + /* The tritonus. Quite alarming. */ + { T_f_2, 2 }, { T_b_1, 4 }, { T_FINE, 0 } + } }; + +static struct melody beep4 = + { 160, NULL, { + /* C-Major chord. A bit playful. */ + { T_c_2, 2 }, { T_e_2, 2 }, { T_g_2, 2 }, + { T_FINE, 0 } + } }; + +static struct melody *beep[] = { &beep1, &beep2, &beep3, &beep4 }; +static int active_beep; + +#if QUAERENDO_INVENIETIS +struct melody tune1 = + { 160, NULL, { + /* The Free Software Song. Measure: 7/4. */ + { T_d_2, 16 }, { T_c_2, 8 }, { T_b_1, 16 }, { T_a_1, 16 }, + { T_b_1, 16 }, { T_c_2, 8 }, { T_b_1, 8 }, { T_a_1, 8 }, { T_g_1, 16 }, + { T_g_1, 24 }, { T_a_1, 24 }, { T_b_1, 8 }, + { T_c_2, 24 }, { T_b_1, 14 }, { T_REST, 2 }, { T_b_1, 8 }, { T_d_2, 8 }, + { T_a_1, 22 }, { T_REST, 2 }, { T_a_1, 32 }, + { T_c_2, 16 }, { T_d_2, 8 }, { T_c_2, 16 }, { T_b_1, 24 }, + { T_d_2, 16 }, { T_c_2, 8 }, { T_b_1, 16 }, { T_a_1, 16 }, + { T_b_1, 16 }, { T_c_2, 8 }, { T_b_1, 8 }, { T_a_1, 8 }, { T_g_1, 16 }, + { T_g_1, 24 }, { T_a_1, 24 }, { T_b_1, 8 }, + { T_c_2, 24 }, { T_b_1, 14 }, { T_REST, 2 }, { T_b_1, 8 }, { T_d_2, 8 }, + { T_a_1, 22 }, { T_REST, 2 }, { T_a_1, 30 }, { T_REST, 2 }, + { T_a_1, 56 }, { T_FINE, 0 } + } }; + +struct melody tune2 = + { 160, NULL, { + /* I feel pretty. Measure: 3/4. By Leonard Bernstein. */ + { T_c_1, 8 }, { T_e_1, 8 }, + { T_f_1, 4 }, { T_a_1, 20 }, + { T_REST, 8 }, { T_c_1, 8 }, { T_e_1, 8 }, + { T_f_1, 4 }, { T_a_1, 20 }, + { T_REST, 8 }, { T_c_1, 8 }, { T_e_1, 8 }, + { T_f_1, 4 }, { T_a_1, 12 }, { T_e_1, 8 }, + { T_f_1, 4 }, { T_a_1, 12 }, { T_f_1, 8 }, + { T_c_2, 32 }, + { T_b_F_1,8 }, { T_a_1, 8 }, + { T_g_1, 4 }, { T_f_1, 20 }, + { T_REST, 8 }, { T_g_1, 8 }, { T_a_1, 8 }, + { T_b_F_1,12}, { T_a_1, 4 }, { T_g_1, 4 }, { T_f_1, 4 }, + { T_e_1, 16 }, { T_d_1, 3 }, { T_e_1, 2 }, { T_d_1, 3 }, + { T_c_1, 56 }, { T_FINE, 0 } + } }; + +struct melody tune3 = + { 120, NULL, { + /* Summertime. Measure: 4/4. By George & Ira Gershwin. */ + { T_b_1, 8 }, { T_g_1, 8 }, + { T_b_1, 36 }, { T_REST, 4 }, { T_a_1, 6 }, { T_g_1, 2 }, + { T_a_1, 6 }, { T_b_1, 2 }, { T_g_1, 8 }, + { T_e_1, 16 }, { T_b, 24 }, { T_REST, 8 }, + { T_b_1, 8 }, { T_g_1, 8 }, + { T_a_1, 3 }, { T_REST, 1 }, { T_a_1, 28 }, + { T_REST, 8 }, { T_g_1, 6 }, { T_e_1, 2 }, + { T_g_1, 6 }, { T_e_1, 2 }, { T_g_1, 8 }, + { T_f_S_1,48}, { T_REST, 4 }, { T_b_1, 8 }, { T_g_1, 4 }, + { T_b_1, 3 }, { T_REST, 1 }, { T_b_1, 7 }, { T_REST, 1 }, { T_b_1, 20 }, + { T_REST, 8 }, { T_a_1, 6 }, { T_g_1, 2 }, + { T_a_1, 6 }, { T_b_1, 2 }, { T_g_1, 8 }, + { T_e_1, 16 }, { T_b, 32 }, { T_REST, 8 }, + { T_b, 8 }, { T_d_1, 8 }, { T_b, 4 }, + { T_d_1, 4 }, { T_e_1, 8 }, { T_g_1, 8 }, + /* Keen attempt at a glissando. */ + { T_b_1, 2 }, { T_b_1 + (T_b_1 - T_a_1) / 3, 1 }, + { T_b_1 + 2 * (T_b_1 - T_a_1) / 3, 1 }, + { T_a_1, 12 }, { T_g_1, 15 }, { T_REST, 1 }, + { T_g_1, 4 }, { T_e_1, 68 }, + { T_FINE, 0 } + } }; + +struct melody tune4 = + { 250, NULL, { + /* Indiana Jones Theme. Measure: 4/4. By John Williams. */ + { T_e_1, 4 }, { T_REST, 8 }, { T_f_1, 4 }, + { T_g_1, 4 }, { T_REST, 4 }, { T_c_2, 24 }, + { T_REST, 16 }, { T_d_1, 4 }, { T_REST, 8 }, { T_e_1, 4 }, + { T_f_1, 32 }, + { T_REST, 16 }, { T_g_1, 4 }, { T_REST, 8 }, { T_a_1, 4 }, + { T_b_1, 4 }, { T_REST, 4 }, { T_f_2, 24 }, + { T_REST, 16 }, { T_a_1, 4 }, { T_REST, 8 }, { T_b_1, 4 }, + { T_c_2, 8 }, { T_REST, 8 }, { T_d_2, 12 }, { T_REST, 4 }, + { T_e_2, 8 }, { T_REST, 8 }, + { T_e_1, 4 }, { T_REST, 8 }, { T_f_1, 4 }, + { T_g_1, 4 }, { T_REST, 4 }, { T_c_2, 24 }, + { T_REST, 16 }, { T_d_2, 4 }, { T_REST, 8 }, { T_e_2, 4 }, + { T_f_2, 32 }, + { T_REST, 16 }, { T_g_1, 4 }, { T_REST, 8 }, { T_g_1, 4 }, + { T_e_2, 16 }, { T_d_2, 4 }, { T_REST, 8 }, { T_g_1, 4 }, + { T_e_2, 16 }, { T_d_2, 4 }, { T_REST, 8 }, { T_g_1, 4 }, + { T_f_2, 16 }, { T_e_2, 4 }, { T_REST, 8 }, { T_d_2, 4 }, + { T_c_2, 32 }, { T_FINE, 0 } + } }; + +struct melody *tune[] = { &tune1, &tune2, &tune3, &tune4 }; +#endif /* QUAERENDO_INVENIETIS */ + + +static void +beep_off (void) +{ + unsigned char status; + + status = inb (SPEAKER) & ~(SPEAKER_TMR2 | SPEAKER_DATA); + outb (status, SPEAKER); +} + +static void +beep_on (short pitch) +{ + unsigned char status; + unsigned int counter; + + if (pitch < 20) + pitch = 20; + else if (pitch > 32767) + pitch = 32767; + + counter = PIT_FREQUENCY / pitch; + + /* Program timer 2. */ + outb (PIT_CTRL_SELECT_2 | PIT_CTRL_READLOAD_WORD + | PIT_CTRL_SQUAREWAVE_GEN | PIT_CTRL_COUNT_BINARY, PIT_CTRL); + outb (counter & 0xff, PIT_COUNTER_2); /* LSB */ + outb ((counter >> 8) & 0xff, PIT_COUNTER_2); /* MSB */ + + /* Start speaker. */ + status = inb (SPEAKER) | SPEAKER_TMR2 | SPEAKER_DATA; + outb (status, SPEAKER); +} + + +static int +next_note (void *handle) +{ + struct melody *melody = handle; + + switch (melody->next->pitch) + { + case T_FINE: + beep_off (); + return 0; + + case T_REST: + beep_off (); + break; + + default: + beep_on (melody->next->pitch); + break; + } + + generic_speaker_timer.expires + += (60 * HZ * melody->next->duration / (8 * melody->measure)); + melody->next++; + return 1; +} + + +/* Initialization of the generic speaker driver. */ +static error_t +generic_speaker_init (void **handle, int no_exit, + int argc, char *argv[], int *next) +{ + timer_clear (&generic_speaker_timer); + generic_speaker_timer.fnc = &next_note; + + return 0; +} + + +/* Start the driver. */ +static error_t +generic_speaker_start (void *handle) +{ + error_t err; + +#if OSKIT_MACH + if (ioperm (SPEAKER, 1, 1) < 0) + return errno; + if (ioperm (PIT_COUNTER_2, PIT_CTRL - PIT_COUNTER_2 + 1, 1) < 0) + return errno; +#endif + + beep_off (); + + err = driver_add_bell (&generic_speaker_ops, NULL); + /* XXX We can not disable the I/O ports on error as there might be + concurrent users of it. */ + return err; +} + + +/* Deinitialization of the generic speaker driver. */ +static error_t +generic_speaker_fini (void *handle, int force) +{ + driver_remove_bell (&generic_speaker_ops, NULL); + + /* XXX We can not disable the I/O ports as there might be concurrent + users of it. */ + if (timer_remove (&generic_speaker_timer)) + beep_off (); + return 0; +} + + +/* Beep! */ +static error_t +generic_speaker_beep (void *handle) +{ + if (timer_remove (&generic_speaker_timer)) + beep_off (); + generic_speaker_timer.fnc_data = beep[active_beep]; + beep[0]->next = &beep[active_beep]->note[1]; + beep_on (beep[active_beep]->note[0].pitch); + generic_speaker_timer.expires = fetch_jiffies () + + (60 * HZ * beep[active_beep]->note[0].duration + / (8 * beep[active_beep]->measure)); + timer_add (&generic_speaker_timer); + return 0; +} + +#if QUAERENDO_INVENIETIS +static void +generic_speaker_play_melody (void *handle, int key) +{ + if (key < 0 || key >= sizeof (tune) / sizeof (tune[0])) + return; + + if (timer_remove (&generic_speaker_timer)) + beep_off (); + generic_speaker_timer.fnc_data = tune[key]; + tune[key]->next = &tune[key]->note[1]; + beep_on (tune[key]->note[0].pitch); + generic_speaker_timer.expires = fetch_jiffies () + + (60 * HZ * tune[key]->note[0].duration / (8 * tune[key]->measure)); + timer_add (&generic_speaker_timer); + return; +} +#endif /* QUAERENDO_INVENIETIS */ + + +struct driver_ops driver_generic_speaker_ops = + { + generic_speaker_init, + generic_speaker_start, + generic_speaker_fini + }; + +static struct bell_ops generic_speaker_ops = + { + generic_speaker_beep, +#if QUAERENDO_INVENIETIS + generic_speaker_play_melody +#else + NULL +#endif + }; diff --git a/console-client/input.h b/console-client/input.h new file mode 100644 index 00000000..38c2a64f --- /dev/null +++ b/console-client/input.h @@ -0,0 +1,83 @@ +/* input.h - The interface to and for an input driver. + Copyright (C) 2002 Free Software Foundation, Inc. + Written by Marcus Brinkmann. + + This file is part of the GNU Hurd. + + The GNU Hurd 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; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#ifndef _INPUT_H_ +#define _INPUT_H_ 1 + +#include +#include + +#include + + +/* The input drivers are set up by the driver's initialization routine + and added to the console client with driver_add_input. All + subsequent operations on the display are fully synchronized by the + caller. The driver deinitialization routine should call + driver_remove_input. */ + +/* Forward declaration. */ +struct input_ops; +typedef struct input_ops *input_ops_t; + +/* Add the input source HANDLE with the operations OPS to the console + client. As soon as this is called, operations on this input source + may be performed, even before the function returns. */ +error_t driver_add_input (input_ops_t ops, void *handle); + +/* Remove the input HANDLE with the operations OPS from the console + client. As soon as this function returns, no operations will be + performed on the input source anymore. */ +error_t driver_remove_input (input_ops_t ops, void *handle); + +/* Enter SIZE bytes from the buffer BUF into the currently active + console. This can be called by the input driver at any time. */ +error_t console_input (char *buf, size_t size); + +/* Scroll the active console by TYPE and VALUE as specified by + cons_vcons_scrollback. */ +int console_scrollback (cons_scroll_t type, float value); + +/* Switch the active console to console ID or DELTA (relative to the + active console). */ +error_t console_switch (int id, int delta); + +/* Signal an error to the user. */ +void console_error (const wchar_t *const err_msg); + +/* Exit the console client. Does not return. */ +void console_exit (void); + +#if QUAERENDO_INVENIETIS +/* Do not use, do not remove. */ +void console_deprecated (int key); +#endif + + +struct input_ops +{ + /* Set the status of the scroll lock indication. */ + error_t (*set_scroll_lock_status) (void *handle, int onoff); + + /* Do not use, do not remove. */ + void (*deprecated) (void *handle, int key); +}; + +#endif /* _INPUT_H_ */ diff --git a/console-client/ncursesw.c b/console-client/ncursesw.c new file mode 100644 index 00000000..b47d6513 --- /dev/null +++ b/console-client/ncursesw.c @@ -0,0 +1,611 @@ +/* ncursesw.c - The ncursesw console driver. + Copyright (C) 2002 Free Software Foundation, Inc. + Written by Marcus Brinkmann. + + 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; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include +#include +#include +#include +#include + +/* The makefiles make sure that this program is compiled with + -I${prefix}/ncursesw. */ +#include + +#include +#include + +#include "driver.h" + + +/* ncurses is not thread-safe. This lock protects all calls into the + ncurses library. */ +struct mutex ncurses_lock; + +/* Forward declaration. */ +static struct display_ops ncursesw_display_ops; +static struct input_ops ncursesw_input_ops; +static struct bell_ops ncursesw_bell_ops; + +struct curses_kc_to_cons_kc +{ + int curses; + char *cons; +}; + +static struct curses_kc_to_cons_kc keycodes[] = + { + { KEY_BREAK, NULL }, /* XXX */ + { KEY_DOWN, CONS_KEY_DOWN }, + { KEY_UP, CONS_KEY_UP }, + { KEY_RIGHT, CONS_KEY_RIGHT }, + { KEY_LEFT, CONS_KEY_LEFT }, + { KEY_HOME, CONS_KEY_HOME }, + { KEY_BACKSPACE, CONS_KEY_BACKSPACE }, + { KEY_F(1), CONS_KEY_F1 }, + { KEY_F(2), CONS_KEY_F2 }, + { KEY_F(3), CONS_KEY_F3 }, + { KEY_F(4), CONS_KEY_F4 }, + { KEY_F(5), CONS_KEY_F5 }, + { KEY_F(6), CONS_KEY_F6 }, + { KEY_F(7), CONS_KEY_F7 }, + { KEY_F(8), CONS_KEY_F8 }, + { KEY_F(9), CONS_KEY_F9 }, + { KEY_F(10), CONS_KEY_F10 }, + { KEY_DL, NULL }, /* XXX Delete line. */ + { KEY_IL, NULL }, /* XXX Insert line. */ + { KEY_DC, CONS_KEY_DC }, + { KEY_IC, CONS_KEY_IC }, + { KEY_EIC, NULL }, /* XXX Exit insert mode. */ + { KEY_CLEAR, NULL }, /* XXX Clear screen. */ + { KEY_EOS, NULL }, /* XXX Clear to end of screen. */ + { KEY_EOL, NULL }, /* XXX Clear to end of line. */ + { KEY_SF, NULL }, /* XXX Scroll one line forward. */ + { KEY_SR, NULL }, /* XXX Scroll one line backward. */ + { KEY_NPAGE, CONS_KEY_NPAGE }, + { KEY_PPAGE, CONS_KEY_PPAGE }, + { KEY_STAB, NULL }, /* XXX Set tab. */ + { KEY_CTAB, NULL }, /* XXX Clear tab. */ + { KEY_CATAB, NULL }, /* XXX Clear all tabs. */ + { KEY_ENTER, NULL }, /* XXX Enter or send. */ + { KEY_SRESET, NULL }, /* XXX Soft (partial) reset. */ + { KEY_RESET, NULL }, /* XXX Reset or hard reset. */ + { KEY_PRINT, NULL }, /* XXX Print or copy. */ + { KEY_LL, NULL }, /* XXX Home down or bottom (lower left). */ + { KEY_A1, NULL }, /* XXX Upper left of keypad. */ + { KEY_A3, NULL }, /* XXX Upper right of keypad. */ + { KEY_B2, NULL }, /* XXX Center of keypad. */ + { KEY_C1, NULL }, /* XXX Lower left of keypad. */ + { KEY_C3, NULL }, /* XXX Lower right of keypad. */ + { KEY_BTAB, CONS_KEY_BTAB }, + { KEY_BEG, NULL }, /* XXX Beg(inning) key. */ + { KEY_CANCEL, NULL }, /* XXX Cancel key. */ + { KEY_CLOSE, NULL }, /* XXX Close key. */ + { KEY_COMMAND, NULL }, /* XXX Cmd (command) key. */ + { KEY_COPY, NULL }, /* XXX Copy key. */ + { KEY_CREATE, NULL }, /* XXX Create key. */ + { KEY_END, CONS_KEY_END }, + { KEY_EXIT, NULL }, /* XXX Exit key. */ + { KEY_FIND, NULL }, /* XXX Find key. */ + { KEY_HELP, NULL }, /* XXX Help key. */ + { KEY_MARK, NULL }, /* XXX Mark key. */ + { KEY_MESSAGE, NULL }, /* XXX Message key. */ + { KEY_MOUSE, NULL }, /* XXX Mouse event read. */ + { KEY_MOVE, NULL }, /* XXX Move key. */ + { KEY_NEXT, NULL }, /* XXX Next object key. */ + { KEY_OPEN, NULL }, /* XXX Open key. */ + { KEY_OPTIONS, NULL }, /* XXX Options key. */ + { KEY_PREVIOUS, NULL }, /* XXX Previous object key. */ + { KEY_REDO, NULL }, /* XXX Redo key. */ + { KEY_REFERENCE, NULL }, /* XXX Ref(erence) key. */ + { KEY_REFRESH, NULL }, /* XXX Refresh key. */ + { KEY_REPLACE, NULL }, /* XXX Replace key. */ + { KEY_RESIZE, NULL }, /* XXX Screen resized. */ + { KEY_RESTART, NULL }, /* XXX Restart key. */ + { KEY_RESUME, NULL }, /* XXX Resume key. */ + { KEY_SAVE, NULL }, /* XXX Save key. */ + { KEY_SBEG, NULL }, /* XXX Shifted beginning key. */ + { KEY_SCANCEL, NULL }, /* XXX Shifted cancel key. */ + { KEY_SCOMMAND, NULL }, /* XXX Shifted command key. */ + { KEY_SCOPY, NULL }, /* XXX Shifted copy key. */ + { KEY_SCREATE, NULL }, /* XXX Shifted create key. */ + { KEY_SDC, NULL }, /* XXX Shifted delete char key. */ + { KEY_SDL, NULL }, /* XXX Shifted delete line key. */ + { KEY_SELECT, NULL }, /* XXX Select key. */ + { KEY_SEND, NULL }, /* XXX Shifted end key. */ + { KEY_SEOL, NULL }, /* XXX Shifted clear line key. */ + { KEY_SEXIT, NULL }, /* XXX Shifted exit key. */ + { KEY_SFIND, NULL }, /* XXX Shifted find key. */ + { KEY_SHELP, NULL }, /* XXX Shifted help key. */ + { KEY_SHOME, NULL }, /* XXX Shifted home key. */ + { KEY_SIC, NULL }, /* XXX Shifted input key. */ + { KEY_SLEFT, NULL }, /* XXX Shifted left arrow key. */ + { KEY_SMESSAGE, NULL }, /* XXX Shifted message key. */ + { KEY_SMOVE, NULL }, /* XXX Shifted move key. */ + { KEY_SNEXT, NULL }, /* XXX Shifted next key. */ + { KEY_SOPTIONS, NULL }, /* XXX Shifted options key. */ + { KEY_SPREVIOUS, NULL }, /* XXX Shifted prev key. */ + { KEY_SPRINT, NULL }, /* XXX Shifted print key. */ + { KEY_SREDO, NULL }, /* XXX Shifted redo key. */ + { KEY_SREPLACE, NULL }, /* XXX Shifted replace key. */ + { KEY_SRIGHT, NULL }, /* XXX Shifted right arrow. */ + { KEY_SRSUME, NULL }, /* XXX Shifted resume key. */ + { KEY_SSAVE, NULL }, /* XXX Shifted save key. */ + { KEY_SSUSPEND, NULL }, /* XXX Shifted suspend key. */ + { KEY_SUNDO, NULL }, /* XXX Shifted undo key. */ + { KEY_SUSPEND, NULL }, /* XXX Suspend key. */ + { KEY_UNDO, NULL } /* XXX Undo key. */ + }; + +static int +ucs4_to_altchar (wchar_t chr, chtype *achr) +{ + switch (chr) + { + case CONS_CHAR_RARROW: + *achr = ACS_RARROW; + break; + case CONS_CHAR_LARROW: + *achr = ACS_LARROW; + break; + case CONS_CHAR_UARROW: + *achr = ACS_UARROW; + break; + case CONS_CHAR_DARROW: + *achr = ACS_DARROW; + break; + case CONS_CHAR_BLOCK: + *achr = ACS_BLOCK; + break; + case CONS_CHAR_LANTERN: + *achr = ACS_LANTERN; + break; + case CONS_CHAR_DIAMOND: + *achr = ACS_DIAMOND; + break; + case CONS_CHAR_CKBOARD: + *achr = ACS_CKBOARD; + break; + case CONS_CHAR_DEGREE: + *achr = ACS_DEGREE; + break; + case CONS_CHAR_PLMINUS: + *achr = ACS_PLMINUS; + break; + case CONS_CHAR_BOARD: + *achr = ACS_BOARD; + break; + case CONS_CHAR_LRCORNER: + *achr = ACS_LRCORNER; + break; + case CONS_CHAR_URCORNER: + *achr = ACS_URCORNER; + break; + case CONS_CHAR_ULCORNER: + *achr = ACS_ULCORNER; + break; + case CONS_CHAR_LLCORNER: + *achr = ACS_LLCORNER; + break; + case CONS_CHAR_PLUS: + *achr = ACS_PLUS; + break; + case CONS_CHAR_S1: + *achr = ACS_S1; + break; + case CONS_CHAR_S3: + *achr = ACS_S3; + break; + case CONS_CHAR_HLINE: + *achr = ACS_HLINE; + break; + case CONS_CHAR_S7: + *achr = ACS_S7; + break; + case CONS_CHAR_S9: + *achr = ACS_S9; + break; + case CONS_CHAR_LTEE: + *achr = ACS_LTEE; + break; + case CONS_CHAR_RTEE: + *achr = ACS_RTEE; + break; + case CONS_CHAR_BTEE: + *achr = ACS_BTEE; + break; + case CONS_CHAR_TTEE: + *achr = ACS_TTEE; + break; + case CONS_CHAR_VLINE: + *achr = ACS_VLINE; + break; + case CONS_CHAR_LEQUAL: + *achr = ACS_LEQUAL; + break; + case CONS_CHAR_GEQUAL: + *achr = ACS_GEQUAL; + break; + case CONS_CHAR_PI: + *achr = ACS_PI; + break; + case CONS_CHAR_NEQUAL: + *achr = ACS_NEQUAL; + break; + case CONS_CHAR_STERLING: + *achr = ACS_STERLING; + break; + case CONS_CHAR_BULLET: + *achr = ACS_BULLET; + break; + default: + return 0; + } + return 1; +} + +static any_t +input_loop (any_t unused) +{ + int fd = 0; + fd_set rfds; + int w_escaped = 0; + + FD_ZERO (&rfds); + FD_SET (fd, &rfds); + + while (1) + { + int ret; + + FD_SET (fd, &rfds); + + ret = select (fd + 1, &rfds, 0, 0, 0); + if (ret == 1) + { + char buffer[100]; + char *buf = buffer; + size_t size = 0; + + mutex_lock (&ncurses_lock); + while ((ret = getch ()) != ERR) + { + int i; + int found; + + if (w_escaped) + { + switch (ret) + { + case 'x': + endwin (); + exit (0); + break; + case 23: /* ^W */ + assert (size < 100); + buf[size++] = ret; + break; + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + /* Avoid a dead lock. */ + mutex_unlock (&ncurses_lock); + console_switch (1 + (ret - '1'), 0); + mutex_lock (&ncurses_lock); + break; + default: + break; + } + w_escaped = 0; + } + else + switch (ret) + { + case 23: /* ^W */ + w_escaped = 1; + break; + default: + found = 0; + for (i =0; i < sizeof(keycodes) / sizeof(keycodes[0]); i++) + { + if (keycodes[i].curses == ret) + { + if (keycodes[i].cons) + { + assert (size < 101 - strlen(keycodes[i].cons)); + strcpy (&buf[size], keycodes[i].cons); + size += strlen (keycodes[i].cons); + } + found = 1; + break; + } + } + if (!found) + { + assert (size < 100); + buf[size++] = ret; + } + break; + } + } + mutex_unlock (&ncurses_lock); + if (size) + console_input (buf, size); + } + } +} + +static inline attr_t +conchar_attr_to_attr (conchar_attr_t attr) +{ + return ((attr.intensity == CONS_ATTR_INTENSITY_BOLD + ? A_BOLD : (attr.intensity == CONS_ATTR_INTENSITY_DIM + ? A_DIM : A_NORMAL)) + | (attr.underlined ? A_UNDERLINE : 0) + | (attr.reversed ? A_REVERSE : 0) + | (attr.blinking ? A_BLINK: 0) + | (attr.concealed ? A_INVIS : 0)); +} + +static inline short +conchar_attr_to_color_pair (conchar_attr_t attr) +{ + return COLOR_PAIR (attr.bgcol << 3 | attr.fgcol); +} + +static void +mvwputsn (conchar_t *str, size_t len, off_t x, off_t y) +{ + cchar_t chr; + wchar_t wch[2] = { L'\0', L'\0' }; + uint32_t last_attr = * (uint32_t *) &str->attr; + attr_t attr = conchar_attr_to_attr (str->attr); + short color_pair = conchar_attr_to_color_pair (str->attr); + + move (y, x); + while (len) + { + int ret; + chtype ac; + + if (last_attr != *(uint32_t *) &str->attr) + { + last_attr = * (uint32_t *) &str->attr; + attr = conchar_attr_to_attr (str->attr); + color_pair = conchar_attr_to_color_pair (str->attr); + } + + if (ucs4_to_altchar (str->chr, &ac)) + addch (ac | attr | color_pair); + else + { + wch[0] = str->chr; + ret = setcchar (&chr, wch, attr, color_pair, NULL); +#if 0 + if (ret == ERR) + { + printf ("setcchar failed: %s\n", strerror (errno)); + printf ("[%lc]\n", wch[0]); + assert (!"Do something if setcchar fails."); + } +#endif + ret = add_wch (&chr); +#if 0 + if (ret == ERR) + { + printf ("add_wch failed: %i, %s\n", ret, strerror (errno)); + printf ("[%lc]\n", wch[0]); + assert (!"Do something if add_wchr fails."); + } +#endif + } + len--; + str++; + } +} + + +static error_t +ncursesw_update (void *handle) +{ + mutex_lock (&ncurses_lock); + refresh (); + mutex_unlock (&ncurses_lock); + return 0; +} + + +static error_t +ncursesw_set_cursor_pos (void *handle, uint32_t col, uint32_t row) +{ + mutex_lock (&ncurses_lock); + move (row, col); + mutex_unlock (&ncurses_lock); + return 0; +} + + +static error_t +ncursesw_set_cursor_status (void *handle, uint32_t status) +{ + mutex_lock (&ncurses_lock); + curs_set (status ? (status == 1 ? 1 : 2) : 0); + mutex_unlock (&ncurses_lock); + return 0; +} + + +static error_t +ncursesw_scroll (void *handle, int delta) +{ + /* XXX We don't support scrollback for now. */ + assert (delta >= 0); + + mutex_lock (&ncurses_lock); + idlok (stdscr, TRUE); + scrollok (stdscr, TRUE); + scrl (delta); + idlok (stdscr, FALSE); + scrollok (stdscr, FALSE); + mutex_unlock (&ncurses_lock); + return 0; +} + + +static error_t +ncursesw_write (void *handle, conchar_t *str, size_t length, + uint32_t col, uint32_t row) +{ + int x; + int y; + + mutex_lock (&ncurses_lock); + getyx (stdscr, y, x); + mvwputsn (str, length, col, row); + wmove (stdscr, y, x); + mutex_unlock (&ncurses_lock); + return 0; +} + + +static error_t +ncursesw_flash (void *handle) +{ + mutex_lock (&ncurses_lock); + flash (); + mutex_unlock (&ncurses_lock); + return 0; +} + + +/* Bell driver operations. */ +error_t +ncursesw_beep (void *handle) +{ + mutex_lock (&ncurses_lock); + beep (); + mutex_unlock (&ncurses_lock); + return 0; +} + + + +static error_t +ncursesw_driver_init (void **handle, int no_exit, + int argc, char *argv[], int *next) +{ + mutex_init (&ncurses_lock); + return 0; +} + +static error_t +ncursesw_driver_start (void *handle) +{ + error_t err; + int i; + + initscr (); + start_color (); + for (i = 0; i < 64; i++) + init_pair (i, i & 7, i >> 3); + raw (); + noecho (); + nonl (); + intrflush (stdscr, FALSE); + nodelay (stdscr, TRUE); + timeout (1); + keypad (stdscr, TRUE); + + err = driver_add_display (&ncursesw_display_ops, NULL); + if (err) + { + endwin (); + return err; + } + err = driver_add_input (&ncursesw_input_ops, NULL); + if (err) + { + err = driver_remove_display (&ncursesw_display_ops, NULL); + endwin (); + return err; + } + err = driver_add_bell (&ncursesw_bell_ops, NULL); + if (err) + { + err = driver_remove_input (&ncursesw_input_ops, NULL); + err = driver_remove_display (&ncursesw_display_ops, NULL); + endwin (); + return err; + } + + cthread_detach (cthread_fork (input_loop, NULL)); + endwin (); + + return err; +} + +/* Destroy the display HANDLE. */ +static error_t +ncursesw_driver_fini (void *handle, int force) +{ + /* XXX Cancel the input thread. */ + mutex_lock (&ncurses_lock); + driver_remove_display (&ncursesw_display_ops, NULL); + driver_remove_input (&ncursesw_input_ops, NULL); + driver_remove_bell (&ncursesw_bell_ops, NULL); + mutex_unlock (&ncurses_lock); + + endwin (); + return 0; +} + + +struct driver_ops driver_ncursesw_ops = + { + ncursesw_driver_init, + ncursesw_driver_start, + ncursesw_driver_fini, + }; + +static struct display_ops ncursesw_display_ops = + { + ncursesw_set_cursor_pos, + ncursesw_set_cursor_status, + ncursesw_scroll, + ncursesw_write, + ncursesw_update, + ncursesw_flash, + NULL + }; + +static struct input_ops ncursesw_input_ops = + { + NULL, + NULL + }; + +static struct bell_ops ncursesw_bell_ops = + { + ncursesw_beep, + NULL + }; diff --git a/console-client/pc-kbd.c b/console-client/pc-kbd.c new file mode 100644 index 00000000..3ec6404e --- /dev/null +++ b/console-client/pc-kbd.c @@ -0,0 +1,824 @@ +/* pc-kbd.c - The PC Keyboard input driver. + Copyright (C) 2002 Free Software Foundation, Inc. + Written by Marcus Brinkmann. + + This file is part of the GNU Hurd. + + The GNU Hurd 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; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "driver.h" + + +/* The keyboard device in the kernel. */ +static device_t kbd_dev; + +/* The converter. */ +static iconv_t cd; + +/* Forward declaration. */ +static struct input_ops pc_kbd_ops; + + +/* A list of scan codes generated by the keyboard. */ +enum scancode + { + SC_ESC = 0x01, + SC_1 = 0x02, + SC_2 = 0x03, + SC_3 = 0x04, + SC_4 = 0x05, + SC_5 = 0x06, + SC_6 = 0x07, + SC_7 = 0x08, + SC_8 = 0x09, + SC_9 = 0x0A, + SC_0 = 0x0B, + SC_MINUS = 0x0C, /* - */ + SC_EQUAL = 0x0D, /* = */ + SC_BACKSPACE = 0x0E, + SC_TAB = 0x0F, + SC_Q = 0x10, + SC_W = 0x11, + SC_E = 0x12, + SC_R = 0x13, + SC_T = 0x14, + SC_Y = 0x15, + SC_U = 0x16, + SC_I = 0x17, + SC_O = 0x18, + SC_P = 0x19, + SC_LEFT_BRACKET = 0x1A, /* [ */ + SC_RIGHT_BRACKET = 0x1B, /* ] */ + SC_ENTER = 0x1C, + SC_LEFT_CTRL = 0x1D, + SC_A = 0x1E, + SC_S = 0x1F, + SC_D = 0x20, + SC_F = 0x21, + SC_G = 0x22, + SC_H = 0x23, + SC_J = 0x24, + SC_K = 0x25, + SC_L = 0x26, + SC_SEMICOLON = 0x27, /* ; */ + SC_APOSTROPHE = 0x28, /* ' */ + SC_BACKQUOTE = 0x29, /* ` */ + SC_LEFT_SHIFT = 0x2A, + SC_BACKSLASH = 0x2B, /* \ */ + SC_Z = 0x2C, + SC_X = 0x2D, + SC_C = 0x2E, + SC_V = 0x2F, + SC_B = 0x30, + SC_N = 0x31, + SC_M = 0x32, + SC_COMMA = 0x33, /* , */ + SC_PERIOD = 0x34, /* . */ + SC_SLASH = 0x35, /* / */ + SC_RIGHT_SHIFT = 0x36, + SC_PAD_ASTERISK = 0x37, + SC_LEFT_ALT = 0x38, + SC_SPACE = 0x39, + SC_CAPSLOCK = 0x3A, + SC_F1 = 0x3B, + SC_F2 = 0x3C, + SC_F3 = 0x3D, + SC_F4 = 0x3E, + SC_F5 = 0x3F, + SC_F6 = 0x40, + SC_F7 = 0x41, + SC_F8 = 0x42, + SC_F9 = 0x43, + SC_F10 = 0x44, + SC_NUMLOCK = 0x45, + SC_SCROLLLOCK = 0x46, + SC_PAD_7 = 0x47, + SC_PAD_8 = 0x48, + SC_PAD_9 = 0x49, + SC_PAD_MINUS = 0x4A, + SC_PAD_4 = 0x4B, + SC_PAD_5 = 0x4C, + SC_PAD_6 = 0x4D, + SC_PAD_PLUS = 0x4E, + SC_PAD_1 = 0x4F, + SC_PAD_2 = 0x50, + SC_PAD_3 = 0x51, + SC_PAD_0 = 0x52, + SC_PAD_DECIMAL = 0x53, + SC_SYSREQ = 0x54, + SC_F11 = 0x57, + SC_F12 = 0x58, + SC_FLAG_UP = 0x80, /* ORed to basic scancode. */ + SC_EXTENDED1 = 0xE0, /* One code follows. */ + SC_EXTENDED2 = 0xE1, /* Two codes follow (only used for Pause). */ + SC_ERROR = 0xFF /* Too many keys held down. */ + }; + +/* Codes which can follow SC_EXTENDED1. */ +enum scancode_x1 + { + SC_X1_PAD_ENTER = 0x1C, + SC_X1_RIGHT_CTRL = 0x1D, + SC_X1_PAD_SLASH = 0x35, + SC_X1_PRTSC = 0x37, + SC_X1_RIGHT_ALT = 0x38, + SC_X1_BREAK = 0x46, + SC_X1_HOME = 0x47, + SC_X1_UP = 0x48, + SC_X1_PGUP = 0x49, + SC_X1_LEFT = 0x4B, + SC_X1_RIGHT = 0x4D, + SC_X1_END = 0x4F, + SC_X1_DOWN = 0x50, + SC_X1_PGDN = 0x51, + SC_X1_INS = 0x52, + SC_X1_DEL = 0x53 + }; + + +/* Scancode to Unicode mapping. The empty string stands for the NULL + character. */ +char *sc_to_kc[][7] = + { + /*None, Shift, Ctrl, LAlt, S+LAlt, C+LAlt, RAlt */ + { 0, 0, 0, 0, 0, 0, 0 }, + { "\e", "\e", "\e", "\e\e", "\e\e", "\e\e", "\e" }, /* SC_ESC. */ + { "1", "!", 0, "\e1", "\e!", 0, "1" }, /* SC_1. */ + { "2", "@", "", "\e2", "\e@", 0, "2" }, /* SC_2. */ + { "3", "#", "\e", "\e3", "\e#", 0, "3" }, /* SC_3. */ + { "4", "$", "\x1c", "\e4", "\e$", "\e\x1c", "4" }, /* SC_4. */ + { "5", "%", "\x1d", "\e5", "\e%", 0, "5" }, /* SC_5. */ + { "6", "^", "\x1e", "\e6", "\e^", 0, "6" }, /* SC_6. */ + { "7", "&", "\x1f", "\e7", "\e&", "\e\x1f", "7" }, /* SC_7. */ + { "8", "*", "\x7f", "\e8", "\e*", 0, "8" }, /* SC_8. */ + { "9", "(", 0, "\e9", "\e(", 0, "9" }, /* SC_9. */ + { "0", ")", 0, "\e0", "\e)", 0, "0" }, /* SC_0. */ + { "-", "_", "\x1f", "\e-", "\e_", "\e\x1f", "-" }, /* SC_MINUS. */ + { "=", "+", 0, "\e=", "\e+", 0, "=" }, /* SC_EQUAL. */ + { CONS_KEY_BACKSPACE, CONS_KEY_BACKSPACE, CONS_KEY_BACKSPACE, /* XXX */ 0, + CONS_KEY_BACKSPACE, /*XXX*/ 0, CONS_KEY_BACKSPACE }, /* SC_BACKSPACE. */ + /* XXX back tab? */ + { "\t", "\t", "\t", "\e\t", "\e\t", "\e\t", "\t" }, /* SC_TAB. */ + { "q", "Q", "\x11", "\eq", "\eQ", "\e\x11", "q" }, /* SC_Q. */ + { "w", "W", "\x17", "\ew", "\eW", "\e\x17", "w" }, /* SC_W. */ + { "e", "E", "\x05", "\ee", "\eE", "\e\x05","\xe2\x82\xac" }, /* SC_E. */ + { "r", "R", "\x12", "\er", "\eR", "\e\x12", "r" }, /* SC_R. */ + { "t", "T", "\x14", "\et", "\eT", "\e\x14", "t" }, /* SC_T. */ + { "y", "Y", "\x19", "\ey", "\eY", "\e\x19", "y" }, /* SC_Y. */ + { "u", "U", "\x15", "\eu", "\eU", "\e\x15", "u" }, /* SC_U. */ + { "i", "I", "\x09", "\ei", "\eI", "\e\x09", "i" }, /* SC_I. */ + { "o", "O", "\x0f", "\eo", "\eO", "\e\x0f", "o" }, /* SC_O. */ + { "p", "P", "\x10", "\ep", "\eP", "\e\x10", "p" }, /* SC_P. */ + { "[", "{", "\e", "\e[", "\e{", 0, 0 }, /* SC_LEFT_BRACKET. */ + { "]", "}", "\x1d", "\e]", "\e}", "\e\x1d", "~" }, /* SC_RIGHT_BRACKET. */ + {"\x0d","\x0d", "\x0d","\e\x0d","\e\x0d","\e\x0d","\x0d" }, /* SC_ENTER. */ + { 0, 0, 0, 0, 0, 0, 0 }, /* SC_LEFT_CTRL. XXX */ + { "a", "A", "\x01", "\ea", "\eA", "\e\x01", "a" }, /* SC_A. */ + { "s", "S", "\x13", "\es", "\eS", "\e\x13", "s" }, /* SC_S. */ + { "d", "D", "\x04", "\ed", "\eD", "\e\x04", "d" }, /* SC_D. */ + { "f", "F", "\x06", "\ef", "\eF", "\e\x06", "f" }, /* SC_F. */ + { "g", "G", "\x07", "\eg", "\eG", "\e\x07", "g" }, /* SC_G. */ + { "h", "H", "\x08", "\eh", "\eH", "\e\x08", "h" }, /* SC_H. */ + { "j", "J", "\x0a", "\ej", "\eJ", "\e\x0a", "j" }, /* SC_J. */ + { "k", "K", "\x0b", "\ek", "\eK", "\e\x0b", "k" }, /* SC_K. */ + { "l", "L", "\x0c", "\el", "\eL", "\e\x0c", "l" }, /* SC_L. */ + { ";", ":", 0, "\e;", "\e:", 0, 0 }, /* SC_SEMICOLON. */ + { "'", "\"", "\x07", "\e'", "\e\"", 0, 0 }, /* SC_APOSTROPHE. */ + { "`", "~", 0, "\e`", "\e~", 0, 0 }, /* SC_BACKQUOTE. */ + { 0, 0, 0, 0, 0, 0, 0 }, /* SC_LEFT_SHIFT. */ + { "\\", "|", "\x1c", "\e\\", "\e|", 0, 0 }, /* SC_BACKSLASH. */ + { "z", "Z", "\x1a", "\ez", "\eZ", "\e\x1a", "z" }, /* SC_Z. */ + { "x", "X", "\x18", "\ex", "\eX", "\e\x18", "x" }, /* SC_X. */ + { "c", "C", "\x03", "\ec", "\eC", "\e\x03", "\xc2\xa2" }, /* SC_C. */ + { "v", "V", "\x16", "\ev", "\eV", "\e\x16", "v" }, /* SC_V. */ + { "b", "B", "\x02", "\eb", "\eB", "\e\x02", "b" }, /* SC_B. */ + { "n", "N", "\x0e", "\en", "\eN", "\e\x0e", "n" }, /* SC_N. */ + { "m", "M", "\x0d", "\em", "\eM", "\e\x0d", "m" }, /* SC_M. */ + { ",", "<", 0, "\e,", "\e<", 0, 0 }, /* SC_COMMA. */ + { ".", ">", 0, "\e.", "\e>", 0, 0 }, /* SC_PERIOD. */ + { "/", "?", "\x7f", "\e/", "\e?", 0, 0 }, /* SC_SLASH. */ + { 0, 0, 0, 0, 0, 0, 0 }, /* SC_RIGHT_SHIFT. */ + { "*", "*", "*", "*", "*", "*", "*" }, /* SC_PAD_ASTERISK. XXX */ + { 0, 0, 0, 0, 0, 0, 0 }, /* SC_LEFT_ALT. XXX */ + { " ", " ", "", "\e ", "\e ", /*XXX*/0, " " }, /* SC_SPACE. */ + { 0, 0, 0, 0, 0, 0, 0 }, /* SC_CAPSLOCK. */ + { CONS_KEY_F1, CONS_KEY_F13, 0, 0, 0, 0, 0 }, /* SC_F1. */ + { CONS_KEY_F2, CONS_KEY_F14, 0, 0, 0, 0, 0 }, /* SC_F2. */ + { CONS_KEY_F3, CONS_KEY_F15, 0, 0, 0, 0, 0 }, /* SC_F3. */ + { CONS_KEY_F4, CONS_KEY_F16, 0, 0, 0, 0, 0 }, /* SC_F4. */ + { CONS_KEY_F5, CONS_KEY_F17, 0, 0, 0, 0, 0 }, /* SC_F5. */ + { CONS_KEY_F6, CONS_KEY_F18, 0, 0, 0, 0, 0 }, /* SC_F6. */ + { CONS_KEY_F7, CONS_KEY_F19, 0, 0, 0, 0, 0 }, /* SC_F7. */ + { CONS_KEY_F8, CONS_KEY_F20, 0, 0, 0, 0, 0 }, /* SC_F8. */ + { CONS_KEY_F9, 0, 0, 0, 0, 0, 0 }, /* SC_F9. */ + { CONS_KEY_F10, 0, 0, 0, 0, 0, 0 }, /* SC_F10. */ + { 0, 0, 0, 0, 0, 0, 0 }, /* SC_NUMLOCK. */ + { 0, 0, 0, 0, 0, 0, 0 }, /* SC_SCROLLLOCK. */ + { CONS_KEY_HOME, CONS_KEY_HOME, CONS_KEY_HOME, 0, 0, 0, 0 }, /* SC_PAD_7. */ + { CONS_KEY_UP, CONS_KEY_UP, CONS_KEY_UP, 0, 0, 0, 0 }, /* SC_PAD_8. */ + { CONS_KEY_PPAGE, CONS_KEY_PPAGE, CONS_KEY_PPAGE,0, 0, 0, 0 }, /* SC_PAD_9. */ + { "-", "-", "-", "-", "-", "-", "-" }, /* SC_PAD_MINUS. */ + { CONS_KEY_LEFT, CONS_KEY_LEFT, CONS_KEY_LEFT, 0, 0, 0, 0 }, /* SC_PAD_4. */ + {/* XXX */ "\e[G", "\e[G", "\e[G", 0, 0, 0, 0 }, /* SC_PAD_5. */ + { CONS_KEY_RIGHT, CONS_KEY_RIGHT, CONS_KEY_RIGHT,0, 0, 0, 0 }, /* SC_PAD_6. */ + { "+", "+", "+", "+", "+", "+", "+" }, /* SC_PAD_MINUS. */ + { CONS_KEY_END, CONS_KEY_END, CONS_KEY_END, 0, 0, 0, 0 }, /* SC_PAD_1. */ + { CONS_KEY_DOWN, CONS_KEY_DOWN, CONS_KEY_DOWN, 0, 0, 0, 0 }, /* SC_PAD_2. */ + { CONS_KEY_NPAGE, CONS_KEY_NPAGE, CONS_KEY_NPAGE,0, 0, 0, 0 }, /* SC_PAD_3. */ + { CONS_KEY_IC, CONS_KEY_IC, CONS_KEY_IC, 0, 0, 0, 0 }, /* SC_PAD_0. */ + { CONS_KEY_DC, CONS_KEY_DC, CONS_KEY_DC, 0, 0, 0, 0 }, /* SC_PAD_DECIMAL. */ + { 0, 0, /*XX*/0, 0, 0, 0, 0 }, /* SC_SYSREQ. */ + { CONS_KEY_F11, 0, 0, 0, 0, 0, 0 }, /* SC_F11. */ + { CONS_KEY_F12, 0, 0, 0, 0, 0, 0 } /* SC_F12. */ + }; + +char *sc_x1_to_kc[][7] = + { + /* None, Shift, Ctrl, LAlt, S+LAlt, C+LAlt, RAlt */ + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { "\n", "\n", "\n", "\n", "\n", "\n", 0 }, /* SC_X1_PAD_ENTER. */ + { 0, 0, 0, 0, 0, 0, 0 }, /* SC_X1_RIGHT_CTRL. */ + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { "/", "/", "/", "/", "/", "/", 0 }, /* SC_X1_PAD_SLASH. */ + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, /* SC_X1_PRTSC. */ + { 0, 0, 0, 0, 0, 0, 0 }, /* SC_X1_RIGHT_ALT. */ + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { "\e[P", "\e[P", "\e[P", "\e[P", "\e[P", "\e[P","\e[P" }, /* SC_X1_BREAK. */ + { CONS_KEY_HOME, CONS_KEY_HOME, CONS_KEY_HOME, CONS_KEY_HOME, + CONS_KEY_HOME, CONS_KEY_HOME, CONS_KEY_HOME }, /* SC_X1_HOME. */ + { CONS_KEY_UP, CONS_KEY_UP, CONS_KEY_UP, CONS_KEY_UP, + CONS_KEY_UP, CONS_KEY_UP, CONS_KEY_UP }, /* SC_X1_UP. */ + { CONS_KEY_PPAGE, CONS_KEY_PPAGE, CONS_KEY_PPAGE, CONS_KEY_PPAGE, + CONS_KEY_PPAGE, CONS_KEY_PPAGE, CONS_KEY_PPAGE }, /* SC_X1_PGUP. */ + { 0, 0, 0, 0, 0, 0, 0 }, + { CONS_KEY_LEFT, CONS_KEY_LEFT, CONS_KEY_LEFT, CONS_KEY_LEFT, + CONS_KEY_LEFT, CONS_KEY_LEFT, CONS_KEY_LEFT }, /* SC_X1_LEFT. */ + { 0, 0, 0, 0, 0, 0, 0 }, + { CONS_KEY_RIGHT, CONS_KEY_RIGHT, CONS_KEY_RIGHT, CONS_KEY_RIGHT, + CONS_KEY_RIGHT, CONS_KEY_RIGHT, CONS_KEY_RIGHT }, /* SC_X1_RIGHT. */ + { 0, 0, 0, 0, 0, 0, 0 }, + { CONS_KEY_END, CONS_KEY_END, CONS_KEY_END, CONS_KEY_END, + CONS_KEY_END, CONS_KEY_END, CONS_KEY_END }, /* SC_X1_END. */ + { CONS_KEY_DOWN, CONS_KEY_DOWN, CONS_KEY_DOWN, CONS_KEY_DOWN, + CONS_KEY_DOWN, CONS_KEY_DOWN, CONS_KEY_DOWN }, /* SC_X1_DOWN. */ + { CONS_KEY_NPAGE, CONS_KEY_NPAGE, CONS_KEY_NPAGE, CONS_KEY_NPAGE, + CONS_KEY_NPAGE, CONS_KEY_NPAGE, CONS_KEY_NPAGE }, /* SC_X1_PGDN. */ + { CONS_KEY_IC, CONS_KEY_IC, CONS_KEY_IC, CONS_KEY_IC, + CONS_KEY_IC, CONS_KEY_IC, CONS_KEY_IC }, /* SC_X1_INS. */ + { CONS_KEY_DC, CONS_KEY_DC, CONS_KEY_DC, CONS_KEY_DC, + CONS_KEY_DC, CONS_KEY_DC, CONS_KEY_DC } /* SC_X1_DEL. */ + }; + + +/* This gross stuff is cut & pasted from Mach sources, as Mach doesn't + export the interface we are using here. */ + +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ + +typedef u_short kev_type; /* kd event type */ + +/* (used for event records) */ +struct mouse_motion { + short mm_deltaX; /* units? */ + short mm_deltaY; +}; +typedef u_char Scancode; + +typedef struct { + kev_type type; /* see below */ + struct timeval time; /* timestamp */ + union { /* value associated with event */ + boolean_t up; /* MOUSE_LEFT .. MOUSE_RIGHT */ + Scancode sc; /* KEYBD_EVENT */ + struct mouse_motion mmotion; /* MOUSE_MOTION */ + } value; +} kd_event; +#define m_deltaX mmotion.mm_deltaX +#define m_deltaY mmotion.mm_deltaY + +/* + * kd_event ID's. + */ +#define MOUSE_LEFT 1 /* mouse left button up/down */ +#define MOUSE_MIDDLE 2 +#define MOUSE_RIGHT 3 +#define MOUSE_MOTION 4 /* mouse motion */ +#define KEYBD_EVENT 5 /* key up/down */ + + +#define IOCPARM_MASK 0x1fff /* parameter length, at most 13 bits */ +#define IOC_OUT 0x40000000 /* copy out parameters */ +#define IOC_IN 0x80000000U /* copy in parameters */ +#define _IOC(inout,group,num,len) \ + (inout | ((len & IOCPARM_MASK) << 16) | ((group) << 8) | (num)) +#define _IOR(g,n,t) _IOC(IOC_OUT, (g), (n), sizeof(t)) +#define _IOW(g,n,t) _IOC(IOC_IN, (g), (n), sizeof(t)) + +#define KDSKBDMODE _IOW('K', 1, int) /* set keyboard mode */ +#define KB_EVENT 1 +#define KB_ASCII 2 + +#define KDGKBDTYPE _IOR('K', 2, int) /* get keyboard type */ +#define KB_VANILLAKB 0 + +/* End of Mach code. */ + + +/* The input loop. */ +static any_t +input_loop (any_t unused) +{ + while (1) + { + kd_event data_buf; + + /* io_buf_ptr_t is (char *), not (void *). So I have a few + casts to quiet warnings. */ + io_buf_ptr_t data_ptr = (void *) &data_buf; + mach_msg_type_number_t data_cnt = sizeof data_buf; + + /* I suppose this could be sped up by reading multiple events + per call. But that isn't important since this is called just + a couple of times per keystroke. Things might be different + if we supported a mouse. */ + error_t err = device_read (kbd_dev, /* device */ + 0, /* mode: could be D_NOWAIT */ + -1, /* recnum: not used by kbdread */ + sizeof (kd_event), /* bytes_wanted */ + &data_ptr, &data_cnt); + if (err) + /* XXX The error occured likely because KBD_DEV was closed, so + terminate. */ + return 0; + + if ((void *) data_ptr != &data_buf) + { + /* Copy the structure to the expected place. */ + data_buf = *(kd_event *) data_ptr; + munmap (data_ptr, data_cnt); + } + + if (data_buf.type == KEYBD_EVENT) + { + enum scancode fsc = data_buf.value.sc; + enum scancode sc = fsc & 0x7f; + int down = !(fsc & SC_FLAG_UP); + char buf[100]; + size_t size = 0; + int modifier = -1; + + static struct { + wchar_t direct; + unsigned int extended : 2; + unsigned int left_shift : 1; + unsigned int right_shift : 1; + unsigned int caps_lock : 1; + unsigned int caps_lock_pressed : 1; + unsigned int left_ctrl : 1; + unsigned int right_ctrl : 1; + unsigned int left_alt : 1; + unsigned int right_alt : 1; + unsigned int num_lock : 1; + unsigned int num_lock_pressed : 1; + } state; + + if (!state.left_alt && !state.right_alt) + { + if (state.left_ctrl || state.right_ctrl) + modifier = 2; + else if (state.left_shift || state.right_shift) + modifier = 1; + else + modifier = 0; + } + else if (state.left_alt) + { + if (state.left_ctrl || state.right_ctrl) + modifier = 5; + if (state.left_shift || state.right_shift) + modifier = 4; + else + modifier = 3; + } + else if (state.right_alt) + { + if (!state.left_ctrl && !state.right_ctrl + && !state.left_shift && !state.right_shift) + modifier = 6; + } + + if (!state.extended) + { + if (fsc == SC_EXTENDED1) + state.extended = 1; + else if (fsc == SC_EXTENDED2) + state.extended = 2; + else if (sc == SC_LEFT_SHIFT) + state.left_shift = down; + else if (sc == SC_RIGHT_SHIFT) + state.right_shift = down; + else if (sc == SC_CAPSLOCK) + { + if (down && !state.caps_lock_pressed) + { + state.caps_lock = !state.caps_lock; + state.caps_lock_pressed = 1; + } + else if (!down) + state.caps_lock_pressed = 0; + } + else if (sc == SC_NUMLOCK) + { + if (down && !state.num_lock_pressed) + { + state.num_lock = !state.num_lock; + state.num_lock_pressed = 1; + } + else if (!down) + state.num_lock_pressed = 0; + } + else if (sc == SC_LEFT_CTRL) + state.left_ctrl = down; + else if (sc == SC_LEFT_ALT) + state.left_alt = down; + else if (state.left_alt && down && sc >= SC_F1 && sc <= SC_F10) /* XXX */ + console_switch (1 + (sc - SC_F1), 0); + else if (state.left_alt && state.left_ctrl && down && sc == SC_BACKSPACE) + console_exit (); + else if (state.right_alt && down && sc == SC_PAD_0) /* XXX */ + state.direct = (state.direct << 4) | 0x0; + else if (state.right_alt && down && sc == SC_PAD_1) /* XXX */ + state.direct = (state.direct << 4) | 0x1; + else if (state.right_alt && down && sc == SC_PAD_2) /* XXX */ + state.direct = (state.direct << 4) | 0x2; + else if (state.right_alt && down && sc == SC_PAD_3) /* XXX */ + state.direct = (state.direct << 4) | 0x3; + else if (state.right_alt && down && sc == SC_PAD_4) /* XXX */ + state.direct = (state.direct << 4) | 0x4; + else if (state.right_alt && down && sc == SC_PAD_5) /* XXX */ + state.direct = (state.direct << 4) | 0x5; + else if (state.right_alt && down && sc == SC_PAD_6) /* XXX */ + state.direct = (state.direct << 4) | 0x6; + else if (state.right_alt && down && sc == SC_PAD_7) /* XXX */ + state.direct = (state.direct << 4) | 0x7; + else if (state.right_alt && down && sc == SC_PAD_8) /* XXX */ + state.direct = (state.direct << 4) | 0x8; + else if (state.right_alt && down && sc == SC_PAD_9) /* XXX */ + state.direct = (state.direct << 4) | 0x9; + else if (state.right_alt && down && sc == SC_NUMLOCK) /* XXX */ + state.direct = (state.direct << 4) | 0xa; + else if (state.right_alt && down && sc == SC_PAD_ASTERISK) /* XXX */ + state.direct = (state.direct << 4) | 0xc; + else if (state.right_alt && down && sc == SC_PAD_MINUS) /* XXX */ + state.direct = (state.direct << 4) | 0xd; + else if (state.right_alt && down && sc == SC_PAD_PLUS) /* XXX */ + state.direct = (state.direct << 4) | 0xe; + else if (down && sc < sizeof (sc_to_kc)/sizeof (sc_to_kc[0])) + { +#if QUAERENDO_INVENIETIS + if (state.left_alt && state.right_alt + && sc_to_kc[sc][0][0] >= '0' && sc_to_kc[sc][0][0] <= '9' + && sc_to_kc[sc][0][1] == '\0') + console_deprecated (sc_to_kc[sc][0][0] - '0'); + else +#endif + { + /* Special rule for caps lock. */ + if (modifier == 0 && state.caps_lock + && sc_to_kc[sc][modifier] + && sc_to_kc[sc][modifier][0] >= 'a' + && sc_to_kc[sc][modifier][0] <= 'z' + && sc_to_kc[sc][modifier][1] == '\0') + modifier = 1; + else if (state.num_lock && sc == SC_PAD_0) + { + modifier = 0; + sc = SC_0; + } + else if (state.num_lock && sc == SC_PAD_1) + { + modifier = 0; + sc = SC_1; + } + else if (state.num_lock && sc == SC_PAD_2) + { + modifier = 0; + sc = SC_2; + } + else if (state.num_lock && sc == SC_PAD_3) + { + modifier = 0; + sc = SC_3; + } + else if (state.num_lock && sc == SC_PAD_4) + { + modifier = 0; + sc = SC_4; + } + else if (state.num_lock && sc == SC_PAD_5) + { + modifier = 0; + sc = SC_5; + } + else if (state.num_lock && sc == SC_PAD_6) + { + modifier = 0; + sc = SC_6; + } + else if (state.num_lock && sc == SC_PAD_7) + { + modifier = 0; + sc = SC_7; + } + else if (state.num_lock && sc == SC_PAD_8) + { + modifier = 0; + sc = SC_8; + } + else if (state.num_lock && sc == SC_PAD_9) + { + modifier = 0; + sc = SC_9; + } + else if (state.num_lock && sc == SC_PAD_DECIMAL) + { + modifier = 0; + sc = SC_PERIOD; + } + + if (modifier >= 0 && sc_to_kc[sc][modifier]) + { + if (!sc_to_kc[sc][modifier][0]) + { + /* Special meaning, emit NUL. */ + assert (size < 100); + buf[size++] = '\0'; + } + else + { + assert (size + < 101 - strlen(sc_to_kc[sc][modifier])); + strcpy (&buf[size], sc_to_kc[sc][modifier]); + size += strlen (sc_to_kc[sc][modifier]); + } + } + } + } + } + else if (state.extended == 1) + { + state.extended = 0; + if (sc == SC_X1_RIGHT_CTRL) + state.right_ctrl = down; + else if (sc == SC_X1_RIGHT_ALT) + { + state.right_alt = down; + + /* Handle the AltGR+Keypad direct input. */ + if (down) + state.direct = (wchar_t) 0; + else + { + if (state.direct != (wchar_t) 0) + { + char *buffer = &buf[size]; + size_t left = sizeof (buf) - size; + char *inbuf = (char *) &state.direct; + size_t inbufsize = sizeof (wchar_t); + int nr; + + nr = iconv (cd, &inbuf, &inbufsize, &buffer, &left); + if (nr == (size_t) -1) + { + if (errno == E2BIG) + console_error (L"Input buffer overflow"); + else if (errno == EILSEQ) + console_error + (L"Input contained invalid byte sequence"); + else if (errno == EINVAL) + console_error + (L"Input contained incomplete byte sequence"); + else + console_error + (L"Input caused unexpected error"); + } + size = sizeof (buf) - left; + } + } + } + else if (state.right_alt && down && sc == SC_X1_PAD_SLASH) /* XXX */ + state.direct = (state.direct << 4) | 0xb; + else if (state.right_alt && down && sc == SC_X1_PAD_ENTER) /* XXX */ + state.direct = (state.direct << 4) | 0xf; + else if (state.left_alt && down && sc == SC_X1_RIGHT) /* XXX */ + console_switch (0, 1); + else if (state.left_alt && down && sc == SC_X1_LEFT) /* XXX */ + console_switch (0, -1); + else if (state.left_alt && down && sc == SC_X1_UP) /* XXX */ + console_scrollback (CONS_SCROLL_DELTA_LINES, 1); + else if (state.left_alt && down && sc == SC_X1_DOWN) /* XXX */ + console_scrollback (CONS_SCROLL_DELTA_LINES, -1); + else if (state.right_shift && down && sc == SC_X1_PGUP) /* XXX */ + console_scrollback (CONS_SCROLL_DELTA_SCREENS, 0.5); + else if (state.right_shift && down && sc == SC_X1_PGDN) /* XXX */ + console_scrollback (CONS_SCROLL_DELTA_SCREENS, -0.5); + else if (down && sc < sizeof (sc_x1_to_kc)/sizeof (sc_x1_to_kc[0])) + { + if (modifier >= 0 && sc_x1_to_kc[sc][modifier]) + { + assert (size < 101 - strlen(sc_x1_to_kc[sc][modifier])); + strcpy (&buf[size], sc_x1_to_kc[sc][modifier]); + size += strlen (sc_x1_to_kc[sc][modifier]); + } + } + } + else if (state.extended == 2) + state.extended = 3; + else if (state.extended == 3) + state.extended = 0; + + if (size) + console_input (buf, size); + } + } + return 0; +} + + + +/* Initialize the PC keyboard driver. */ +static error_t +pc_kbd_init (void **handle, int no_exit, int argc, char *argv[], int *next) +{ + return 0; +} + + +/* Start the PC keyboard driver. */ +static error_t +pc_kbd_start (void *handle) +{ + error_t err; + device_t device_master; + int data[1]; + + cd = iconv_open ("UTF-8", "WCHAR_T"); + if (cd == (iconv_t) -1) + return errno; + + err = get_privileged_ports (0, &device_master); + if (err) + { + iconv_close (cd); + return err; + } + + err = device_open (device_master, D_READ, "kbd", &kbd_dev); + mach_port_deallocate (mach_task_self (), device_master); + if (err) + { + iconv_close (cd); + return err; + } + + data[0] = KB_EVENT; + err = device_set_status (kbd_dev, KDSKBDMODE, data, 1); + if (err) + { + device_close (kbd_dev); + mach_port_deallocate (mach_task_self (), kbd_dev); + iconv_close (cd); + return err; + } + + err = driver_add_input (&pc_kbd_ops, NULL); + if (err) + { + device_set_status (kbd_dev, KDSKBDMODE, data, 1); + device_close (kbd_dev); + mach_port_deallocate (mach_task_self (), kbd_dev); + iconv_close (cd); + return err; + } + cthread_detach (cthread_fork (input_loop, NULL)); + + return 0; +} + +/* Deinitialize the PC keyboard driver. */ +static error_t +pc_kbd_fini (void *handle, int force) +{ + int data[1]; + + driver_remove_input (&pc_kbd_ops, NULL); + data[0] = KB_ASCII; + device_set_status (kbd_dev, KDSKBDMODE, data, 1); + device_close (kbd_dev); + mach_port_deallocate (mach_task_self (), kbd_dev); + iconv_close (cd); + + return 0; +} + + +/* Set the scroll lock status indication (Scroll LED) to ONOFF. */ +static error_t +pc_kbd_set_scroll_lock_status (void *handle, int onoff) +{ + /* XXX */ + return 0; +} + + +struct driver_ops driver_pc_kbd_ops = + { + pc_kbd_init, + pc_kbd_start, + pc_kbd_fini + }; + +static struct input_ops pc_kbd_ops = + { + pc_kbd_set_scroll_lock_status, + NULL + }; diff --git a/console-client/timer.c b/console-client/timer.c new file mode 100644 index 00000000..7bcdeab0 --- /dev/null +++ b/console-client/timer.c @@ -0,0 +1,210 @@ +/* timer.c - A timer module for Mach. + Copyright (C) 1995,96,2000,02 Free Software Foundation, Inc. + Written by Michael I. Bushnell, p/BSG and Marcus Brinkmann. + + This file is part of the GNU Hurd. + + The GNU Hurd 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; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#include +#include +#include + +#include + +#include "timer.h" + +/* The value of fetch_jiffies() at startup. */ +long long timer_root_jiffies; + +/* The mapped time. */ +volatile struct mapped_time_value *timer_mapped_time; + + +/* The timer thread. */ +static thread_t timer_thread; + +/* The lock protects the timer list TIMERS. */ +static struct mutex timer_lock; + +/* A list of all active timers. */ +static struct timer_list *timers; + + +static inline void +timer_add_internal (struct timer_list *timer) +{ + struct timer_list **tp; + + for (tp = &timers; *tp; tp = &(*tp)->next) + if ((*tp)->expires > timer->expires) + { + timer->next = *tp; + timer->next->prev = &timer->next; + timer->prev = tp; + *tp = timer; + break; + } + if (!*tp) + { + timer->next = 0; + timer->prev = tp; + *tp = timer; + } +} + + +/* Make the timer thread aware of new timers at the beginning of the + list. */ +static inline void +kick_timer_thread (void) +{ + /* XXX This is a whacky notion. */ + while (!timer_thread) + swtch_pri (0); + + if (timer_thread != mach_thread_self ()) + { + thread_suspend (timer_thread); + thread_abort (timer_thread); + thread_resume (timer_thread); + } +} + +/* The timer thread. */ +static int +timer_function (int this_is_a_pointless_variable_with_a_rather_long_name) +{ + mach_port_t recv = mach_reply_port (); + int wait = 0; + + timer_thread = mach_thread_self (); + + mutex_lock (&timer_lock); + while (1) + { + int jiff = fetch_jiffies (); + + if (!timers) + wait = -1; + else if (timers->expires < jiff) + wait = 0; + else + wait = ((timers->expires - jiff) * 1000) / HZ; + + mutex_unlock (&timer_lock); + mach_msg (NULL, (MACH_RCV_MSG | MACH_RCV_INTERRUPT + | (wait == -1 ? 0 : MACH_RCV_TIMEOUT)), + 0, 0, recv, wait, MACH_PORT_NULL); + mutex_lock (&timer_lock); + + while (timers && timers->expires < fetch_jiffies ()) + { + struct timer_list *tp; + + tp = timers; + + timers = timers->next; + if (timers) + timers->prev = &timers; + + tp->next = 0; + tp->prev = 0; + + if ((*tp->fnc) (tp->fnc_data)) + timer_add_internal (tp); + } + } + + return 0; +} + + +/* Initialize the timer component. Must be called once at startup. */ +error_t +timer_init (void) +{ + error_t err; + struct timeval tp; + + mutex_init (&timer_lock); + + err = maptime_map (0, 0, &timer_mapped_time); + if (err) + return err; + + maptime_read (timer_mapped_time, &tp); + + timer_root_jiffies = (long long) tp.tv_sec * HZ + + ((long long) tp.tv_usec * HZ) / 1000000; + + cthread_detach (cthread_fork ((cthread_fn_t) timer_function, 0)); + return 0; +} + + +/* Initialize the timer TIMER. */ +void +timer_clear (struct timer_list *timer) +{ + memset (timer, 0, sizeof (struct timer_list)); +} + +/* Add the timer TIMER to the list. */ +void +timer_add (struct timer_list *timer) +{ + mutex_lock (&timer_lock); + timer_add_internal (timer); + + if (timers == timer) + kick_timer_thread (); + + mutex_unlock (&timer_lock); +} + +/* Remove the timer TIMER from the list. */ +int +timer_remove (struct timer_list *timer) +{ + mutex_lock (&timer_lock); + if (timer->prev) + { + *timer->prev = timer->next; + if (timer->next) + timer->next->prev = timer->prev; + + timer->next = 0; + timer->prev = 0; + mutex_unlock (&timer_lock); + return 1; + } + else + { + mutex_unlock (&timer_lock); + return 0; + } +} + +/* Change the expiration time of the timer TIMER to EXPIRES. */ +void +timer_change (struct timer_list *timer, long long expires) +{ + /* XXX Should optimize this. */ + timer_remove (timer); + timer->expires = expires; + timer_add (timer); +} + diff --git a/console-client/timer.h b/console-client/timer.h new file mode 100644 index 00000000..4204192e --- /dev/null +++ b/console-client/timer.h @@ -0,0 +1,72 @@ +/* timer.h - Interface to a timer module for Mach. + Copyright (C) 1995,96,2000,02 Free Software Foundation, Inc. + Written by Michael I. Bushnell, p/BSG and Marcus Brinkmann. + + This file is part of the GNU Hurd. + + The GNU Hurd 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; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#ifndef _TIMER_H_ +#define _TIMER_H_ + +#include +#include + + +/* Initialize the timer component. Must be called once at startup. */ +error_t timer_init (void); + +/* The data structure of a timer. A user can set the values EXPIRES, + DATA and FUNCTION, and should leave the other fields alone. */ +struct timer_list +{ + struct timer_list *next, **prev; /* things like to test "T->prev != NULL" */ + long long expires; + + /* The function to be called when the timer expires. If the + function returns a non-zero value, the timer is put back on the + list. */ + int (*fnc) (void *); + void *fnc_data; +}; + +/* Initialize the timer TIMER. */ +void timer_clear (struct timer_list *timer); + +/* Add the timer TIMER to the list. */ +void timer_add (struct timer_list *timer); + +/* Remove the timer TIMER from the list. */ +int timer_remove (struct timer_list *timer); + +/* Change the expiration time of the timer TIMER to EXPIRES. */ +void timer_change (struct timer_list *timer, long long expires); + +extern inline long long +fetch_jiffies () +{ + extern volatile struct mapped_time_value *timer_mapped_time; + extern long long timer_root_jiffies; + struct timeval tv; + long long j; + + maptime_read (timer_mapped_time, &tv); + +#define HZ 100 + j = (long long) tv.tv_sec * HZ + ((long long) tv.tv_usec * HZ) / 1000000; + return j - timer_root_jiffies; +} + +#endif /* _TIMER_H_ */ diff --git a/console-client/unicode.h b/console-client/unicode.h new file mode 100644 index 00000000..386628c9 --- /dev/null +++ b/console-client/unicode.h @@ -0,0 +1,350 @@ +/* unicode.h - A list of useful Unicode characters. + Copyright (C) 2002 Free Software Foundation, Inc. + Written by Marcus Brinkmann. + + This file is part of the GNU Hurd. + + The GNU Hurd 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; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#ifndef _UNICODE_H_ +#define _UNICODE_H_ + +#define UNICODE_NO_BREAK_SPACE ((wchar_t) 0x00a0) +#define UNICODE_INVERTED_EXCLAMATION_MARK ((wchar_t) 0x00a1) +#define UNICODE_CENT_SIGN ((wchar_t) 0x00a2) +#define UNICODE_POUND_SIGN ((wchar_t) 0x00a3) +#define UNICODE_CURRENCY_SIGN ((wchar_t) 0x00a4) +#define UNICODE_YEN_SIGN ((wchar_t) 0x00a5) +#define UNICODE_BROKEN_BAR ((wchar_t) 0x00a6) +#define UNICODE_BROKEN_VERTICAL_BAR UNICODE_BROKEN_BAR +#define UNICODE_SECTION_SIGN ((wchar_t) 0x00a7) +#define UNICODE_DIARESIS ((wchar_t) 0x00a8) +#define UNICODE_COPYRIGHT_SIGN ((wchar_t) 0x00a9) +#define UNICODE_FEMININE_ORDINAL_INDICATOR ((wchar_t) 0x00aa) +#define UNICODE_LEFT_POINTING_DOUBLE_ANGLE_QUOTATION_MARK ((wchar_t) 0x00ab) +#define UNICODE_LEFT_POINTING_GUILLEMET \ + UNICODE_LEFT_POINTING_DOUBLE_ANGLE_QUOTATION_MARK +#define UNICODE_NOT_SIGN ((wchar_t) 0x00ac) +#define UNICODE_SOFT_HYPHEN ((wchar_t) 0x00ad) +#define UNICODE_REGISTERED_SIGN ((wchar_t) 0x00ae) +#define UNICODE_REGISTERED_TRADE_MARK_SIGN UNICODE_REGISTERED_SIGN +#define UNICODE_MACRON ((wchar_t) 0x00af) +#define UNICODE_DEGREE_SIGN ((wchar_t) 0x00b0) +#define UNICODE_PLUS_MINUS_SIGN ((wchar_t) 0x00b1) +#define UNICODE_SUPERSCRIPT_TWO ((wchar_t) 0x00b2) +#define UNICODE_SUPERSCRIPT_THREE ((wchar_t) 0x00b3) +#define UNICODE_ACUTE_ACCENT ((wchar_t) 0x00b4) +#define UNICODE_MICRO_SIGN ((wchar_t) 0x00b5) +#define UNICODE_PILCROW_SIGN ((wchar_t) 0x00b6) +#define UNICODE_PARAGRAPH_SIGN UNICODE_PILCROW_SIGN +#define UNICODE_MIDDLE_DOT ((wchar_t) 0x00b7) +#define UNICODE_CEDILLA ((wchar_t) 0x00b8) +#define UNICODE_SUPERSCRIPT_ONE ((wchar_t) 0x00b9) +#define UNICODE_MASCULINE_ORDINAL_INDICATOR ((wchar_t) 0x00ba) +#define UNICODE_RIGHT_POINTING_DOUBLE_ANGLE_QUOTATION_MARK ((wchar_t) 0x00bb) +#define UNICODE_RIGHT_POINTING_GUILLEMET \ + UNICODE_RIGHT_POINTING_DOUBLE_ANGLE_QUOTATION_MARK +#define UNICODE_VULGAR_FRACTION_ONE_QUARTER ((wchar_t) 0x00bc) +#define UNICODE_VULGAR_FRACTION_ONE_HALF ((wchar_t) 0x00bd) +#define UNICODE_VULGAR_FRACTION_THREE_QUARTERS ((wchar_t) 0x00be) +#define UNICODE_INVERTED_QUESTION_MARK ((wchar_t) 0x00bf) +#define UNICODE_LATIN_CAPITAL_LETTER_A_WITH_GRAVE ((wchar_t) 0x00c0) +#define UNICODE_LATIN_CAPITAL_LETTER_A_WITH_ACUTE ((wchar_t) 0x00c1) +#define UNICODE_LATIN_CAPITAL_LETTER_A_WITH_CIRCUMFLEX ((wchar_t) 0x00c2) +#define UNICODE_LATIN_CAPITAL_LETTER_A_WITH_TILDE ((wchar_t) 0x00c3) +#define UNICODE_LATIN_CAPITAL_LETTER_A_WITH_DIARESIS ((wchar_t) 0x00c4) +#define UNICODE_LATIN_CAPITAL_LETTER_A_WITH_RING_ABOVE ((wchar_t) 0x00c5) +#define UNICODE_LATIN_CAPITAL_LETTER_AE ((wchar_t) 0x00c6) +#define UNICODE_LATIN_CAPITAL_LETTER_C_WITH_CEDILLA ((wchar_t) 0x00c7) +#define UNICODE_LATIN_CAPITAL_LIGATURE_AE UNICODE_LATIN_CAPITAL_LETTER_AE +#define UNICODE_LATIN_CAPITAL_LETTER_E_WITH_GRAVE ((wchar_t) 0x00c8) +#define UNICODE_LATIN_CAPITAL_LETTER_E_WITH_ACUTE ((wchar_t) 0x00c9) +#define UNICODE_LATIN_CAPITAL_LETTER_E_WITH_CIRCUMFLEX ((wchar_t) 0x00ca) +#define UNICODE_LATIN_CAPITAL_LETTER_E_WITH_DIARESIS ((wchar_t) 0x00cb) +#define UNICODE_LATIN_CAPITAL_LETTER_I_WITH_GRAVE ((wchar_t) 0x00cc) +#define UNICODE_LATIN_CAPITAL_LETTER_I_WITH_ACUTE ((wchar_t) 0x00cd) +#define UNICODE_LATIN_CAPITAL_LETTER_I_WITH_CIRCUMFLEX ((wchar_t) 0x00ce) +#define UNICODE_LATIN_CAPITAL_LETTER_I_WITH_DIARESIS ((wchar_t) 0x00cf) +#define UNICODE_LATIN_CAPITAL_LETTER_ETH ((wchar_t) 0x00d0) +#define UNICODE_LATIN_CAPITAL_LETTER_N_WITH_TILDE ((wchar_t) 0x00d1) +#define UNICODE_LATIN_CAPITAL_LETTER_O_WITH_GRAVE ((wchar_t) 0x00d2) +#define UNICODE_LATIN_CAPITAL_LETTER_O_WITH_ACUTE ((wchar_t) 0x00d3) +#define UNICODE_LATIN_CAPITAL_LETTER_O_WITH_CIRCUMFLEX ((wchar_t) 0x00d4) +#define UNICODE_LATIN_CAPITAL_LETTER_O_WITH_TILDE ((wchar_t) 0x00d5) +#define UNICODE_LATIN_CAPITAL_LETTER_O_WITH_DIARESIS ((wchar_t) 0x00d6) +#define UNICODE_MULTIPLICATION_SIGN ((wchar_t) 0x00d7) +#define UNICODE_CAPITAL_LETTER_O_WITH_STROKE ((wchar_t) 0x00d8) +#define UNICODE_LATIN_CAPITAL_LETTER_U_WITH_GRAVE ((wchar_t) 0x00d9) +#define UNICODE_LATIN_CAPITAL_LETTER_U_WITH_ACUTE ((wchar_t) 0x00da) +#define UNICODE_LATIN_CAPITAL_LETTER_U_WITH_CIRCUMFLEX ((wchar_t) 0x00db) +#define UNICODE_LATIN_CAPITAL_LETTER_U_WITH_DIARESIS ((wchar_t) 0x00dc) +#define UNICODE_LATIN_CAPITAL_LETTER_Y_WITH_ACUTE ((wchar_t) 0x00dd) +#define UNICODE_LATIN_CAPITAL_LETTER_THORN ((wchar_t) 0x00de) +#define UNICODE_LATIN_SMALL_LETTER_SHARP_S ((wchar_t) 0x00df) +#define UNICODE_LATIN_SMALL_LETTER_A_WITH_GRAVE ((wchar_t) 0x00e0) +#define UNICODE_LATIN_SMALL_LETTER_A_WITH_ACUTE ((wchar_t) 0x00e1) +#define UNICODE_LATIN_SMALL_LETTER_A_WITH_CIRCUMFLEX ((wchar_t) 0x00e2) +#define UNICODE_LATIN_SMALL_LETTER_A_WITH_DIARESIS ((wchar_t) 0x00e4) +#define UNICODE_LATIN_SMALL_LETTER_A_WITH_RING_ABOVE ((wchar_t) 0x00e5) +#define UNICODE_LATIN_SMALL_LETTER_AE ((wchar_t) 0x00e6) +#define UNICODE_LATIN_SMALL_LIGATURE_AE UNICODE_LATIN_SMALL_LETTER_AE +#define UNICODE_LATIN_SMALL_LETTER_C_WITH_CEDILLA ((wchar_t) 0x00e7) +#define UNICODE_LATIN_SMALL_LETTER_E_WITH_GRAVE ((wchar_t) 0x00e8) +#define UNICODE_LATIN_SMALL_LETTER_E_WITH_ACUTE ((wchar_t) 0x00e9) +#define UNICODE_LATIN_SMALL_LETTER_E_WITH_CIRCUMFLEX ((wchar_t) 0x00ea) +#define UNICODE_LATIN_SMALL_LETTER_E_WITH_DIARESIS ((wchar_t) 0x00eb) +#define UNICODE_LATIN_SMALL_LETTER_I_WITH_GRAVE ((wchar_t) 0x00ec) +#define UNICODE_LATIN_SMALL_LETTER_I_WITH_ACUTE ((wchar_t) 0x00ed) +#define UNICODE_LATIN_SMALL_LETTER_I_WITH_CIRCUMFLEX ((wchar_t) 0x00ee) +#define UNICODE_LATIN_SMALL_LETTER_I_WITH_DIARESIS ((wchar_t) 0x00ef) +#define UNICODE_LATIN_SMALL_LETTER_ETH ((wchar_t) 0x00f0) +#define UNICODE_LATIN_SMALL_LETTER_N_WITH_TILDE ((wchar_t) 0x00f1) +#define UNICODE_LATIN_SMALL_LETTER_O_WITH_GRAVE ((wchar_t) 0x00f2) +#define UNICODE_LATIN_SMALL_LETTER_O_WITH_ACUTE ((wchar_t) 0x00f3) +#define UNICODE_LATIN_SMALL_LETTER_O_WITH_CIRCUMFLEX ((wchar_t) 0x00f4) +#define UNICODE_LATIN_SMALL_LETTER_O_WITH_DIARESIS ((wchar_t) 0x00f6) +#define UNICODE_DIVISION_SIGN ((wchar_t) 0x00f7) +#define UNICODE_SMALL_LETTER_O_WITH_STROKE ((wchar_t) 0x00f8) +#define UNICODE_LATIN_SMALL_LETTER_U_WITH_GRAVE ((wchar_t) 0x00f9) +#define UNICODE_LATIN_SMALL_LETTER_U_WITH_ACUTE ((wchar_t) 0x00fa) +#define UNICODE_LATIN_SMALL_LETTER_U_WITH_CIRCUMFLEX ((wchar_t) 0x00fb) +#define UNICODE_LATIN_SMALL_LETTER_U_WITH_DIARESIS ((wchar_t) 0x00fc) +#define UNICODE_LATIN_SMALL_LETTER_Y_WITH_ACUTE ((wchar_t) 0x00fd) +#define UNICODE_LATIN_SMALL_LETTER_THORN ((wchar_t) 0x00fe) +#define UNICODE_LATIN_SMALL_LETTER_Y_WITH_DIARESIS ((wchar_t) 0x00ff) + +#define UNICODE_LATIN_SMALL_LETTER_F_WITH_HOOK ((wchar_t) 0x0192) +#define UNICODE_LATIN_SMALL_LETTER_SCRIPT_F \ + UNICODE_LATIN_SMALL_LETTER_F_WITH_HOOK +#define UNICODE_GREEK_CAPITAL_LETTER_GAMMA ((wchar_t) 0x0393) +#define UNICODE_GREEK_CAPITAL_LETTER_OMICRON ((wchar_t) 0x039f) +#define UNICODE_GREEK_CAPITAL_LETTER_SIGMA ((wchar_t) 0x03a3) +#define UNICODE_GREEK_CAPITAL_LETTER_PHI ((wchar_t) 0x03a6) +#define UNICODE_GREEK_CAPITAL_LETTER_OMEGA ((wchar_t) 0x03a9) +#define UNICODE_GREEK_SMALL_LETTER_ALPHA ((wchar_t) 0x03b1) +#define UNICODE_GREEK_SMALL_LETTER_BETA ((wchar_t) 0x03b2) +#define UNICODE_GREEK_SMALL_LETTER_DELTA ((wchar_t) 0x03b4) +#define UNICODE_GREEK_SMALL_LETTER_EPSILON ((wchar_t) 0x03b5) +#define UNICODE_GREEK_SMALL_LETTER_MU ((wchar_t) 0x03bc) +#define UNICODE_GREEK_SMALL_LETTER_PI ((wchar_t) 0x03c0) +#define UNICODE_GREEK_SMALL_LETTER_SIGMA ((wchar_t) 0x03c3) +#define UNICODE_GREEK_SMALL_LETTER_TAU ((wchar_t) 0x03c4) +#define UNICODE_GREEK_SMALL_LETTER_PHI ((wchar_t) 0x03c6) + +#define UNICODE_BULLET ((wchar_t) 0x2022) +#define UNICODE_DOUBLE_EXCLAMATION_MARK ((wchar_t) 0x203c) +#define UNICODE_SUPERSCRIPT_LATIN_SMALL_LETTER ((wchar_t) 0x207f) +#define UNICODE_PESETA_SIGN ((wchar_t) 0x20a7) + +#define UNICODE_LEFTWARDS_ARROW ((wchar_t) 0x2190) +#define UNICODE_UPWARDS_ARROW ((wchar_t) 0x2191) +#define UNICODE_RIGHTWARDS_ARROW ((wchar_t) 0x2192) +#define UNICODE_DOWNWARDS_ARROW ((wchar_t) 0x2193) +#define UNICODE_LEFT_RIGHT_ARROW ((wchar_t) 0x2194) +#define UNICODE_UP_DOWN_ARROW ((wchar_t) 0x2195) +#define UNICODE_UP_DOWN_ARROW_WITH_BASE ((wchar_t) 0x21a8) + +#define UNICODE_BULLET_OPERATOR ((wchar_t) 0x2219) +#define UNICODE_SQUARE_ROOT ((wchar_t) 0x221a) +#define UNICODE_INFINITY ((wchar_t) 0x221e) +#define UNICODE_RIGHT_ANGLE ((wchar_t) 0x221f) +#define UNICODE_INTERSECTION ((wchar_t) 0x2229) +#define UNICODE_ALMOST_EQUAL_TO ((wchar_t) 0x2248) +#define UNICODE_NOT_EQUAL_TO ((wchar_t) 0x2260) +#define UNICODE_IDENTICAL_TO ((wchar_t) 0x2261) +#define UNICODE_LESS_THAN_OR_EQUAL_TO ((wchar_t) 0x2264) +#define UNICODE_GREATER_THAN_OR_EQUAL_TO ((wchar_t) 0x2265) + +#define UNICODE_HOUSE ((wchar_t) 0x2302) +#define UNICODE_REVERSED_NOT_SIGN ((wchar_t) 0x2310) +#define UNICODE_TOP_HALF_INTEGRAL ((wchar_t) 0x2320) +#define UNICODE_BOTTOM_HALF_INTEGRAL ((wchar_t) 0x2321) + +#define UNICODE_BOX_DRAWINGS_LIGHT_HORIZONTAL ((wchar_t) 0x2500) +#define UNICODE_BOX_DRAWINGS_HEAVY_HORIZONTAL ((wchar_t) 0x2501) +#define UNICODE_BOX_DRAWINGS_LIGHT_VERTICAL ((wchar_t) 0x2502) +#define UNICODE_BOX_DRAWINGS_LIGHT_DOWN_AND_RIGHT ((wchar_t) 0x250c) +#define UNICODE_BOX_DRAWINGS_DOWN_LIGHT_AND_RIGHT_HEAVY ((wchar_t) 0x250d) +#define UNICODE_BOX_DRAWINGS_DOWN_HEAVY_AND_RIGHT_LIGHT ((wchar_t) 0x250e) +#define UNICODE_BOX_DRAWINGS_HEAVY_DOWN_AND_RIGHT ((wchar_t) 0x250f) +#define UNICODE_BOX_DRAWINGS_LIGHT_DOWN_AND_LEFT ((wchar_t) 0x2510) +#define UNICODE_BOX_DRAWINGS_LIGHT_UP_AND_RIGHT ((wchar_t) 0x2514) +#define UNICODE_BOX_DRAWINGS_UP_LIGHT_AND_RIGHT_HEAVY ((wchar_t) 0x2515) +#define UNICODE_BOX_DRAWINGS_UP_HEAVY_AND_RIGHT_LIGHT ((wchar_t) 0x2516) +#define UNICODE_BOX_DRAWINGS_HEAVY_UP_AND_RIGHT ((wchar_t) 0x2517) +#define UNICODE_BOX_DRAWINGS_LIGHT_UP_AND_LEFT ((wchar_t) 0x2518) +#define UNICODE_BOX_DRAWINGS_LIGHT_VERTICAL_AND_RIGHT ((wchar_t) 0x251c) +#define UNICODE_BOX_DRAWINGS_VERTICAL_LIGHT_AND_RIGHT_HEAVY ((wchar_t) 0x251d) +#define UNICODE_BOX_DRAWINGS_UP_HEAVY_AND_RIGHT_UP_LIGHT ((wchar_t) 0x251e) +#define UNICODE_BOX_DRAWINGS_DOWN_HEAVY_AND_RIGHT_UP_LIGHT ((wchar_t) 0x251f) +#define UNICODE_BOX_DRAWINGS_VERTICAL_HEAVY_AND_RIGHT_LIGHT ((wchar_t) 0x2520) +#define UNICODE_BOX_DRAWINGS_DOWN_LIGHT_AND_RIGHT_UP_HEAVY ((wchar_t) 0x2521) +#define UNICODE_BOX_DRAWINGS_UP_LIGHT_AND_RIGHT_DOWN_HEAVY ((wchar_t) 0x2522) +#define UNICODE_BOX_DRAWINGS_HEAVY_VERTICAL_AND_RIGHT ((wchar_t) 0x2523) +#define UNICODE_BOX_DRAWINGS_LIGHT_VERTICAL_AND_LEFT ((wchar_t) 0x2524) +#define UNICODE_BOX_DRAWINGS_LIGHT_DOWN_AND_HORIZONTAL ((wchar_t) 0x252c) +#define UNICODE_BOX_DRAWINGS_LEFT_HEAVY_AND_RIGHT_DOWN_LIGHT ((wchar_t) 0x252d) +#define UNICODE_BOX_DRAWINGS_RIGHT_HEAVY_AND_LEFT_DOWN_LIGHT ((wchar_t) 0x252e) +#define UNICODE_BOX_DRAWINGS_DOWN_LIGHT_AND_HORIZONTAL_HEAVY ((wchar_t) 0x252f) +#define UNICODE_BOX_DRAWINGS_DOWN_HEAVY_AND_HORIZONTAL_LIGHT ((wchar_t) 0x2530) +#define UNICODE_BOX_DRAWINGS_RIGHT_LIGHT_AND_LEFT_DOWN_HEAVY ((wchar_t) 0x2531) +#define UNICODE_BOX_DRAWINGS_LEFT_LIGHT_AND_RIGHT_DOWN_HEAVY ((wchar_t) 0x2532) +#define UNICODE_BOX_DRAWINGS_HEAVY_DOWN_AND_HORIZONTAL ((wchar_t) 0x2533) +#define UNICODE_BOX_DRAWINGS_LIGHT_UP_AND_HORIZONTAL ((wchar_t) 0x2534) +#define UNICODE_BOX_DRAWINGS_LEFT_HEAVY_AND_RIGHT_UP_LIGHT ((wchar_t) 0x2535) +#define UNICODE_BOX_DRAWINGS_RIGHT_HEAVY_AND_LEFT_UP_LIGHT ((wchar_t) 0x2536) +#define UNICODE_BOX_DRAWINGS_UP_LIGHT_AND_HORIZONTAL_HEAVY ((wchar_t) 0x2537) +#define UNICODE_BOX_DRAWINGS_UP_HEAVY_AND_HORIZONTAL_LIGHT ((wchar_t) 0x2538) +#define UNICODE_BOX_DRAWINGS_RIGHT_LIGHT_AND_LEFT_UP_HEAVY ((wchar_t) 0x2539) +#define UNICODE_BOX_DRAWINGS_LEFT_LIGHT_AND_RIGHT_UP_HEAVY ((wchar_t) 0x253a) +#define UNICODE_BOX_DRAWINGS_HEAVY_UP_AND_HORIZONTAL ((wchar_t) 0x253b) +#define UNICODE_BOX_DRAWINGS_LIGHT_VERTICAL_AND_HORIZONTAL ((wchar_t) 0x253c) +#define UNICODE_BOX_DRAWINGS_LEFT_HEAVY_AND_RIGHT_VERTICAL_LIGHT \ + ((wchar_t) 0x253d) +#define UNICODE_BOX_DRAWINGS_RIGHT_HEAVY_AND_LEFT_VERTICAL_LIGHT \ + ((wchar_t) 0x253e) +#define UNICODE_BOX_DRAWINGS_VERTICAL_LIGHT_AND_HORIZONTAL_HEAVY \ + ((wchar_t) 0x253f) +#define UNICODE_BOX_DRAWINGS_UP_HEAVY_AND_DOWN_HORIZONTAL_LIGHT \ + ((wchar_t) 0x2540) +#define UNICODE_BOX_DRAWINGS_DOWN_HEAVY_AND_UP_HORIZONTAL_LIGHT \ + ((wchar_t) 0x2541) +#define UNICODE_BOX_DRAWINGS_VERTICAL_HEAVY_AND_HORIZONTAL_LIGHT \ + ((wchar_t) 0x2542) +#define UNICODE_BOX_DRAWINGS_LEFT_UP_HEAVY_AND_RIGHT_DOWN_LIGHT \ + ((wchar_t) 0x2543) +#define UNICODE_BOX_DRAWINGS_RIGHT_UP_HEAVY_AND_LEFT_DOWN_LIGHT \ + ((wchar_t) 0x2544) +#define UNICODE_BOX_DRAWINGS_LEFT_DOWN_HEAVY_AND_RIGHT_UP_LIGHT \ + ((wchar_t) 0x2545) +#define UNICODE_BOX_DRAWINGS_RIGHT_DOWN_HEAVY_AND_LEFT_UP_LIGHT \ + ((wchar_t) 0x2546) +#define UNICODE_BOX_DRAWINGS_DOWN_LIGHT_AND_UP_HORIZONTAL_HEAVY \ + ((wchar_t) 0x2547) +#define UNICODE_BOX_DRAWINGS_UP_LIGHT_AND_DOWN_HORIZONTAL_HEAVY \ + ((wchar_t) 0x2548) +#define UNICODE_BOX_DRAWINGS_RIGHT_LIGHT_AND_LEFT_VERTICAL_HEAVY \ + ((wchar_t) 0x2549) +#define UNICODE_BOX_DRAWINGS_LEFT_LIGHT_AND_RIGHT_VERTICAL_HEAVY \ + ((wchar_t) 0x254a) +#define UNICODE_BOX_DRAWINGS_HEAVY_VERTICAL_AND_HORIZONTAL \ + ((wchar_t) 0x254b) +#define UNICODE_BOX_DRAWINGS_DOUBLE_HORIZONTAL ((wchar_t) 0x2550) +#define UNICODE_BOX_DRAWINGS_DOUBLE_VERTICAL ((wchar_t) 0x2551) +#define UNICODE_BOX_DRAWINGS_DOWN_SINGLE_AND_RIGHT_DOUBLE ((wchar_t) 0x2552) +#define UNICODE_BOX_DRAWINGS_DOWN_DOUBLE_AND_RIGHT_SINGLE ((wchar_t) 0x2553) +#define UNICODE_BOX_DRAWINGS_DOUBLE_DOWN_AND_RIGHT ((wchar_t) 0x2554) +#define UNICODE_BOX_DRAWINGS_DOWN_SINGLE_AND_LEFT_DOUBLE ((wchar_t) 0x2555) +#define UNICODE_BOX_DRAWINGS_DOWN_DOUBLE_AND_LEFT_SINGLE ((wchar_t) 0x2556) +#define UNICODE_BOX_DRAWINGS_DOUBLE_DOWN_AND_LEFT ((wchar_t) 0x2557) +#define UNICODE_BOX_DRAWINGS_UP_SINGLE_AND_RIGHT_DOUBLE ((wchar_t) 0x2558) +#define UNICODE_BOX_DRAWINGS_UP_DOUBLE_AND_RIGHT_SINGLE ((wchar_t) 0x2559) +#define UNICODE_BOX_DRAWINGS_DOUBLE_UP_AND_RIGHT ((wchar_t) 0x255a) +#define UNICODE_BOX_DRAWINGS_UP_SINGLE_AND_LEFT_DOUBLE ((wchar_t) 0x255b) +#define UNICODE_BOX_DRAWINGS_UP_DOUBLE_AND_LEFT_SINGLE ((wchar_t) 0x255c) +#define UNICODE_BOX_DRAWINGS_DOUBLE_UP_AND_LEFT ((wchar_t) 0x255d) +#define UNICODE_BOX_DRAWINGS_VERTICAL_SINGLE_AND_RIGHT_DOUBLE \ + ((wchar_t) 0x255e) +#define UNICODE_BOX_DRAWINGS_VERTICAL_DOUBLE_AND_RIGHT_SINGLE \ + ((wchar_t) 0x255f) +#define UNICODE_BOX_DRAWINGS_DOUBLE_VERTICAL_AND_RIGHT ((wchar_t) 0x2560) +#define UNICODE_BOX_DRAWINGS_VERTICAL_SINGLE_AND_LEFT_DOUBLE ((wchar_t) 0x2561) +#define UNICODE_BOX_DRAWINGS_VERTICAL_DOUBLE_AND_LEFT_SINGLE ((wchar_t) 0x2562) +#define UNICODE_BOX_DRAWINGS_DOUBLE_VERTICAL_AND_LEFT ((wchar_t) 0x2563) +#define UNICODE_BOX_DRAWINGS_DOWN_SINGLE_AND_HORIZONTAL_DOUBLE \ + ((wchar_t) 0x2564) +#define UNICODE_BOX_DRAWINGS_DOWN_DOUBLE_AND_HORIZONTAL_SINGLE \ + ((wchar_t) 0x2565) +#define UNICODE_BOX_DRAWINGS_DOUBLE_DOWN_AND_HORIZONTAL ((wchar_t) 0x2566) +#define UNICODE_BOX_DRAWINGS_UP_SINGLE_AND_HORIZONTAL_DOUBLE ((wchar_t) 0x2567) +#define UNICODE_BOX_DRAWINGS_UP_DOUBLE_AND_HORIZONTAL_SINGLE ((wchar_t) 0x2568) +#define UNICODE_BOX_DRAWINGS_DOUBLE_UP_AND_HORIZONTAL ((wchar_t) 0x2569) +#define UNICODE_BOX_DRAWINGS_VERTICAL_SINGLE_AND_HORIZONTAL_DOUBLE \ + ((wchar_t) 0x256a) +#define UNICODE_BOX_DRAWINGS_VERTICAL_DOUBLE_AND_HORIZONTAL_SINGLE \ + ((wchar_t) 0x256b) +#define UNICODE_BOX_DRAWINGS_DOUBLE_VERTICAL_AND_HORIZONTAL ((wchar_t) 0x256c) +#define UNICODE_BOX_DRAWINGS_LIGHT_ARC_DOWN_AND_RIGHT ((wchar_t) 0x256d) +#define UNICODE_BOX_DRAWINGS_LIGHT_ARC_UP_AND_RIGHT ((wchar_t) 0x2570) +#define UNICODE_BOX_DRAWINGS_LIGHT_DIAGONAL_UPPER_RIGHT_TO_LOWER_LEFT \ + ((wchar_t) 0x2571) +#define UNICODE_BOX_DRAWINGS_LIGHT_DIAGONAL_UPPER_LEFT_TO_LOWER_RIGHT \ + ((wchar_t) 0x2572) +#define UNICODE_BOX_DRAWINGS_LIGHT_DIAGONAL_CROSS ((wchar_t) 0x2573) +#define UNICODE_BOX_DRAWINGS_LIGHT_RIGHT ((wchar_t) 0x2576) +#define UNICODE_BOX_DRAWINGS_HEAVY_RIGHT ((wchar_t) 0x257a) +#define UNICODE_BOX_DRAWINGS_LIGHT_LEFT_AND_HEAVY_RIGHT ((wchar_t) 0x257c) +#define UNICODE_BOX_DRAWINGS_HEAVY_LEFT_AND_LIGHT_RIGHT ((wchar_t) 0x257e) + +#define UNICODE_UPPER_HALF_BLOCK ((wchar_t) 0x2580) +#define UNICODE_LOWER_ONE_EIGHTH_BLOCK ((wchar_t) 0x2581) +#define UNICODE_LOWER_ONE_QUARTER_BLOCK ((wchar_t) 0x2582) +#define UNICODE_LOWER_THREE_EIGHTHS_BLOCK ((wchar_t) 0x2583) +#define UNICODE_LOWER_HALF_BLOCK ((wchar_t) 0x2584) +#define UNICODE_LOWER_FIVE_EIGHTHS_BLOCK ((wchar_t) 0x2585) +#define UNICODE_LOWER_THREE_QUARTERS_BLOCK ((wchar_t) 0x2586) +#define UNICODE_LOWER_SEVEN_EIGHTHS_BLOCK ((wchar_t) 0x2587) +#define UNICODE_FULL_BLOCK ((wchar_t) 0x2588) +#define UNICODE_LEFT_HALF_BLOCK ((wchar_t) 0x258c) +#define UNICODE_RIGHT_HALF_BLOCK ((wchar_t) 0x2590) +#define UNICODE_LIGHT_SHADE ((wchar_t) 0x2591) +#define UNICODE_MEDIUM_SHADE ((wchar_t) 0x2592) +#define UNICODE_DARK_SHADE ((wchar_t) 0x2593) +#define UNICODE_UPPER_ONE_EIGHTH_BLOCK ((wchar_t) 0x2594) +#define UNICODE_RIGHT_ONE_EIGHTH_BLOCK ((wchar_t) 0x2595) +#define UNICODE_QUADRANT_LOWER_LEFT ((wchar_t) 0x2596) +#define UNICODE_QUADRANT_LOWER_RIGHT ((wchar_t) 0x2597) +#define UNICODE_QUADRANT_UPPER_LEFT ((wchar_t) 0x2598) +#define UNICODE_QUADRANT_UPPER_LEFT_AND_LOWER_LEFT_AND_LOWER_RIGHT \ + ((wchar_t) 0x2599) +#define UNICODE_QUADRANT_UPPER_LEFT_AND_LOWER_RIGHT ((wchar_t) 0x259a) +#define UNICODE_QUADRANT_UPPER_LEFT_AND_UPPER_RIGHT_AND_LOWER_LEFT \ + ((wchar_t) 0x259b) +#define UNICODE_QUADRANT_UPPER_LEFT_AND_UPPER_RIGHT_AND_LOWER_RIGHT \ + ((wchar_t) 0x259c) +#define UNICODE_QUADRANT_UPPER_RIGHT ((wchar_t) 0x259d) +#define UNICODE_QUADRANT_UPPER_RIGHT_AND_LOWER_LEFT ((wchar_t) 0x259e) +#define UNICODE_QUADRANT_UPPER_RIGHT_AND_LOWER_LEFT_AND_LOWER_RIGHT \ + ((wchar_t) 0x259f) + +#define UNICODE_BLACK_SQUARE ((wchar_t) 0x25a0) +#define UNICODE_BLACK_RECTANGLE ((wchar_t) 0x25ac) +#define UNICODE_BLACK_UP_POINTING_TRIANGLE ((wchar_t) 0x25b2) +#define UNICODE_BLACK_RIGHT_POINTING_TRIANGLE ((wchar_t) 0x25b6) +#define UNICODE_BLACK_DOWN_POINTING_TRIANGLE ((wchar_t) 0x25bc) +#define UNICODE_BLACK_LEFT_POINTING_TRIANGLE ((wchar_t) 0x25c0) +#define UNICODE_WHITE_CIRCLE ((wchar_t) 0x25cb) +#define UNICODE_INVERSE_BULLET ((wchar_t) 0x25d8) +#define UNICODE_INVERSE_WHITE_CIRCLE ((wchar_t) 0x25d9) + +#define UNICODE_WHITE_SMILING_FACE ((wchar_t) 0x263a) +#define UNICODE_BLACK_SMILING_FACE ((wchar_t) 0x263b) +#define UNICODE_WHITE_SUN_WITH_RAYS ((wchar_t) 0x263c) +#define UNICODE_FEMALE_SIGN ((wchar_t) 0x2640) +#define UNICODE_MALE_SIGN ((wchar_t) 0x2642) +#define UNICODE_BLACK_SPADE_SUIT ((wchar_t) 0x2660) +#define UNICODE_BLACK_CLUB_SUIT ((wchar_t) 0x2663) +#define UNICODE_BLACK_HEART_SUIT ((wchar_t) 0x2665) +#define UNICODE_BLACK_DIAMOND_SUIT ((wchar_t) 0x2666) +#define UNICODE_EIGHTH_NOTE ((wchar_t) 0x266a) +#define UNICODE_BEAMED_EIGHTH_NOTES ((wchar_t) 0x266b) + +#define UNICODE_PRIVATE_USE_AREA ((wchar_t) 0xe000) +#define UNICODE_PRIVATE_USE_AREA_LAST ((wchar_t) 0xf8ff) + +#define UNICODE_REPLACEMENT_CHARACTER ((wchar_t) 0xfffd) + +#endif /* _UNICODE_H_ */ + diff --git a/console-client/vga-dynacolor.c b/console-client/vga-dynacolor.c new file mode 100644 index 00000000..7b81e2f9 --- /dev/null +++ b/console-client/vga-dynacolor.c @@ -0,0 +1,322 @@ +/* vga-dynacolor.c - Dynamic color handling for VGA cards. + Copyright (C) 2002 Free Software Foundation, Inc. + Written by Marcus Brinkmann. + + This file is part of the GNU Hurd. + + The GNU Hurd 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; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#include + +#include + +#include "vga-hw.h" +#include "vga-support.h" +#include "vga-dynacolor.h" + + +dynacolor_t dynacolor_init_8 = DYNACOLOR_INIT_8; +dynacolor_t dynacolor_init_16 = DYNACOLOR_INIT_16; + +static const unsigned char std_palette[16][DYNACOLOR_COMPONENTS] = + { + { 0, 0, 0 }, /* Black. */ + { 42, 0, 0 }, /* Red. */ + { 0, 42, 0 }, /* Green. */ + { 42, 21, 0 }, /* Brown. */ + { 0, 0, 42 }, /* Blue. */ + { 42, 0, 42 }, /* Magenta. */ + { 0, 42, 42 }, /* Cyan. */ + { 42, 42, 42 }, /* White. */ + { 21, 21, 21 }, /* Bright Black. */ + { 63, 21, 21 }, /* Bright Red. */ + { 21, 63, 21 }, /* Bright Green. */ + { 63, 63, 21 }, /* Bright Yellow. */ + { 21, 21, 63 }, /* Bright Blue. */ + { 63, 21, 63 }, /* Bright Magenta. */ + { 21, 63, 63 }, /* Bright Cyan. */ + { 63, 63, 63 } /* Bright White. */ + }; + +/* The currently active (visible) dynafont. */ +static dynacolor_t *active_dynacolor; + +static unsigned char saved_palette[16][DYNACOLOR_COMPONENTS]; + +/* We initialize this to the desired mapping for + vga_exchange_palette_attributes. */ +/* Palette index 0 is left as it is, for the border color. */ +static unsigned char saved_palette_attr[16] = + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; + + +/* Initialize the dynacolor subsystem. */ +void +dynacolor_init (void) +{ + /* Palette index 0 is left as it is, as it is also used for the + border color by default. */ + vga_exchange_palette_attributes (0, saved_palette_attr, 16); + vga_read_palette (0, saved_palette[0], 16); + + vga_write_palette (0, std_palette[0], 16); +} + + +/* Restore the original palette. */ +void +dynacolor_fini (void) +{ + vga_write_palette (0, saved_palette[0], 16); + vga_exchange_palette_attributes (0, saved_palette_attr, 16); +} + + +/* Activate the dynamic color palette DC. */ +void +dynacolor_activate (dynacolor_t *dc) +{ + if (dc == active_dynacolor) + return; + + if (dc->ref[0] < 0 && (!active_dynacolor || active_dynacolor->ref[0] >= 0)) + { + /* Switching from dynamic to static palette. */ + vga_write_palette (0, std_palette[0], 16); + } + else if (dc->ref[0] >= 0 + && (!active_dynacolor || active_dynacolor->ref[0] < 0)) + { + /* Switching from static to dynamic palette. */ + int i; + for (i = 0; i < 16; i++) + if (dc->col[i] >= 0) + { + vga_write_palette (dc->col[i], std_palette[i], 1); + vga_write_palette (8 + dc->col[i], std_palette[i], 1); + } + } + active_dynacolor = dc; +} + + +/* Try to allocate a slot for the color COL in the dynamic color + palette DC. Return the allocated slot number or -1 if no slot is + available. */ +signed char +dynacolor_allocate (dynacolor_t *dc, unsigned char col) +{ + int i; + + for (i = 0; i < 8; i++) + if (dc->ref[i] == 0) + { + /* We want to reuse slot i. Clear the old user. */ + int j; + + for (j = 0; j < 16; j++) + if (dc->col[j] == i) + { + dc->col[j] = -1; + break; + } + + dc->ref[i] = 1; + dc->col[col] = i; + if (active_dynacolor == dc) + { + vga_write_palette (0 + i, std_palette[col], 1); + vga_write_palette (8 + i, std_palette[col], 1); + } + return i; + } + return -1; +} + + +/* This is a convenience function that looks up a replacement color + pair if the original colors are not available. The function always + succeeds. DC is the dynacolor to use for allocation, FGCOL and + BGCOL are the desired colors, and R_FGCOL and R_BGCOL are the + resulting colors. The function assumes that the caller already + tried to look up the desired colors before trying to replace them, + and that the result of the lookup is contained in R_FGCOL and + R_BGCOL. + + Example: + + res_bgcol = dynacolor_lookup (&dc, bgcol); + res_fgcol = dynacolor_lookup (&dc, fgcol); + if (res_bgcol == -1 || res_fgcol == -1) + dynacolor_replace_colors (&dc, fgcol, bgcol, &res_fgcol, &res_bgcol); + + After the above code, res_fgcol and res_bgcol contain valid color + values. */ +void +dynacolor_replace_colors (dynacolor_t *dc, + signed char fgcol, signed char bgcol, + signed char *r_fgcol, signed char *r_bgcol) +{ + /* Replacement colors. As we have 8 colors in our palette, and + one was already tried, we only need to try out 8 possible + replacements. Only the first seven can fail. But one is + possibly taken by the fore-/background color, so we actually + have to try up to nine. XXX Maybe we should have a table + based on pairs, but that increases the number of cases a + lot. */ + /* Note that no color must occur twice in one replacement list, + and that the color to be replaced must not occure either. */ + static signed char pref[16][9] = + { + /* Replacements for CONS_COLOR_BLACK. */ + { CONS_COLOR_BLACK | (1 << 3), CONS_COLOR_BLUE, + CONS_COLOR_YELLOW, CONS_COLOR_RED, CONS_COLOR_MAGENTA, + CONS_COLOR_GREEN, CONS_COLOR_CYAN, CONS_COLOR_WHITE, + CONS_COLOR_BLUE | (1 << 3) }, + /* Replacements for CONS_COLOR_RED. */ + { CONS_COLOR_RED | (1 << 3), CONS_COLOR_YELLOW, + CONS_COLOR_MAGENTA, CONS_COLOR_BLUE, CONS_COLOR_CYAN, + CONS_COLOR_GREEN, CONS_COLOR_WHITE, CONS_COLOR_BLACK, + CONS_COLOR_BLACK | (1 << 3) }, + /* Replacements for CONS_COLOR_GREEN. */ + { CONS_COLOR_GREEN | (1 << 3), CONS_COLOR_CYAN, + CONS_COLOR_YELLOW, CONS_COLOR_BLUE, CONS_COLOR_RED, + CONS_COLOR_MAGENTA, CONS_COLOR_WHITE, CONS_COLOR_BLACK, + CONS_COLOR_BLACK | (1 << 3) }, + /* Replacements for CONS_COLOR_YELLOW. */ + { CONS_COLOR_YELLOW | (1 << 3), CONS_COLOR_RED, + CONS_COLOR_GREEN, CONS_COLOR_MAGENTA, CONS_COLOR_BLUE, + CONS_COLOR_CYAN, CONS_COLOR_WHITE, CONS_COLOR_BLACK, + CONS_COLOR_BLACK | (1 << 3) }, + /* Replacements for CONS_COLOR_BLUE. */ + { CONS_COLOR_BLUE | (1 << 3), CONS_COLOR_CYAN, + CONS_COLOR_MAGENTA, CONS_COLOR_RED, CONS_COLOR_GREEN, + CONS_COLOR_YELLOW, CONS_COLOR_WHITE, CONS_COLOR_BLACK, + CONS_COLOR_BLACK | (1 << 3) }, + /* Replacements for CONS_COLOR_MAGENTA. */ + { CONS_COLOR_MAGENTA | (1 << 3), CONS_COLOR_RED, + CONS_COLOR_BLUE, CONS_COLOR_YELLOW, CONS_COLOR_CYAN, + CONS_COLOR_BLUE, CONS_COLOR_WHITE, CONS_COLOR_BLACK, + CONS_COLOR_BLACK | (1 << 3) }, + /* Replacements for CONS_COLOR_CYAN. */ + { CONS_COLOR_CYAN | (1 << 3), CONS_COLOR_BLUE, + CONS_COLOR_MAGENTA, CONS_COLOR_GREEN, CONS_COLOR_RED, + CONS_COLOR_YELLOW, CONS_COLOR_WHITE, CONS_COLOR_BLACK, + CONS_COLOR_BLACK | (1 << 3) }, + /* Replacements for CONS_COLOR_WHITE. */ + { CONS_COLOR_WHITE | (1 << 3), CONS_COLOR_CYAN, + CONS_COLOR_GREEN, CONS_COLOR_YELLOW, CONS_COLOR_MAGENTA, + CONS_COLOR_RED, CONS_COLOR_BLUE, CONS_COLOR_CYAN | (1 << 3), + CONS_COLOR_BLACK | (1 << 3) }, + /* Replacements for CONS_COLOR_BLACK | (1 << 3). */ + { CONS_COLOR_BLACK, CONS_COLOR_BLUE | (1 << 3), + CONS_COLOR_YELLOW | (1 << 3), CONS_COLOR_RED | (1 << 3), + CONS_COLOR_MAGENTA | (1 << 3), CONS_COLOR_GREEN | (1 << 3), + CONS_COLOR_CYAN | (1 << 3), CONS_COLOR_WHITE | (1 << 3), + CONS_COLOR_WHITE }, + /* Replacements for CONS_COLOR_RED | (1 << 3). */ + { CONS_COLOR_RED, CONS_COLOR_YELLOW | (1 << 3), + CONS_COLOR_MAGENTA | (1 << 3), CONS_COLOR_BLUE | (1 << 3), + CONS_COLOR_CYAN | (1 << 3), CONS_COLOR_GREEN | (1 << 3), + CONS_COLOR_WHITE | (1 << 3), CONS_COLOR_WHITE, + CONS_COLOR_BLACK | (1 << 3) }, + /* Replacements for CONS_COLOR_GREEN | (1 << 3). */ + { CONS_COLOR_GREEN, CONS_COLOR_CYAN | (1 << 3), + CONS_COLOR_YELLOW | (1 << 3), CONS_COLOR_BLUE | (1 << 3), + CONS_COLOR_RED | (1 << 3), CONS_COLOR_MAGENTA | (1 << 3), + CONS_COLOR_WHITE | (1 << 3), CONS_COLOR_WHITE, + CONS_COLOR_BLACK | (1 << 3) }, + /* Replacements for CONS_COLOR_YELLOW | (1 << 3). */ + { CONS_COLOR_YELLOW, CONS_COLOR_RED | (1 << 3), + CONS_COLOR_GREEN | (1 << 3), CONS_COLOR_MAGENTA | (1 << 3), + CONS_COLOR_BLUE | (1 << 3), CONS_COLOR_CYAN | (1 << 3), + CONS_COLOR_WHITE | (1 << 3), CONS_COLOR_WHITE, + CONS_COLOR_BLACK | (1 << 3) }, + /* Replacements for CONS_COLOR_BLUE | (1 << 3). */ + { CONS_COLOR_BLUE, CONS_COLOR_CYAN | (1 << 3), + CONS_COLOR_MAGENTA | (1 << 3), CONS_COLOR_RED | (1 << 3), + CONS_COLOR_GREEN | (1 << 3), CONS_COLOR_YELLOW | (1 << 3), + CONS_COLOR_WHITE | (1 << 3), CONS_COLOR_WHITE, + CONS_COLOR_BLACK | (1 << 3) }, + /* Replacements for CONS_COLOR_MAGENTA | (1 << 3). */ + { CONS_COLOR_MAGENTA, CONS_COLOR_RED | (1 << 3), + CONS_COLOR_BLUE | (1 << 3), CONS_COLOR_YELLOW | (1 << 3), + CONS_COLOR_CYAN | (1 << 3), CONS_COLOR_GREEN | (1 << 3), + CONS_COLOR_WHITE | (1 << 3), CONS_COLOR_WHITE, + CONS_COLOR_BLACK | (1 << 3) }, + /* Replacements for CONS_COLOR_CYAN | (1 << 3). */ + { CONS_COLOR_CYAN, CONS_COLOR_BLUE | (1 << 3), + CONS_COLOR_MAGENTA | (1 << 3), CONS_COLOR_GREEN | (1 << 3), + CONS_COLOR_RED | (1 << 3), CONS_COLOR_YELLOW | (1 << 3), + CONS_COLOR_WHITE | (1 << 3), CONS_COLOR_WHITE, + CONS_COLOR_BLACK | (1 << 3) }, + /* Replacements for CONS_COLOR_WHITE | (1 << 3). */ + { CONS_COLOR_WHITE, CONS_COLOR_CYAN | (1 << 3), + CONS_COLOR_GREEN | (1 << 3), CONS_COLOR_YELLOW | (1 << 3), + CONS_COLOR_MAGENTA | (1 << 3), CONS_COLOR_RED | (1 << 3), + CONS_COLOR_BLUE | (1 << 3), CONS_COLOR_CYAN, + CONS_COLOR_BLACK | (1 << 3) }, + }; + + signed char res_fgcol = *r_fgcol; + signed char res_bgcol = *r_bgcol; + signed char new_bgcol = bgcol; + int i; + + /* First get a background color. */ + if (res_bgcol == -1) + { + for (i = 0; i < 9; i++) + { + /* If the foreground color is found, make sure to not + set the background to it. */ + if (res_fgcol == -1 || pref[bgcol][i] != fgcol) + { + res_bgcol = dynacolor_lookup (*dc, pref[bgcol][i]); + if (res_bgcol >= 0) + break; + } + } + + assert (res_bgcol >= 0); + new_bgcol = pref[bgcol][i]; + } + + if (fgcol == bgcol) + { + assert (res_fgcol == -1); + /* Acquire another reference. */ + res_fgcol = dynacolor_lookup (*dc, new_bgcol); + } + else + assert (res_fgcol != res_bgcol); + + if (res_fgcol == -1) + { + /* Now find a foreground color. */ + for (i = 0; i < 9; i++) + { + if (pref[fgcol][i] != new_bgcol) + { + res_fgcol = dynacolor_lookup (*dc, pref[fgcol][i]); + if (res_fgcol >= 0) + break; + } + } + assert (res_fgcol >= 0); + } + *r_fgcol = res_fgcol; + *r_bgcol = res_bgcol; +} diff --git a/console-client/vga-dynacolor.h b/console-client/vga-dynacolor.h new file mode 100644 index 00000000..304bcc1b --- /dev/null +++ b/console-client/vga-dynacolor.h @@ -0,0 +1,108 @@ +/* vga-dynacolor.h - Interface to dynamic color handling for VGA cards. + Copyright (C) 2002 Free Software Foundation, Inc. + Written by Marcus Brinkmann. + + This file is part of the GNU Hurd. + + The GNU Hurd 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; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#ifndef _VGA_DYNACOLOR_H_ +#define _VGA_DYNACOLOR_H_ 1 + +/* Every component can have up to 6 bits. */ +#define DYNACOLOR_COMPONENT_MAX 0x63 + +/* The components are, in that order, red, green, blue. */ +#define DYNACOLOR_COMPONENTS 3 + +/* The maximum number of colors. */ +#define DYNACOLOR_COLORS 8 + +struct dynacolor +{ + int ref[8]; + /* Reverse lookup, -1 denotes an unmapped color. */ + signed char col[16]; +}; + +typedef struct dynacolor dynacolor_t; + +/* We start with one reference for the black color so that it is + always available. It is also put in front so that it can be shared + as the border color. The importance of this is that for + non-standard font heights, we have some black-filled normal text + rows below the screen matrix, for which we must allocate a real + color slot :( */ +#define DYNACOLOR_INIT_8 { { 1, 0, 0, 0, 0, 0, 0, 0 }, \ + { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 } } +#define DYNACOLOR_INIT_16 { { -1 } } + +extern dynacolor_t dynacolor_init_8; +extern dynacolor_t dynacolor_init_16; + + +/* Initialize the dynacolor subsystem. */ +void dynacolor_init (void); + +/* Restore the original palette. */ +void dynacolor_fini (void); + +/* Activate the dynamic color palette DC. */ +void dynacolor_activate (dynacolor_t *dc); + +/* Try to allocate a slot for the color COL in the dynamic color + palette DC. Return the allocated slot number (with one reference) + or -1 if no slot is available. */ +signed char dynacolor_allocate (dynacolor_t *dc, unsigned char col); + +/* Get the slot number for color C in the dynamic color palette DC, or + -1 if we ran out of color slots. If successful, this allocates a + reference for the color. */ +#define dynacolor_lookup(dc,c) \ + ((dc).ref[0] < 0 ? (c) : \ + ((dc).col[(c)] >= 0 ? (dc).ref[(dc).col[(c)]]++, (dc).col[(c)] : \ + dynacolor_allocate (&(dc), (c)))) + +/* Add a reference to palette entry P in the dynamic font DC. */ +#define dynacolor_add_ref(dc,p) \ + ((dc).ref[0] >= 0 && (dc).ref[p]++) + +/* Deallocate a reference for palette entry P in the dynamic font DC. */ +#define dynacolor_release(dc,p) \ + ((dc).ref[0] >= 0 && (dc).ref[p]--) + +/* This is a convenience function that looks up a replacement color + pair if the original colors are not available. The function always + succeeds. DC is the dynacolor to use for allocation, FGCOL and + BGCOL are the desired colors, and R_FGCOL and R_BGCOL are the + resulting colors. The function assumes that the caller already + tried to look up the desired colors before trying to replace them, + and that the result of the lookup is contained in R_FGCOL and + R_BGCOL. + + Example: + + res_bgcol = dynacolor_lookup (&dc, bgcol); + res_fgcol = dynacolor_lookup (&dc, fgcol); + if (res_bgcol == -1 || res_fgcol == -1) + dynacolor_replace_colors (&dc, fgcol, bgcol, &res_fgcol, &res_bgcol); + + After the above code, res_fgcol and res_bgcol contain valid color + values. */ +void dynacolor_replace_colors (dynacolor_t *dc, + signed char fgcol, signed char bgcol, + signed char *r_fgcol, signed char *r_bgcol); + +#endif /* _VGA_DYNACOLOR_H_ */ diff --git a/console-client/vga-dynafont.c b/console-client/vga-dynafont.c new file mode 100644 index 00000000..0df28df1 --- /dev/null +++ b/console-client/vga-dynafont.c @@ -0,0 +1,1047 @@ +/* vga-dynafont.c - Dynamic font handling for VGA cards. + Copyright (C) 2002 Free Software Foundation, Inc. + Written by Marcus Brinkmann. + + This file is part of the GNU Hurd. + + The GNU Hurd 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; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#include +#include +#include +#include +#include +#include + +#include +#include "vga-hw.h" +#include "vga-support.h" +#include "bdf.h" +#include "vga-dynafont.h" +#include "unicode.h" + + +/* The currently active (visible) dynafont. The original idea was to + use some sort of caching for multiple dynafonts using the 8 font + buffers on the graphic card, but the new idea is to share a common + state among all virtual consoles and do a full refresh on + switching. However, bits and pieces of the old idea are still + present, in case they become useful. */ +static dynafont_t active_dynafont; + + +/* One glyph in a VGA font is 8 pixels wide and 32 pixels high. Only + the first N lines are visible, and N depends on the VGA register + settings. */ +typedef char vga_font_glyph[VGA_FONT_HEIGHT]; + + +/* For each glyph in the VGA font, one instance of this structure is + held in the dynafont. */ +struct mapped_character +{ + /* Reference count of the mapped character. If this drops zero, we + can remove the mapping at any time. */ + int refs; + + /* Remember the character this glyph belongs to, so the glyph can be + reloaded when the font is changed. This is actually a wchar_t + with some text attributes mixed into the high bits. */ +#define WCHAR_BOLD ((wchar_t) 0x40000000) +#define WCHAR_ITALIC ((wchar_t) 0x20000000) +#define WCHAR_MASK ((wchar_t) 0x001fffff) + wchar_t character; + + /* Used by libihash for fast removal of elements. */ + void **locp; +}; + + +struct dynafont +{ + /* The sorted font to take the glyphs from. */ + bdf_font_t font; + + /* The sorted font to take the italic glyphs from. */ + bdf_font_t font_italic; + + /* The sorted font to take the bold glyphs from. */ + bdf_font_t font_bold; + + /* The sorted font to take the bold and italic glyphs from. */ + bdf_font_t font_bold_italic; + + /* The size of the VGA font (256 or 512). Must be a power of 2! */ + int size; + + /* A hash containing a pointer to a struct mapped_character for each + UCS-4 character we map to a VGA font index. */ + ihash_t charmap; + + /* The struct mapped_characters are preallocated for all vga font + index values. This points to an array of SIZE such elements. */ + struct mapped_character *charmap_data; + + /* The last vga font index that had been free (or could be + reused). */ + int vga_font_last_free_index; + + /* The number of free slots in the VGA font. */ + int vga_font_free_indices; + + int use_lgc; + + /* The last vga font index that had been free (or could be reused) + for horizontal line graphic characters. */ + int vga_font_last_free_index_lgc; + + /* The number of free slots in the VGA font for horizontal line + graphic charactes. */ + int vga_font_free_indices_lgc; + + /* The font memory as stored on the card. */ + vga_font_glyph *vga_font; + + /* The cursor size, standout or normal. */ + int cursor_standout; +}; + + +/* The VGA standard font follows IBM code page 437. The following + table maps this to unicode. For simplicity, a 1:1 mapping is + assumed. Ambiguities are special cased in create_system_font. */ +static +wchar_t ibm437_to_unicode[VGA_FONT_SIZE] = { + 0, /* 0x00 */ + UNICODE_WHITE_SMILING_FACE, /* 0x01 */ + UNICODE_BLACK_SMILING_FACE, /* 0x02 */ + UNICODE_BLACK_HEART_SUIT, /* 0x03 */ + UNICODE_BLACK_DIAMOND_SUIT, /* 0x04 */ + UNICODE_BLACK_CLUB_SUIT, /* 0x05 */ + UNICODE_BLACK_SPADE_SUIT, /* 0x06 */ + UNICODE_BULLET, /* 0x07 */ + UNICODE_INVERSE_BULLET, /* 0x08 */ + UNICODE_WHITE_CIRCLE, /* 0x09 */ + UNICODE_INVERSE_WHITE_CIRCLE, /* 0x0a */ + UNICODE_MALE_SIGN, /* 0x0b */ + UNICODE_FEMALE_SIGN, /* 0x0c */ + UNICODE_EIGHTH_NOTE, /* 0x0d */ + UNICODE_BEAMED_EIGHTH_NOTES, /* 0x0e */ + UNICODE_WHITE_SUN_WITH_RAYS, /* 0x0f */ + UNICODE_BLACK_RIGHT_POINTING_TRIANGLE, /* 0x10 */ + UNICODE_BLACK_LEFT_POINTING_TRIANGLE, /* 0x11 */ + UNICODE_UP_DOWN_ARROW, /* 0x12 */ + UNICODE_DOUBLE_EXCLAMATION_MARK, /* 0x13 */ + UNICODE_PILCROW_SIGN, /* 0x14 */ + UNICODE_SECTION_SIGN, /* 0x15 */ + UNICODE_BLACK_RECTANGLE, /* 0x16 */ + UNICODE_UP_DOWN_ARROW_WITH_BASE, /* 0x17 */ + UNICODE_UPWARDS_ARROW, /* 0x18 */ + UNICODE_DOWNWARDS_ARROW, /* 0x19 */ + UNICODE_RIGHTWARDS_ARROW, /* 0x1a */ + UNICODE_LEFTWARDS_ARROW, /* 0x1b */ + UNICODE_RIGHT_ANGLE, /* 0x1c */ + UNICODE_LEFT_RIGHT_ARROW, /* 0x1d */ + UNICODE_BLACK_UP_POINTING_TRIANGLE, /* 0x1e */ + UNICODE_BLACK_DOWN_POINTING_TRIANGLE, /* 0x1f */ + ' ', '!', '"', '#', '$', '%', '&', '\'', /* 0x20 - 0x27 */ + '(', ')', '*', '+', ',', '-', '.', '/', /* 0x28 - 0x2f */ + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', /* 0x30 - 0x37 */ + ':', ';', '<', '=', '>', '?', /* 0x38 - 0x3f */ + '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', /* 0x40 - 0x47 */ + 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', /* 0x48 - 0x4f */ + 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', /* 0x50 - 0x57 */ + 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', /* 0x58 - 0x5f */ + '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', /* 0x60 - 0x67 */ + 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', /* 0x68 - 0x6f */ + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', /* 0x70 - 0x77 */ + 'x', 'y', 'z', '{', '|', '}', '~', UNICODE_HOUSE, /* 0x78 - 0x7f */ + UNICODE_LATIN_CAPITAL_LETTER_C_WITH_CEDILLA, /* 0x80 */ + UNICODE_LATIN_SMALL_LETTER_U_WITH_DIARESIS, /* 0x81 */ + UNICODE_LATIN_SMALL_LETTER_E_WITH_ACUTE, /* 0x82 */ + UNICODE_LATIN_SMALL_LETTER_A_WITH_CIRCUMFLEX, /* 0x83 */ + UNICODE_LATIN_SMALL_LETTER_A_WITH_DIARESIS, /* 0x84 */ + UNICODE_LATIN_SMALL_LETTER_A_WITH_GRAVE, /* 0x85 */ + UNICODE_LATIN_SMALL_LETTER_A_WITH_RING_ABOVE, /* 0x86 */ + UNICODE_LATIN_SMALL_LETTER_C_WITH_CEDILLA, /* 0x87 */ + UNICODE_LATIN_SMALL_LETTER_E_WITH_CIRCUMFLEX, /* 0x88 */ + UNICODE_LATIN_SMALL_LETTER_E_WITH_DIARESIS, /* 0x89 */ + UNICODE_LATIN_SMALL_LETTER_E_WITH_GRAVE, /* 0x8a */ + UNICODE_LATIN_SMALL_LETTER_I_WITH_DIARESIS, /* 0x8b */ + UNICODE_LATIN_SMALL_LETTER_I_WITH_CIRCUMFLEX, /* 0x8c */ + UNICODE_LATIN_SMALL_LETTER_I_WITH_GRAVE, /* 0x8d */ + UNICODE_LATIN_CAPITAL_LETTER_A_WITH_DIARESIS, /* 0x8e */ + UNICODE_LATIN_CAPITAL_LETTER_A_WITH_RING_ABOVE, /* 0x8f */ + UNICODE_LATIN_CAPITAL_LETTER_E_WITH_ACUTE, /* 0x90 */ + UNICODE_LATIN_SMALL_LETTER_AE, /* 0x91 */ + UNICODE_LATIN_CAPITAL_LETTER_AE, /* 0x92 */ + UNICODE_LATIN_SMALL_LETTER_O_WITH_CIRCUMFLEX, /* 0x93 */ + UNICODE_LATIN_SMALL_LETTER_O_WITH_DIARESIS, /* 0x94 */ + UNICODE_LATIN_SMALL_LETTER_O_WITH_GRAVE, /* 0x95 */ + UNICODE_LATIN_SMALL_LETTER_U_WITH_CIRCUMFLEX, /* 0x96 */ + UNICODE_LATIN_SMALL_LETTER_U_WITH_GRAVE, /* 0x97 */ + UNICODE_LATIN_SMALL_LETTER_Y_WITH_DIARESIS, /* 0x98 */ + UNICODE_LATIN_CAPITAL_LETTER_O_WITH_DIARESIS, /* 0x99 */ + UNICODE_LATIN_CAPITAL_LETTER_U_WITH_DIARESIS, /* 0x9a */ + UNICODE_CENT_SIGN, /* 0x9b */ + UNICODE_POUND_SIGN, /* 0x9c */ + UNICODE_YEN_SIGN, /* 0x9d */ + UNICODE_PESETA_SIGN, /* 0x9e */ + UNICODE_LATIN_SMALL_LETTER_F_WITH_HOOK, /* 0x9f */ + UNICODE_LATIN_SMALL_LETTER_A_WITH_ACUTE, /* 0xa0 */ + UNICODE_LATIN_SMALL_LETTER_I_WITH_ACUTE, /* 0xa1 */ + UNICODE_LATIN_SMALL_LETTER_O_WITH_ACUTE, /* 0xa2 */ + UNICODE_LATIN_SMALL_LETTER_U_WITH_ACUTE, /* 0xa3 */ + UNICODE_LATIN_SMALL_LETTER_N_WITH_TILDE, /* 0xa4 */ + UNICODE_LATIN_CAPITAL_LETTER_N_WITH_TILDE, /* 0xa5 */ + UNICODE_FEMININE_ORDINAL_INDICATOR, /* 0xa6 */ + UNICODE_MASCULINE_ORDINAL_INDICATOR, /* 0xa7 */ + UNICODE_INVERTED_QUESTION_MARK, /* 0xa8 */ + UNICODE_REVERSED_NOT_SIGN, /* 0xa9 */ + UNICODE_NOT_SIGN, /* 0xaa */ + UNICODE_VULGAR_FRACTION_ONE_HALF, /* 0xab */ + UNICODE_VULGAR_FRACTION_ONE_QUARTER, /* 0xac */ + UNICODE_INVERTED_EXCLAMATION_MARK, /* 0xad */ + UNICODE_LEFT_POINTING_DOUBLE_ANGLE_QUOTATION_MARK, /* 0xae */ + UNICODE_RIGHT_POINTING_DOUBLE_ANGLE_QUOTATION_MARK, /* 0xaf */ + UNICODE_LIGHT_SHADE, /* 0xb0 */ + UNICODE_MEDIUM_SHADE, /* 0xb1 */ + UNICODE_DARK_SHADE, /* 0xb2 */ + UNICODE_BOX_DRAWINGS_LIGHT_VERTICAL, /* 0xb3 */ + UNICODE_BOX_DRAWINGS_LIGHT_VERTICAL_AND_LEFT, /* 0xb4 */ + UNICODE_BOX_DRAWINGS_VERTICAL_SINGLE_AND_LEFT_DOUBLE, /* 0xb5 */ + UNICODE_BOX_DRAWINGS_VERTICAL_DOUBLE_AND_LEFT_SINGLE, /* 0xb6 */ + UNICODE_BOX_DRAWINGS_DOWN_DOUBLE_AND_LEFT_SINGLE, /* 0xb7 */ + UNICODE_BOX_DRAWINGS_DOWN_SINGLE_AND_LEFT_DOUBLE, /* 0xb8 */ + UNICODE_BOX_DRAWINGS_DOUBLE_VERTICAL_AND_LEFT, /* 0xb9 */ + UNICODE_BOX_DRAWINGS_DOUBLE_VERTICAL, /* 0xba */ + UNICODE_BOX_DRAWINGS_DOUBLE_DOWN_AND_LEFT, /* 0xbb */ + UNICODE_BOX_DRAWINGS_DOUBLE_UP_AND_LEFT, /* 0xbc */ + UNICODE_BOX_DRAWINGS_UP_DOUBLE_AND_LEFT_SINGLE, /* 0xbd */ + UNICODE_BOX_DRAWINGS_UP_SINGLE_AND_LEFT_DOUBLE, /* 0xbe */ + UNICODE_BOX_DRAWINGS_LIGHT_DOWN_AND_LEFT, /* 0xbf */ + UNICODE_BOX_DRAWINGS_LIGHT_UP_AND_RIGHT, /* 0xc0 */ + UNICODE_BOX_DRAWINGS_LIGHT_UP_AND_HORIZONTAL, /* 0xc1 */ + UNICODE_BOX_DRAWINGS_LIGHT_DOWN_AND_HORIZONTAL, /* 0xc2 */ + UNICODE_BOX_DRAWINGS_LIGHT_VERTICAL_AND_RIGHT, /* 0xc3 */ + UNICODE_BOX_DRAWINGS_LIGHT_HORIZONTAL, /* 0xc4 */ + UNICODE_BOX_DRAWINGS_LIGHT_VERTICAL_AND_HORIZONTAL, /* 0xc5 */ + UNICODE_BOX_DRAWINGS_VERTICAL_SINGLE_AND_RIGHT_DOUBLE, /* 0xc6 */ + UNICODE_BOX_DRAWINGS_VERTICAL_DOUBLE_AND_RIGHT_SINGLE, /* 0xc7 */ + UNICODE_BOX_DRAWINGS_DOUBLE_UP_AND_RIGHT, /* 0xc8 */ + UNICODE_BOX_DRAWINGS_DOUBLE_DOWN_AND_RIGHT, /* 0xc9 */ + UNICODE_BOX_DRAWINGS_DOUBLE_UP_AND_HORIZONTAL, /* 0xca */ + UNICODE_BOX_DRAWINGS_DOUBLE_DOWN_AND_HORIZONTAL, /* 0xcb */ + UNICODE_BOX_DRAWINGS_DOUBLE_VERTICAL_AND_RIGHT, /* 0xcc */ + UNICODE_BOX_DRAWINGS_DOUBLE_HORIZONTAL, /* 0xcd */ + UNICODE_BOX_DRAWINGS_DOUBLE_VERTICAL_AND_HORIZONTAL,/* 0xce */ + UNICODE_BOX_DRAWINGS_UP_SINGLE_AND_HORIZONTAL_DOUBLE, /* 0xcf */ + UNICODE_BOX_DRAWINGS_UP_DOUBLE_AND_HORIZONTAL_SINGLE, /* 0xd0 */ + UNICODE_BOX_DRAWINGS_DOWN_SINGLE_AND_HORIZONTAL_DOUBLE, /* 0xd1 */ + UNICODE_BOX_DRAWINGS_DOWN_DOUBLE_AND_HORIZONTAL_SINGLE, /* 0xd2 */ + UNICODE_BOX_DRAWINGS_UP_DOUBLE_AND_RIGHT_SINGLE, /* 0xd3 */ + UNICODE_BOX_DRAWINGS_UP_SINGLE_AND_RIGHT_DOUBLE, /* 0xd4 */ + UNICODE_BOX_DRAWINGS_DOWN_SINGLE_AND_RIGHT_DOUBLE, /* 0xd5 */ + UNICODE_BOX_DRAWINGS_DOWN_DOUBLE_AND_RIGHT_SINGLE, /* 0xd6 */ + UNICODE_BOX_DRAWINGS_VERTICAL_DOUBLE_AND_HORIZONTAL_SINGLE, /* 0xd7 */ + UNICODE_BOX_DRAWINGS_VERTICAL_SINGLE_AND_HORIZONTAL_DOUBLE, /* 0xd8 */ + UNICODE_BOX_DRAWINGS_LIGHT_UP_AND_LEFT, /* 0xd9 */ + UNICODE_BOX_DRAWINGS_LIGHT_DOWN_AND_RIGHT, /* 0xda */ + UNICODE_FULL_BLOCK, /* 0xdb */ + UNICODE_LOWER_HALF_BLOCK, /* 0xdc */ + UNICODE_LEFT_HALF_BLOCK, /* 0xdd */ + UNICODE_RIGHT_HALF_BLOCK, /* 0xde */ + UNICODE_UPPER_HALF_BLOCK, /* 0xdf */ + UNICODE_GREEK_SMALL_LETTER_ALPHA, /* 0xe0 */ + /* The next one: Also sz-ligature. */ + UNICODE_GREEK_SMALL_LETTER_BETA, /* 0xe1 */ + UNICODE_GREEK_CAPITAL_LETTER_GAMMA, /* 0xe2 */ + UNICODE_GREEK_SMALL_LETTER_PI, /* 0xe3 */ + UNICODE_GREEK_CAPITAL_LETTER_SIGMA, /* 0xe4 */ + UNICODE_GREEK_SMALL_LETTER_SIGMA, /* 0xe5 */ + /* The next one: Also micro. */ + UNICODE_GREEK_SMALL_LETTER_MU, /* 0xe6 */ + UNICODE_GREEK_SMALL_LETTER_TAU, /* 0xe7 */ + UNICODE_GREEK_CAPITAL_LETTER_PHI, /* 0xe8 */ + UNICODE_GREEK_CAPITAL_LETTER_OMICRON, /* 0xe9 */ + UNICODE_GREEK_CAPITAL_LETTER_OMEGA, /* 0xea */ + UNICODE_GREEK_SMALL_LETTER_DELTA, /* 0xeb */ + UNICODE_INFINITY, /* 0xec */ + UNICODE_GREEK_SMALL_LETTER_PHI, /* 0xed */ + UNICODE_GREEK_SMALL_LETTER_EPSILON, /* 0xee */ + UNICODE_INTERSECTION, /* 0xef */ + UNICODE_IDENTICAL_TO, /* 0xf0 */ + UNICODE_PLUS_MINUS_SIGN, /* 0xf1 */ + UNICODE_GREATER_THAN_OR_EQUAL_TO, /* 0xf2 */ + UNICODE_LESS_THAN_OR_EQUAL_TO, /* 0xf3 */ + UNICODE_TOP_HALF_INTEGRAL, /* 0xf4 */ + UNICODE_BOTTOM_HALF_INTEGRAL, /* 0xf5 */ + UNICODE_DIVISION_SIGN, /* 0xf6 */ + UNICODE_ALMOST_EQUAL_TO, /* 0xf7 */ + UNICODE_DEGREE_SIGN, /* 0xf8 */ + UNICODE_BULLET_OPERATOR, /* 0xf9 */ + UNICODE_MIDDLE_DOT, /* 0xfa */ + UNICODE_SQUARE_ROOT, /* 0xfb */ + UNICODE_SUPERSCRIPT_LATIN_SMALL_LETTER, /* 0xfc */ + UNICODE_SUPERSCRIPT_TWO, /* 0xfd */ + UNICODE_BLACK_SQUARE, /* 0xfe */ + UNICODE_NO_BREAK_SPACE /* 0xff */ + }; + +/* Read the VGA card's memory as IBM 437 font and create a Unicode BDF + font from it. If an error occurs, errno is set and NULL is + returned. */ +static bdf_font_t +create_system_font (void) +{ + bdf_error_t bdferr; + bdf_font_t font; + unsigned char bitmap[VGA_FONT_SIZE][VGA_FONT_HEIGHT]; /* 8kB on stack. */ + int width = 9; /* XXX Find out if we are in 8 or 9 pixel mode. */ + int i; + + /* Add the glyph at position POS to the font for character + ENCODING. */ + void vga_add_glyph (int pos, int encoding) + { + char name[16]; + snprintf (name, sizeof (name), "VGA %i", pos); + + if (width == 8) + bdferr = bdf_add_glyph (font, name, encoding, + 0, 8, 16, 0, 0, bitmap[pos]); + else + { + int i; + char glyph_bitmap[32]; + + for (i = 0; i < 16; i++) + { + glyph_bitmap[i * 2] = bitmap[pos][i]; + if (pos >= VGA_FONT_LGC_BEGIN + && pos < VGA_FONT_LGC_BEGIN + VGA_FONT_LGC_COUNT) + glyph_bitmap[i * 2 + 1] + = (bitmap[pos][i] & 1) ? 0x80 : 0; + else + glyph_bitmap[i * 2 + 1] = 0; + } + bdferr = bdf_add_glyph (font, name, encoding, + 0, 9, 16, 0, 0, glyph_bitmap); + } + } + + /* The point size and resolution is arbitrary. */ + bdferr = bdf_new (&font, 2, 2, "vga-system", 10, 100, 100, + width, 16, 0, 0, 0); + if (bdferr) + { + if (bdferr != BDF_SYSTEM_ERROR) + errno = EGRATUITOUS; + return NULL; + } + + vga_read_font_buffer (0, 0, (unsigned char *) bitmap, + VGA_FONT_SIZE * VGA_FONT_HEIGHT); + + for (i = 0; i < VGA_FONT_SIZE; i++) + if (ibm437_to_unicode[i]) + { + vga_add_glyph (i, ibm437_to_unicode[i]); + if (bdferr) + break; + + /* Some glyphs are ambivalent. */ + if (ibm437_to_unicode[i] == UNICODE_GREEK_SMALL_LETTER_BETA) + vga_add_glyph (i, UNICODE_LATIN_SMALL_LETTER_SHARP_S); + else if (ibm437_to_unicode[i] == UNICODE_GREEK_SMALL_LETTER_MU) + vga_add_glyph (i, UNICODE_MICRO_SIGN); + if (bdferr) + break; + } + + if (bdferr) + { + bdf_destroy (font); + if (bdferr != BDF_SYSTEM_ERROR) + errno = EGRATUITOUS; + return NULL; + } + + return font; +} + + +#if QUAERENDO_INVENIETIS +#define GNU_HEAD_BEGIN (UNICODE_PRIVATE_USE_AREA + 0x0f00) + +static void +add_gnu_head (bdf_font_t font) +{ +#define GNU_HEAD_WIDTH 6 + static unsigned char gnu_head[][GNU_HEAD_WIDTH] = + { + { 255, 255, 255, 255, 255, 255 }, { 255, 0, 127, 255, 252, 31 }, + { 252, 0, 31, 255, 224, 7 }, { 248, 0, 7, 255, 0, 3 }, + { 240, 0, 15, 255, 128, 3 }, { 240, 31, 255, 255, 252, 1 }, + { 224, 63, 241, 255, 255, 1 }, { 192, 127, 128, 96, 255, 129 }, + { 192, 255, 0, 0, 63, 193 }, { 192, 254, 0, 0, 31, 193 }, + { 192, 252, 0, 0, 15, 193 }, { 192, 248, 0, 0, 15, 193 }, + { 192, 248, 0, 0, 7, 129 }, { 192, 96, 63, 131, 192, 1 }, + { 192, 1, 227, 98, 112, 1 }, { 192, 3, 195, 244, 176, 3 }, + { 224, 7, 221, 125, 248, 3 }, { 240, 15, 184, 124, 120, 7 }, + { 240, 15, 248, 124, 60, 15 }, { 248, 15, 220, 254, 124, 127 }, + { 252, 31, 223, 255, 254, 127 }, { 255, 159, 159, 255, 31, 191 }, + { 255, 223, 159, 255, 35, 63 }, { 255, 191, 127, 195, 152, 127 }, + { 255, 188, 255, 156, 199, 255 }, { 255, 96, 253, 134, 115, 255 }, + { 254, 134, 251, 227, 251, 255 }, { 254, 46, 254, 249, 251, 255 }, + { 255, 238, 126, 127, 231, 255 }, { 255, 239, 127, 127, 207, 255 }, + { 255, 239, 63, 63, 231, 255 }, { 255, 247, 159, 158, 15, 255 }, + { 255, 247, 207, 193, 159, 255 }, { 255, 247, 223, 255, 223, 255 }, + { 255, 243, 199, 252, 31, 255 }, { 255, 251, 227, 224, 63, 255 }, + { 255, 253, 241, 240, 255, 255 }, { 255, 252, 244, 126, 255, 255 }, + { 255, 254, 121, 122, 255, 255 }, { 255, 255, 252, 48, 255, 255 }, + { 255, 255, 252, 35, 255, 255 }, { 255, 255, 249, 1, 127, 255 }, + { 255, 255, 251, 0, 127, 255 }, { 255, 255, 255, 128, 255, 255 }, + { 255, 255, 255, 255, 255, 255 } + }; + /* Height of a single glyph, truncated to fit VGA glyph slot. */ + int height = (font->bbox.height > 32) ? 32 : font->bbox.height; + /* Width of a single glyph in byte. */ + int width = (font->bbox.width + 7) / 8; + /* Number of rows in bitmap. */ + int rows = sizeof (gnu_head) / sizeof (gnu_head[0]); + /* Number of rows of glyphs necessary. */ + int nr = (rows + height - 1) / height; + int row, col; + + /* Check that all those glyphs are free in the private area. */ + if (nr * GNU_HEAD_WIDTH > GNU_HEAD_BEGIN - UNICODE_PRIVATE_USE_AREA + 1) + return; + for (int i = 0; i < nr * GNU_HEAD_WIDTH; i++) + if (bdf_find_glyph (font, (int) GNU_HEAD_BEGIN + i, 0) + || bdf_find_glyph (font, -1, (int) GNU_HEAD_BEGIN + i)) + return; + + for (row = 0; row < nr; row++) + for (col = 0; col < GNU_HEAD_WIDTH; col++) + { + char bitmap[font->bbox.height][width]; + char name[] = "GNU Head .........."; + + /* strlen ("GNU Head ") == 9. */ + sprintf (&name[9], "%i", row * GNU_HEAD_WIDTH + col); + + memset (bitmap, 0, sizeof (bitmap)); + for (int j = 0; j < height && row * height + j < rows; j++) + bitmap[j][0] = gnu_head[row * height + j][col]; + + if (bdf_add_glyph (font, name, + GNU_HEAD_BEGIN + row * GNU_HEAD_WIDTH + col, + 0, font->bbox.width, font->bbox.height, + 0, 0, (unsigned char *) bitmap)) + return; + } +} +#endif + +/* Create a new dynafont object, which uses glyphs from the font FONT + (which should be 8 or 9 pixels wide and 13 to 16 pixels high). + SIZE is either 256 or 512, and specifies the number of available + glyphs in the font cache. The object is returned in DYNAFONT. + + The font is consumed. If FONT is the null pointer, the VGA card's + memory is converted to a font according to the VGA default map (IBM + Code Page 437). */ +error_t +dynafont_new (bdf_font_t font, bdf_font_t font_italic, bdf_font_t font_bold, + bdf_font_t font_bold_italic, int size, dynafont_t *dynafont) +{ + error_t err = 0; + dynafont_t df; + struct bdf_glyph *glyph = NULL; + + if (!font) + font = create_system_font (); + if (!font || !font->bbox.height) + return errno; + + df = malloc (sizeof *df); + if (!df) + return ENOMEM; + +#if QUAERENDO_INVENIETIS + add_gnu_head (font); +#endif + bdf_sort_glyphs (font); + df->font = font; + df->font_italic = font_italic; + df->font_bold = font_bold; + df->font_bold_italic = font_bold_italic; + df->size = size; + df->cursor_standout = 0; + err = ihash_create (&df->charmap); + if (err) + { + free (df); + return err; + } + + df->charmap_data = calloc (size, sizeof (struct mapped_character)); + if (!df->charmap_data) + { + ihash_free (df->charmap); + free (df); + return ENOMEM; + } + + df->vga_font = malloc (sizeof (vga_font_glyph) * size); + if (!df->vga_font) + { + ihash_free (df->charmap); + free (df->charmap_data); + free (df); + return ENOMEM; + } + if (df->font->bbox.width == 9) + { + /* 32 from 256 font slots are for horizontal line graphic + characters. */ + df->use_lgc = 1; + df->vga_font_free_indices = df->size + - (df->size / 256) * VGA_FONT_LGC_COUNT; + df->vga_font_last_free_index = 0; + df->vga_font_free_indices_lgc = (df->size / 256) * VGA_FONT_LGC_COUNT; + df->vga_font_last_free_index_lgc = VGA_FONT_LGC_BEGIN; + } + else + { + df->use_lgc = 0; + df->vga_font_free_indices = df->size; + df->vga_font_last_free_index = 0; + df->vga_font_free_indices_lgc = 0; + df->vga_font_last_free_index_lgc = 0; + } + + /* Ensure that we always have the replacement character + available. */ + { + struct mapped_character *chr = &df->charmap_data[FONT_INDEX_UNKNOWN]; + df->vga_font_free_indices--; + chr->refs = 1; + + glyph = bdf_find_glyph (df->font, UNICODE_REPLACEMENT_CHARACTER, 0); + if (!glyph) + glyph = bdf_find_glyph (df->font, -1, UNICODE_REPLACEMENT_CHARACTER); + if (glyph) + { + /* XXX Take glyph size into account. */ + for (int i = 0; i < ((df->font->bbox.height > 32) + ? 32 : df->font->bbox.height); i++) + df->vga_font[FONT_INDEX_UNKNOWN][i] + = glyph->bitmap[i * ((df->font->bbox.width + 7) / 8)]; + if (df->font->bbox.height < 32) + memset (((char *) df->vga_font[FONT_INDEX_UNKNOWN]) + + df->font->bbox.height, 0, 32 - df->font->bbox.height); + + /* Update the hash table. */ + ihash_add (df->charmap, UNICODE_REPLACEMENT_CHARACTER, + chr, &chr->locp); + } + else + { + int i; + char *gl = df->vga_font[FONT_INDEX_UNKNOWN]; + /* XXX Take font height into account. */ + gl[0] = 0x7E; /* ****** */ + gl[1] = 0xC3; /* ** ** */ + gl[2] = 0x99; /* * ** * */ + gl[3] = 0x99; /* * ** * */ + gl[4] = 0xF9; /* ***** * */ + gl[5] = 0xF3; /* **** ** */ + gl[6] = 0xF3; /* *** *** */ + gl[7] = 0xE7; /* *** *** */ + gl[8] = 0xFF; /* ******** */ + gl[9] = 0xE7; /* *** *** */ + gl[10] = 0xE7; /* *** *** */ + gl[11] = 0x7E; /* ****** */ + for (i = 12; i < 32; i++) + gl[i] = 0; + } + } + + *dynafont = df; + return err; +} + + +/* Release a dynafont object and its associated resources. */ +void +dynafont_free (dynafont_t df) +{ + if (active_dynafont == df) + active_dynafont = NULL; + + bdf_destroy (df->font); + if (df->font_italic) + bdf_destroy (df->font_italic); + if (df->font_bold) + bdf_destroy (df->font_bold); + if (df->font_bold_italic) + bdf_destroy (df->font_bold_italic); + ihash_free (df->charmap); + free (df->charmap_data); + free (df->vga_font); + free (df); +} + + +/* Determine if CHR is a character that needs to be continuous in the + horizontal direction by repeating the last (eighth) column in + 9-pixel-width mode. */ +static inline int +is_lgc (wchar_t chr) +{ + /* This list must be sorted for bsearch! */ + static wchar_t horiz_glyphs[] = + { + UNICODE_BOX_DRAWINGS_LIGHT_HORIZONTAL, /* 0x2500 */ + UNICODE_BOX_DRAWINGS_HEAVY_HORIZONTAL, /* 0x2501 */ + UNICODE_BOX_DRAWINGS_LIGHT_DOWN_AND_RIGHT, /* 0x250c */ + UNICODE_BOX_DRAWINGS_DOWN_LIGHT_AND_RIGHT_HEAVY, /* 0x250d */ + UNICODE_BOX_DRAWINGS_DOWN_HEAVY_AND_RIGHT_LIGHT, /* 0x250e */ + UNICODE_BOX_DRAWINGS_HEAVY_DOWN_AND_RIGHT, /* 0x250f */ + UNICODE_BOX_DRAWINGS_LIGHT_UP_AND_RIGHT, /* 0x2514 */ + UNICODE_BOX_DRAWINGS_UP_LIGHT_AND_RIGHT_HEAVY, /* 0x2515 */ + UNICODE_BOX_DRAWINGS_UP_HEAVY_AND_RIGHT_LIGHT, /* 0x2516 */ + UNICODE_BOX_DRAWINGS_HEAVY_UP_AND_RIGHT, /* 0x2517 */ + UNICODE_BOX_DRAWINGS_LIGHT_VERTICAL_AND_RIGHT, /* 0x251c */ + UNICODE_BOX_DRAWINGS_VERTICAL_LIGHT_AND_RIGHT_HEAVY, /* 0x251d */ + UNICODE_BOX_DRAWINGS_UP_HEAVY_AND_RIGHT_UP_LIGHT, /* 0x251e */ + UNICODE_BOX_DRAWINGS_DOWN_HEAVY_AND_RIGHT_UP_LIGHT, /* 0x251f */ + UNICODE_BOX_DRAWINGS_VERTICAL_HEAVY_AND_RIGHT_LIGHT, /* 0x2520 */ + UNICODE_BOX_DRAWINGS_DOWN_LIGHT_AND_RIGHT_UP_HEAVY, /* 0x2521 */ + UNICODE_BOX_DRAWINGS_UP_LIGHT_AND_RIGHT_DOWN_HEAVY, /* 0x2522 */ + UNICODE_BOX_DRAWINGS_HEAVY_VERTICAL_AND_RIGHT, /* 0x2523 */ + UNICODE_BOX_DRAWINGS_LIGHT_DOWN_AND_HORIZONTAL, /* 0x252c */ + UNICODE_BOX_DRAWINGS_LEFT_HEAVY_AND_RIGHT_DOWN_LIGHT, /* 0x252d */ + UNICODE_BOX_DRAWINGS_RIGHT_HEAVY_AND_LEFT_DOWN_LIGHT, /* 0x252e */ + UNICODE_BOX_DRAWINGS_DOWN_LIGHT_AND_HORIZONTAL_HEAVY, /* 0x252f */ + UNICODE_BOX_DRAWINGS_DOWN_HEAVY_AND_HORIZONTAL_LIGHT, /* 0x2530 */ + UNICODE_BOX_DRAWINGS_RIGHT_LIGHT_AND_LEFT_DOWN_HEAVY, /* 0x2531 */ + UNICODE_BOX_DRAWINGS_LEFT_LIGHT_AND_RIGHT_DOWN_HEAVY, /* 0x2532 */ + UNICODE_BOX_DRAWINGS_HEAVY_DOWN_AND_HORIZONTAL, /* 0x2533 */ + UNICODE_BOX_DRAWINGS_LIGHT_UP_AND_HORIZONTAL, /* 0x2534 */ + UNICODE_BOX_DRAWINGS_LEFT_HEAVY_AND_RIGHT_UP_LIGHT, /* 0x2535 */ + UNICODE_BOX_DRAWINGS_RIGHT_HEAVY_AND_LEFT_UP_LIGHT, /* 0x2536 */ + UNICODE_BOX_DRAWINGS_UP_LIGHT_AND_HORIZONTAL_HEAVY, /* 0x2537 */ + UNICODE_BOX_DRAWINGS_UP_HEAVY_AND_HORIZONTAL_LIGHT, /* 0x2538 */ + UNICODE_BOX_DRAWINGS_RIGHT_LIGHT_AND_LEFT_UP_HEAVY, /* 0x2539 */ + UNICODE_BOX_DRAWINGS_LEFT_LIGHT_AND_RIGHT_UP_HEAVY, /* 0x253a */ + UNICODE_BOX_DRAWINGS_HEAVY_UP_AND_HORIZONTAL, /* 0x253b */ + UNICODE_BOX_DRAWINGS_LIGHT_VERTICAL_AND_HORIZONTAL, /* 0x253c */ + UNICODE_BOX_DRAWINGS_LEFT_HEAVY_AND_RIGHT_VERTICAL_LIGHT, /* 0x253d */ + UNICODE_BOX_DRAWINGS_RIGHT_HEAVY_AND_LEFT_VERTICAL_LIGHT, /* 0x253e */ + UNICODE_BOX_DRAWINGS_VERTICAL_LIGHT_AND_HORIZONTAL_HEAVY, /* 0x253f */ + UNICODE_BOX_DRAWINGS_UP_HEAVY_AND_DOWN_HORIZONTAL_LIGHT, /* 0x2540 */ + UNICODE_BOX_DRAWINGS_DOWN_HEAVY_AND_UP_HORIZONTAL_LIGHT, /* 0x2541 */ + UNICODE_BOX_DRAWINGS_VERTICAL_HEAVY_AND_HORIZONTAL_LIGHT, /* 0x2542 */ + UNICODE_BOX_DRAWINGS_LEFT_UP_HEAVY_AND_RIGHT_DOWN_LIGHT, /* 0x2543 */ + UNICODE_BOX_DRAWINGS_RIGHT_UP_HEAVY_AND_LEFT_DOWN_LIGHT, /* 0x2544 */ + UNICODE_BOX_DRAWINGS_LEFT_DOWN_HEAVY_AND_RIGHT_UP_LIGHT, /* 0x2545 */ + UNICODE_BOX_DRAWINGS_RIGHT_DOWN_HEAVY_AND_LEFT_UP_LIGHT, /* 0x2546 */ + UNICODE_BOX_DRAWINGS_DOWN_LIGHT_AND_UP_HORIZONTAL_HEAVY, /* 0x2547 */ + UNICODE_BOX_DRAWINGS_UP_LIGHT_AND_DOWN_HORIZONTAL_HEAVY, /* 0x2548 */ + UNICODE_BOX_DRAWINGS_RIGHT_LIGHT_AND_LEFT_VERTICAL_HEAVY, /* 0x2549 */ + UNICODE_BOX_DRAWINGS_LEFT_LIGHT_AND_RIGHT_VERTICAL_HEAVY, /* 0x254a */ + UNICODE_BOX_DRAWINGS_HEAVY_VERTICAL_AND_HORIZONTAL, /* 0x254b */ + UNICODE_BOX_DRAWINGS_DOUBLE_HORIZONTAL, /* 0x2550 */ + UNICODE_BOX_DRAWINGS_DOWN_SINGLE_AND_RIGHT_DOUBLE, /* 0x2552 */ + UNICODE_BOX_DRAWINGS_DOWN_DOUBLE_AND_RIGHT_SINGLE, /* 0x2553 */ + UNICODE_BOX_DRAWINGS_DOUBLE_DOWN_AND_RIGHT, /* 0x2554 */ + UNICODE_BOX_DRAWINGS_UP_SINGLE_AND_RIGHT_DOUBLE, /* 0x2558 */ + UNICODE_BOX_DRAWINGS_UP_DOUBLE_AND_RIGHT_SINGLE, /* 0x2559 */ + UNICODE_BOX_DRAWINGS_DOUBLE_UP_AND_RIGHT, /* 0x255a */ + UNICODE_BOX_DRAWINGS_VERTICAL_SINGLE_AND_RIGHT_DOUBLE, /* 0x255e */ + UNICODE_BOX_DRAWINGS_VERTICAL_DOUBLE_AND_RIGHT_SINGLE, /* 0x255f */ + UNICODE_BOX_DRAWINGS_DOUBLE_VERTICAL_AND_RIGHT, /* 0x2560 */ + UNICODE_BOX_DRAWINGS_DOWN_SINGLE_AND_HORIZONTAL_DOUBLE, /* 0x2564 */ + UNICODE_BOX_DRAWINGS_DOWN_DOUBLE_AND_HORIZONTAL_SINGLE, /* 0x2565 */ + UNICODE_BOX_DRAWINGS_DOUBLE_DOWN_AND_HORIZONTAL, /* 0x2566 */ + UNICODE_BOX_DRAWINGS_UP_SINGLE_AND_HORIZONTAL_DOUBLE, /* 0x2567 */ + UNICODE_BOX_DRAWINGS_UP_DOUBLE_AND_HORIZONTAL_SINGLE, /* 0x2568 */ + UNICODE_BOX_DRAWINGS_DOUBLE_UP_AND_HORIZONTAL, /* 0x2569 */ + UNICODE_BOX_DRAWINGS_VERTICAL_SINGLE_AND_HORIZONTAL_DOUBLE, /* 0x256a */ + UNICODE_BOX_DRAWINGS_VERTICAL_DOUBLE_AND_HORIZONTAL_SINGLE, /* 0x256b */ + UNICODE_BOX_DRAWINGS_DOUBLE_VERTICAL_AND_HORIZONTAL, /* 0x256c */ + UNICODE_BOX_DRAWINGS_LIGHT_ARC_DOWN_AND_RIGHT, /* 0x256d */ + UNICODE_BOX_DRAWINGS_LIGHT_ARC_UP_AND_RIGHT, /* 0x2570 */ +#if 0 + /* The diagonal lines don't look much better with or without the + ninth column. */ + /* 0x2571 */ + UNICODE_BOX_DRAWINGS_LIGHT_DIAGONAL_UPPER_RIGHT_TO_LOWER_LEFT, + /* 0x2572 */ + UNICODE_BOX_DRAWINGS_LIGHT_DIAGONAL_UPPER_LEFT_TO_LOWER_RIGHT, + UNICODE_BOX_DRAWINGS_LIGHT_DIAGONAL_CROSS, /* 0x2573 */ +#endif + UNICODE_BOX_DRAWINGS_LIGHT_RIGHT, /* 0x2576 */ + UNICODE_BOX_DRAWINGS_HEAVY_RIGHT, /* 0x257a */ + UNICODE_BOX_DRAWINGS_LIGHT_LEFT_AND_HEAVY_RIGHT, /* 0x257c */ + UNICODE_BOX_DRAWINGS_HEAVY_LEFT_AND_LIGHT_RIGHT, /* 0x257e */ + UNICODE_UPPER_HALF_BLOCK, /* 0x2580 */ + UNICODE_LOWER_ONE_EIGHTH_BLOCK, /* 0x2581 */ + UNICODE_LOWER_ONE_QUARTER_BLOCK, /* 0x2582 */ + UNICODE_LOWER_THREE_EIGHTHS_BLOCK, /* 0x2583 */ + UNICODE_LOWER_HALF_BLOCK, /* 0x2584 */ + UNICODE_LOWER_FIVE_EIGHTHS_BLOCK, /* 0x2585 */ + UNICODE_LOWER_THREE_QUARTERS_BLOCK, /* 0x2586 */ + UNICODE_LOWER_SEVEN_EIGHTHS_BLOCK, /* 0x2587 */ + UNICODE_FULL_BLOCK, /* 0x2588 */ + UNICODE_RIGHT_HALF_BLOCK, /* 0x2590 */ +#if 0 + /* The shades don't look much better with or without the ninth + column. */ + UNICODE_LIGHT_SHADE, /* 0x2591 */ + UNICODE_MEDIUM_SHADE, /* 0x2592 */ + UNICODE_DARK_SHADE, /* 0x2593 */ +#endif + UNICODE_BLACK_SQUARE, /* 0x25a0 */ + UNICODE_UPPER_ONE_EIGHTH_BLOCK, /* 0x2594 */ + UNICODE_RIGHT_ONE_EIGHTH_BLOCK, /* 0x2595 */ + UNICODE_QUADRANT_LOWER_RIGHT, /* 0x2597 */ + UNICODE_QUADRANT_UPPER_LEFT_AND_LOWER_LEFT_AND_LOWER_RIGHT, /* 0x2599 */ + UNICODE_QUADRANT_UPPER_LEFT_AND_LOWER_RIGHT, /* 0x259a */ + UNICODE_QUADRANT_UPPER_LEFT_AND_UPPER_RIGHT_AND_LOWER_LEFT, /* 0x259b */ + UNICODE_QUADRANT_UPPER_LEFT_AND_UPPER_RIGHT_AND_LOWER_RIGHT, /* 0x259c */ + UNICODE_QUADRANT_UPPER_RIGHT, /* 0x259d */ + UNICODE_QUADRANT_UPPER_RIGHT_AND_LOWER_LEFT, /* 0x259e */ + UNICODE_QUADRANT_UPPER_RIGHT_AND_LOWER_LEFT_AND_LOWER_RIGHT, /* 0x259f */ + }; + + int cmp_wchar (const void *a, const void *b) + { + const wchar_t *wa = (const wchar_t *) a; + const wchar_t *wb = (const wchar_t *) b; + return (*wa > *wb) - (*wa < *wb); + } + +#if QUAERENDO_INVENIETIS + /* XXX The (maximum) size is hard coded. */ + if (chr >= GNU_HEAD_BEGIN && chr <= GNU_HEAD_BEGIN + 50) + return 1; +#endif + + return bsearch (&chr, horiz_glyphs, + sizeof (horiz_glyphs) / sizeof (horiz_glyphs[0]), + sizeof (horiz_glyphs[0]), cmp_wchar) ? 1 : 0; +} + + +/* Try to look up glyph CHR in font FONT of dynafont DF with attribute + ATTR. This is a helper function for dynafont_lookup. Returns 1 on + success, 0 otherwise. */ +static int +dynafont_lookup_internal (dynafont_t df, bdf_font_t font, + wchar_t wide_chr, wchar_t attr, int *rpos) +{ + /* When hashing the character, we mix in the font attribute. */ + struct mapped_character *chr = ihash_find (df->charmap, + (int) (wide_chr | attr)); + int lgc; + struct bdf_glyph *glyph; + int pos; + int found = 0; + + lgc = df->use_lgc ? is_lgc (wide_chr) : 0; + + if (chr) + { + if (!chr->refs++) + { + if (lgc) + df->vga_font_free_indices_lgc--; + else + df->vga_font_free_indices--; + } + /* Return the index of CHR. */ + *rpos = chr - df->charmap_data; + return 1; + } + + /* The character is not currently mapped. Look for an empty slot + and add it. */ + if ((lgc && !df->vga_font_free_indices_lgc) + || (!lgc && !df->vga_font_free_indices)) + return 0; + + glyph = bdf_find_glyph (font, (int) wide_chr, 0); + if (!glyph) + glyph = bdf_find_glyph (font, -1, (int) wide_chr); + if (!glyph) + return 0; + + if (lgc) + { + int start_pos = df->vga_font_last_free_index_lgc + 1; + if ((start_pos % VGA_FONT_SIZE) + == VGA_FONT_LGC_BEGIN + VGA_FONT_LGC_COUNT) + { + start_pos += VGA_FONT_SIZE - VGA_FONT_LGC_COUNT; + start_pos %= df->size; + } + pos = start_pos; + + do + { + if (df->charmap_data[pos].refs == 0) + { + found = 1; + break; + } + pos++; + if ((pos % VGA_FONT_SIZE) == VGA_FONT_LGC_BEGIN + VGA_FONT_LGC_COUNT) + { + pos += VGA_FONT_SIZE - VGA_FONT_LGC_COUNT; + pos %= df->size; + } + } + while (pos != start_pos); + + assert (found); + df->vga_font_free_indices_lgc--; + df->vga_font_last_free_index_lgc = pos; + } + else + { + int start_pos = (df->vga_font_last_free_index + 1) % df->size; + if (df->use_lgc && (start_pos % VGA_FONT_SIZE) == VGA_FONT_LGC_BEGIN) + start_pos += VGA_FONT_LGC_COUNT; + pos = start_pos; + + do + { + if (df->charmap_data[pos].refs == 0) + { + found = 1; + break; + } + pos = (pos + 1) % df->size; + if (df->use_lgc && (pos % VGA_FONT_SIZE) == VGA_FONT_LGC_BEGIN) + pos += VGA_FONT_LGC_COUNT; + } + while (pos != start_pos); + + assert (found); + df->vga_font_free_indices--; + df->vga_font_last_free_index = pos; + } + + /* Ok, we found a new entry, use it. */ + chr = &df->charmap_data[pos]; + chr->refs = 1; + chr->character = (wide_chr | attr); + + /* XXX Should look at glyph bbox. */ + for (int i = 0; i < ((font->bbox.height > 32) + ? 32 : font->bbox.height); i++) + df->vga_font[pos][i] + = glyph->bitmap[i * ((font->bbox.width + 7) / 8)]; + if (font->bbox.height < 32) + memset (((char *) df->vga_font[pos]) + + font->bbox.height, 0, 32 - font->bbox.height); + + if (active_dynafont == df) + vga_write_font_buffer (0, pos, df->vga_font[pos], + VGA_FONT_HEIGHT); + /* Update the hash table. */ + if (chr->locp) + ihash_locp_remove (df->charmap, chr->locp); + ihash_add (df->charmap, (int) (wide_chr | attr), chr, &chr->locp); + *rpos = pos; + return 1; +} + +/* Look up the vga font index for an UCS-4 character. If not already + mapped, try to find space for a new entry and add the mapping. + Acquires an additional reference to the character. Might return + the glyph for the unrepresentable character if the glyph is ot + available for this character or no free slot is available right + now. In the former case, some information gets lost (to do + otherwise, one would have to either assign one of the scarce font + indices to each undisplayable character value on screen, or to + store the whole scrollback buffer as wide chars as well to recover + the lost info from that copy of the original text. */ +int +dynafont_lookup (dynafont_t df, conchar_t *conchr) +{ + wchar_t attr = (conchr->attr.italic ? WCHAR_ITALIC : 0) + | (conchr->attr.bold ? WCHAR_BOLD : 0); + int found = 0; + int pos = FONT_INDEX_UNKNOWN; + + if (attr == (WCHAR_BOLD | WCHAR_ITALIC) && df->font_bold_italic) + found = dynafont_lookup_internal (df, df->font_bold_italic, + conchr->chr, WCHAR_BOLD | WCHAR_ITALIC, + &pos); + /* This order will prefer bold over italic if both are requested but + are not available at the same time. The other order is just as + good. XXX. */ + if (!found && (attr & WCHAR_BOLD) && df->font_bold) + found = dynafont_lookup_internal (df, df->font_bold, + conchr->chr, WCHAR_BOLD, &pos); + if (!found && (attr & WCHAR_ITALIC) && df->font_italic) + found = dynafont_lookup_internal (df, df->font_italic, + conchr->chr, WCHAR_ITALIC, &pos); + if (!found) + found = dynafont_lookup_internal (df, df->font, conchr->chr, 0, &pos); + if (!found) + { + df->charmap_data[FONT_INDEX_UNKNOWN].refs++; + pos = FONT_INDEX_UNKNOWN; + } + return pos; +} + + +/* Release a reference to the glyph VGA_FONT_INDEX in dynafont DF. */ +void +dynafont_release (dynafont_t df, int vga_font_index) +{ + if (! --df->charmap_data[vga_font_index].refs) + { + if (df->use_lgc + && is_lgc (df->charmap_data[vga_font_index].character & WCHAR_MASK)) + df->vga_font_free_indices_lgc++; + else + df->vga_font_free_indices++; + } +} + + + +/* Set the cursor to normal if STANDOUT is zero, or to a block cursor + otherwise. */ +void +dynafont_set_cursor (dynafont_t df, int standout) +{ + int height = (df->font->bbox.height > 32) ? 32 : df->font->bbox.height; + + df->cursor_standout = standout; + + if (df == active_dynafont) + { + if (standout) + vga_set_cursor_size (1, -1); + else + vga_set_cursor_size ((height >= 2) ? height - 2 : 0, -1); + } +} + + +/* Load the VGA font to the card and make it active. */ +void +dynafont_activate (dynafont_t df) +{ + int height = (df->font->bbox.height > 32) ? 32 : df->font->bbox.height; + + vga_write_font_buffer (0, 0, (char *) df->vga_font, + df->size * VGA_FONT_HEIGHT); + vga_select_font_buffer (0, (df->size == 512) ? 1 : 0); + + /* XXX Changing the font height below 13 or above 16 will cause + display problems for the user if we don't also program the video + mode timings. The standard font height for 80x25 is 16. */ + vga_set_font_height (height); + vga_set_font_width (df->font->bbox.width <= 8 ? 8 : 9); + + active_dynafont = df; + dynafont_set_cursor (df, df->cursor_standout); +} + + +#if 0 +/* XXX This code is deactivated because it needs to be changed to + allow the italic and bold code, too. */ + +/* Change the font used by dynafont DF to FONT. This transformation + is initially loss-less, if the new font can't display some + characters on the screen, you can always go back to a font that + does and get the glyphs for those characters back. (However, the + comments in dynafont_lookup hold for glyphs looked up after the font + change.) */ +void +dynafont_change_font (dynafont_t df, bdf_font_t font) +{ + int i; + + df->font = font; + for (i = 0; i < df->size; i++) + { + /* If we don't derive the unknown or space glyph from the font, + we don't update it. */ +#ifndef ENCODING_UNKNOWN + if (i == FONT_INDEX_UNKNOWN) + continue; +#endif + if (! df->charmap_data[i].refs) + { + /* The glyph is not used. If it is mapped, we need to + remove the mapping to invalidate the glyph. */ + if (df->charmap_data[i].locp) + ihash_locp_remove (df->charmap, df->charmap_data[i].locp); + } + + else + { + /* The glyph is mapped and in use. We will not destroy the + mapping, but just override the glyph in the VGA font. + This way the user can recover from loading a bad font by + going back to a better one. */ + struct bdf_glyph *glyph; + glyph = bdf_find_glyph (df->font, + (int) df->charmap_data[i].character, 0); + if (!glyph) + glyph = bdf_find_glyph (df->font, -1, + (int) df->charmap_data[i].character); + if (!glyph) + { +#ifdef ENCODING_UNKNOWN + /* The new font doesn't have a representation for the + unknown glyph at the desired place, so keep the old + one. XXX Should load the built-in one here. */ + if (i == FONT_INDEX_UNKNOWN) + continue; +#endif + memcpy (df->vga_font[i], df->vga_font[FONT_INDEX_UNKNOWN], 32); + } + else + { + /* XXX Take font size and glyph size into account. */ + for (int j = 0; j < ((df->font->bbox.height > 32) + ? 32 : df->font->bbox.height); j++) + df->vga_font[i][j] + = glyph->bitmap[j * ((df->font->bbox.width + 7) / 8)]; + if (df->font->bbox.height < 32) + memset (((char *) df->vga_font[i]) + df->font->bbox.height, + 0, 32 - df->font->bbox.height); + } + } + } + + if (active_dynafont == df) + /* Refresh the card info. */ + dynafont_activate (df); +} +#endif diff --git a/console-client/vga-dynafont.h b/console-client/vga-dynafont.h new file mode 100644 index 00000000..c2bab700 --- /dev/null +++ b/console-client/vga-dynafont.h @@ -0,0 +1,83 @@ +/* vga-dynafont.h - Interface to the dynamic font handling for VGA cards. + Copyright (C) 2002 Free Software Foundation, Inc. + Written by Marcus Brinkmann. + + This file is part of the GNU Hurd. + + The GNU Hurd 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; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#ifndef _VGA_DYNAFONT_H_ +#define _VGA_DYNAFONT_H_ 1 + +#include + +#include "bdf.h" + + +/* The dynafont interface does not do locking on its own, for maximum + efficiency it relies on locking by the caller (because usually the + caller has some other data structures to lock along with the + dynafont. However, it is safe to call two functions on different + dynafonts asynchronously. */ +typedef struct dynafont *dynafont_t; + +/* The representation for the unknown glyph is always at the same + location. */ +#define FONT_INDEX_UNKNOWN 0 + +/* Create a new dynafont object, which uses glyphs from the font FONT + (which must be 8 pixels wide and up to 32 pixels heigh). SIZE is + either 256 or 512, and specifies the number of available glyphs in + the font cache. The object is returned in DYNAFONT. The caller + must ensure the integrity of FONT until the object is destroyed or + the font is changed with dynafont_change_font. */ +error_t dynafont_new (bdf_font_t font, bdf_font_t font_italic, + bdf_font_t font_bold, bdf_font_t font_bold_italic, + int size, dynafont_t *dynafont); + +/* Release a dynafont object and its associated resources. */ +void dynafont_free (dynafont_t df); + +/* Look up the vga font index for an UCS-4 character. If not already + mapped, try to find space for a new entry and add the mapping. + Acquires an additional reference to the character. Might return + the glyph for the unrepresentable character if the glyph is ot + available for this character or no free slot is available right + now. In the former case, some information gets lost (to do + otherwise, one would have to either assign one of the scarce font + indices to each undisplayable character value on screen, or to + store the whole scrollback buffer as wide chars as well to recover + the lost info from that copy of the original text. */ +int dynafont_lookup (dynafont_t df, conchar_t *chr); + +/* Release a reference to the glyph VGA_FONT_INDEX in dynafont DF. */ +void dynafont_release (dynafont_t df, int vga_font_index); + +/* Load the VGA font to the card and make it active. */ +void dynafont_activate (dynafont_t df); + +/* Set the cursor to normal if STANDOUT is zero, or to a block cursor + otherwise. */ +void dynafont_set_cursor (dynafont_t df, int standout); + +/* Change the font used by dynafont DF to FONT. This transformation + is initially loss-less, if the new font can't display some + characters on the screen, you can always go back to a font that + does and get the glyphs for those characters back. (However, the + comments in dynafont_lookup hold for glyphs looked up after the font + change.) */ +void dynafont_change_font (dynafont_t df, bdf_font_t font); + +#endif /* _VGA_DYNAFONT_H_ */ diff --git a/console-client/vga-hw.h b/console-client/vga-hw.h new file mode 100644 index 00000000..6be87d31 --- /dev/null +++ b/console-client/vga-hw.h @@ -0,0 +1,139 @@ +/* vga-hw.h - Definitions for the VGA hardware. + Copyright (C) 2002 Free Software Foundation, Inc. + Written by Marcus Brinkmann. + + This file is part of the GNU Hurd. + + The GNU Hurd 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; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#ifndef _VGA_HW_H_ +#define _VGA_HW_H_ 1 + +#define VGA_VIDEO_MEM_BASE_ADDR 0x0a0000 +#define VGA_VIDEO_MEM_LENGTH 0x004000 + +#define VGA_FONT_BUFFER 8 +#define VGA_FONT_SIZE 256 +#define VGA_FONT_HEIGHT 32 +#define VGA_FONT_LGC_BEGIN 0xc0 +#define VGA_FONT_LGC_COUNT 32 + +#define VGA_MIN_REG 0x3c0 +#define VGA_MAX_REG 0x3df + +/* The sequencer address register selects the sub-register of the + sequencer that is accessed through the sequencer data register. */ +#define VGA_SEQ_ADDR_REG 0x3c4 +#define VGA_SEQ_DATA_REG 0x3c5 + +/* The reset subregister can be used to asynchronously or + synchronously halt or clear the sequencer. */ +#define VGA_SEQ_RESET_ADDR 0x00 +#define VGA_SEQ_RESET_ASYNC 0x10 /* Can cause loss of video data. */ +#define VGA_SEQ_RESET_SYNC 0x01 +#define VGA_SEQ_RESET_CLEAR 0x11 /* Sequencer can operate. */ + +/* The clocking mode subregister. */ +#define VGA_SEQ_CLOCK_MODE_ADDR 0x01 +#define VGA_SEQ_CLOCK_MODE_8 0x01 /* 8-pixel width for fonts. */ + +/* The map subregister specifies which planes are written to. */ +#define VGA_SEQ_MAP_ADDR 0x02 +#define VGA_SEQ_MAP_PLANE0 0x01 +#define VGA_SEQ_MAP_PLANE1 0x02 +#define VGA_SEQ_MAP_PLANE2 0x04 +#define VGA_SEQ_MAP_PLANE3 0x08 + +/* The font subregister. */ +#define VGA_SEQ_FONT_ADDR 0x03 + +/* The memory mode subregister specifies the way that memory is + accessed. */ +#define VGA_SEQ_MODE_ADDR 0x04 +#define VGA_SEQ_MODE_EXT 0x02 /* Access 265kB rather than 64kB. */ +#define VGA_SEQ_MODE_SEQUENTIAL 0x04 /* Sequential, not odd/even addr. */ +#define VGA_SEQ_MODE_CHAIN4 0x08 /* Chain 4 addressing. */ + + +/* The graphics address register selects the sub-register that is + accessed through the graphics data register. */ +#define VGA_GFX_ADDR_REG 0x3ce +#define VGA_GFX_DATA_REG 0x3cf + +/* The map subregister selects the plane to read from. */ +#define VGA_GFX_MAP_ADDR 0x04 +#define VGA_GFX_MAP_PLANE0 0x00 +#define VGA_GFX_MAP_PLANE1 0x01 +#define VGA_GFX_MAP_PLANE2 0x02 +#define VGA_GFX_MAP_PLANE3 0x03 + +/* The mode subregister selects the memory access mode. */ +#define VGA_GFX_MODE_ADDR 0x05 +#define VGA_GFX_MODE_SHIFT256 0x40 +#define VGA_GFX_MODE_SHIFT 0x20 +#define VGA_GFX_MODE_HOSTOE 0x10 +#define VGA_GFX_MODE_READ0 0x00 +#define VGA_GFX_MODE_READ1 0x08 +#define VGA_GFX_MODE_WRITE0 0x00 +#define VGA_GFX_MODE_WRITE1 0x01 +#define VGA_GFX_MODE_WRITE2 0x02 +#define VGA_GFX_MODE_WRITE3 0x03 + +/* The miscellaneous subregister. */ +#define VGA_GFX_MISC_ADDR 0x06 +#define VGA_GFX_MISC_GFX 0x01 /* Switch on graphics mode. */ +#define VGA_GFX_MISC_CHAINOE 0x02 +#define VGA_GFX_MISC_A0TOBF 0x00 +#define VGA_GFX_MISC_A0TOAF 0x04 +#define VGA_GFX_MISC_B0TOB7 0x08 +#define VGA_GFX_MISC_B8TOBF 0x0c + + +/* The CRTC Registers. XXX Depends on the I/O Address Select field. + However, the only need to use the other values is for compatibility + with monochrome adapters. */ +#define VGA_CRT_ADDR_REG 0x3d4 +#define VGA_CRT_DATA_REG 0x3d5 + +/* The maximum scan line subregister. */ +#define VGA_CRT_MAX_SCAN_LINE 0x09 + +/* The cursor start subregister. */ +#define VGA_CRT_CURSOR_START 0x0a +#define VGA_CRT_CURSOR_DISABLE 0x20 + +/* The cursor end subregister. */ +#define VGA_CRT_CURSOR_END 0x0b + +/* The cursor position subregisters. */ +#define VGA_CRT_CURSOR_HIGH 0x0e +#define VGA_CRT_CURSOR_LOW 0x0f + + +/* The DAC Registers. */ +#define VGA_DAC_WRITE_ADDR_REG 0x3c8 +#define VGA_DAC_READ_ADDR_REG 0x3c7 +#define VGA_DAC_DATA_REG 0x3c9 + + +/* The Attribute Registers. */ +#define VGA_ATTR_ADDR_DATA_REG 0x3c0 +#define VGA_ATTR_DATA_READ_REG 0x3c1 + + +/* Other junk. */ +#define VGA_INPUT_STATUS_1_REG 0x3da + +#endif /* _VGA_HW_H_ */ diff --git a/console-client/vga-support.c b/console-client/vga-support.c new file mode 100644 index 00000000..babdfa46 --- /dev/null +++ b/console-client/vga-support.c @@ -0,0 +1,480 @@ +/* vga-support.c - VGA hardware access. + Copyright (C) 2002 Free Software Foundation, Inc. + Written by Marcus Brinkmann. + + This file is part of the GNU Hurd. + + The GNU Hurd 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; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "vga-hw.h" +#include "vga-support.h" + + +/* The base of the video memory mapping. */ +char *vga_videomem; + +/* The saved state of the VGA card. */ +struct vga_state +{ + unsigned char seq_clock_mode; + unsigned char seq_map; + unsigned char seq_font; + unsigned char seq_mode; + + unsigned char gfx_map; + unsigned char gfx_mode; + unsigned char gfx_misc; + + unsigned char crt_max_scan_line; + unsigned char crt_cursor_start; + unsigned char crt_cursor_end; + unsigned char crt_cursor_high; + unsigned char crt_cursor_low; + + char videomem[2 * 80 * 25]; + unsigned char fontmem[2 * VGA_FONT_SIZE * VGA_FONT_HEIGHT]; +}; + +static struct vga_state *vga_state; + + +#if OSKIT_MACH +#else + +#include +#include + +/* Constants from Mach. */ +#define VIDMMAP_BEGIN 0xA0000 +#define VIDMMAP_SIZE (0xC0000 - 0xA0000) +#define VIDMMAP_KDOFS 0xA0000 /* == kd_bitmap_start in mach/i386/i386at/kd.c */ + +#endif + +error_t +vga_init (void) +{ + error_t err; +#if OSKIT_MACH + int fd; +#else + device_t device_master = MACH_PORT_NULL; + memory_object_t kd_mem = MACH_PORT_NULL; + static device_t kd_device = MACH_PORT_NULL; + vm_address_t mapped; +#endif + +#if OSKIT_MACH + if (ioperm (VGA_MIN_REG, VGA_MAX_REG - VGA_MIN_REG + 1, 1) < 0) + { + free (vga_state); + return errno; + } + + fd = open ("/dev/mem", O_RDWR); + if (fd < 0) + return errno; + vga_videomem = mmap (0, VGA_VIDEO_MEM_LENGTH, PROT_READ | PROT_WRITE, + MAP_SHARED, fd, VGA_VIDEO_MEM_BASE_ADDR); + err = errno; + close (fd); + if (vga_videomem == (void *) -1) + return err; +#else + err = get_privileged_ports (0, &device_master); + if (err) + return err; + + err = device_open (device_master, D_WRITE, "kd", &kd_device); + if (err) + return err; + + err = device_map (kd_device, VM_PROT_READ | VM_PROT_WRITE, + VIDMMAP_BEGIN - VIDMMAP_KDOFS, VIDMMAP_SIZE, + &kd_mem, 0); + if (err) + return err; + + err = vm_map (mach_task_self (), &mapped, VIDMMAP_SIZE, + 0, 1, kd_mem, VIDMMAP_BEGIN - VIDMMAP_KDOFS, 0, + VM_PROT_READ | VM_PROT_WRITE, VM_PROT_READ | VM_PROT_WRITE, + VM_INHERIT_NONE); + if (err) + return err; + + vga_videomem = (char *) mapped; + assert (vga_videomem != NULL); + + mach_port_deallocate (mach_task_self (), device_master); + mach_port_deallocate (mach_task_self (), kd_mem); +#endif + + /* Save the current state. */ + vga_state = malloc (sizeof (*vga_state)); + if (!vga_state) + return errno; + + outb (VGA_SEQ_CLOCK_MODE_ADDR, VGA_SEQ_ADDR_REG); + vga_state->seq_clock_mode = inb (VGA_SEQ_DATA_REG); + outb (VGA_SEQ_MAP_ADDR, VGA_SEQ_ADDR_REG); + vga_state->seq_map = inb (VGA_SEQ_DATA_REG); + outb (VGA_SEQ_FONT_ADDR, VGA_SEQ_ADDR_REG); + vga_state->seq_font = inb (VGA_SEQ_DATA_REG); + outb (VGA_SEQ_MODE_ADDR, VGA_SEQ_ADDR_REG); + vga_state->seq_mode = inb (VGA_SEQ_DATA_REG); + + outb (VGA_GFX_MAP_ADDR, VGA_GFX_ADDR_REG); + vga_state->gfx_map = inb (VGA_GFX_DATA_REG); + outb (VGA_GFX_MODE_ADDR, VGA_GFX_ADDR_REG); + vga_state->gfx_mode = inb (VGA_GFX_DATA_REG); + outb (VGA_GFX_MISC_ADDR, VGA_GFX_ADDR_REG); + vga_state->gfx_misc = inb (VGA_GFX_DATA_REG); + + outb (VGA_CRT_MAX_SCAN_LINE, VGA_CRT_ADDR_REG); + vga_state->crt_max_scan_line = inb (VGA_CRT_DATA_REG); + outb (VGA_CRT_CURSOR_START, VGA_CRT_ADDR_REG); + vga_state->crt_cursor_start = inb (VGA_CRT_DATA_REG); + outb (VGA_CRT_CURSOR_END, VGA_CRT_ADDR_REG); + vga_state->crt_cursor_end = inb (VGA_CRT_DATA_REG); + outb (VGA_CRT_CURSOR_HIGH, VGA_CRT_ADDR_REG); + vga_state->crt_cursor_high = inb (VGA_CRT_DATA_REG); + outb (VGA_CRT_CURSOR_LOW, VGA_CRT_ADDR_REG); + vga_state->crt_cursor_low = inb (VGA_CRT_DATA_REG); + + /* Read/write in interleaved mode. */ + outb (VGA_GFX_MISC_ADDR, VGA_GFX_ADDR_REG); + outb (VGA_GFX_MISC_CHAINOE | VGA_GFX_MISC_A0TOAF, VGA_GFX_DATA_REG); + outb (VGA_GFX_MODE_ADDR, VGA_GFX_ADDR_REG); + outb (VGA_GFX_MODE_HOSTOE, VGA_GFX_DATA_REG); + + memcpy (vga_state->videomem, vga_videomem, 2 * 80 * 25); + vga_read_font_buffer (0, 0, vga_state->fontmem, + 2 * VGA_FONT_SIZE * VGA_FONT_HEIGHT); + + /* 80 cols, 25 rows, two bytes per cell and twice because with lower + max scan line we get more lines on the screen. */ + memset (vga_videomem, 0, 80 * 25 * 2 * 2); + + return 0; +} + + +/* Release the resources and privileges associated with the VGA + hardware access. */ +void +vga_fini (void) +{ + /* Recover the saved state. */ + vga_write_font_buffer (0, 0, vga_state->fontmem, + 2 * VGA_FONT_SIZE * VGA_FONT_HEIGHT); + memcpy (vga_videomem, vga_state->videomem, 2 * 80 * 25); + + /* Restore the registers. */ + outb (VGA_SEQ_CLOCK_MODE_ADDR, VGA_SEQ_ADDR_REG); + outb (vga_state->seq_clock_mode, VGA_SEQ_DATA_REG); + outb (VGA_SEQ_MAP_ADDR, VGA_SEQ_ADDR_REG); + outb (vga_state->seq_map, VGA_SEQ_DATA_REG); + outb (VGA_SEQ_FONT_ADDR, VGA_SEQ_ADDR_REG); + outb (vga_state->seq_font, VGA_SEQ_DATA_REG); + outb (VGA_SEQ_MODE_ADDR, VGA_SEQ_ADDR_REG); + outb (vga_state->seq_mode, VGA_SEQ_DATA_REG); + + outb (VGA_GFX_MAP_ADDR, VGA_GFX_ADDR_REG); + outb (vga_state->gfx_map, VGA_GFX_DATA_REG); + outb (VGA_GFX_MODE_ADDR, VGA_GFX_ADDR_REG); + outb (vga_state->gfx_mode, VGA_GFX_DATA_REG); + outb (VGA_GFX_MISC_ADDR, VGA_GFX_ADDR_REG); + outb (vga_state->gfx_misc, VGA_GFX_DATA_REG); + + outb (VGA_CRT_MAX_SCAN_LINE, VGA_CRT_ADDR_REG); + outb (vga_state->crt_max_scan_line, VGA_CRT_DATA_REG); + outb (VGA_CRT_CURSOR_START, VGA_CRT_ADDR_REG); + outb (vga_state->crt_cursor_start, VGA_CRT_DATA_REG); + outb (VGA_CRT_CURSOR_END, VGA_CRT_ADDR_REG); + outb (vga_state->crt_cursor_end, VGA_CRT_DATA_REG); + outb (VGA_CRT_CURSOR_HIGH, VGA_CRT_ADDR_REG); + outb (vga_state->crt_cursor_high, VGA_CRT_DATA_REG); + outb (VGA_CRT_CURSOR_LOW, VGA_CRT_ADDR_REG); + outb (vga_state->crt_cursor_low, VGA_CRT_DATA_REG); + + ioperm (VGA_MIN_REG, VGA_MAX_REG, 0); + munmap (vga_videomem, VGA_VIDEO_MEM_LENGTH); +} + + +/* Write DATALEN bytes from DATA to the font buffer BUFFER, starting + from glyph INDEX. */ +void +vga_write_font_buffer (int buffer, int index, char *data, size_t datalen) +{ + char saved_seq_map; + char saved_seq_mode; + char saved_gfx_mode; + char saved_gfx_misc; + + int offset = buffer * VGA_FONT_SIZE + index * VGA_FONT_HEIGHT; + assert (offset >= 0 && offset + datalen <= VGA_VIDEO_MEM_LENGTH); + + /* Select plane 2 for sequential writing. */ + outb (VGA_SEQ_MAP_ADDR, VGA_SEQ_ADDR_REG); + saved_seq_map = inb (VGA_SEQ_DATA_REG); + outb (VGA_SEQ_MAP_PLANE2, VGA_SEQ_DATA_REG); + outb (VGA_SEQ_MODE_ADDR, VGA_SEQ_ADDR_REG); + saved_seq_mode = inb (VGA_SEQ_DATA_REG); + outb (VGA_SEQ_MODE_SEQUENTIAL | VGA_SEQ_MODE_EXT | 0x1 /* XXX Why? */, + VGA_SEQ_DATA_REG); + + /* Set write mode 0, but assume that rotate count, enable set/reset, + logical operation and bit mask fields are set to their + `do-not-modify-host-value' default. The misc register is set to + select sequential addressing in text mode. */ + outb (VGA_GFX_MODE_ADDR, VGA_GFX_ADDR_REG); + saved_gfx_mode = inb (VGA_GFX_DATA_REG); + outb (VGA_GFX_MODE_WRITE0, VGA_GFX_DATA_REG); + outb (VGA_GFX_MISC_ADDR, VGA_GFX_ADDR_REG); + saved_gfx_misc = inb (VGA_GFX_DATA_REG); + outb (VGA_GFX_MISC_A0TOAF, VGA_GFX_DATA_REG); + + memcpy (vga_videomem + offset, data, datalen); + + /* Restore sequencer and graphic register values. */ + outb (VGA_SEQ_MAP_ADDR, VGA_SEQ_ADDR_REG); + outb (saved_seq_map, VGA_SEQ_DATA_REG); + outb (VGA_SEQ_MODE_ADDR, VGA_SEQ_ADDR_REG); + outb (saved_seq_mode, VGA_SEQ_DATA_REG); + + outb (VGA_GFX_MODE_ADDR, VGA_GFX_ADDR_REG); + outb (saved_gfx_mode, VGA_GFX_DATA_REG); + outb (VGA_GFX_MISC_ADDR, VGA_GFX_ADDR_REG); + outb (saved_gfx_misc, VGA_GFX_DATA_REG); +} + + +/* Read DATALEN bytes into DATA from the font buffer BUFFER, starting + from glyph INDEX. */ +void +vga_read_font_buffer (int buffer, int index, char *data, size_t datalen) +{ + char saved_gfx_map; + char saved_gfx_mode; + char saved_gfx_misc; + + int offset = buffer * VGA_FONT_SIZE + index * VGA_FONT_HEIGHT; + assert (offset >= 0 && offset + datalen <= VGA_VIDEO_MEM_LENGTH); + + /* Read sequentially from plane 2. */ + outb (VGA_GFX_MAP_ADDR, VGA_GFX_ADDR_REG); + saved_gfx_map = inb (VGA_GFX_DATA_REG); + outb (VGA_GFX_MAP_PLANE2, VGA_GFX_DATA_REG); + outb (VGA_GFX_MODE_ADDR, VGA_GFX_ADDR_REG); + saved_gfx_mode = inb (VGA_GFX_DATA_REG); + outb (VGA_GFX_MODE_READ0, VGA_GFX_DATA_REG); + outb (VGA_GFX_MISC_ADDR, VGA_GFX_ADDR_REG); + saved_gfx_misc = inb (VGA_GFX_DATA_REG); + outb (VGA_GFX_MISC_A0TOBF, VGA_GFX_DATA_REG); + + memcpy (data, vga_videomem + offset, datalen); + + outb (VGA_GFX_MAP_ADDR, VGA_GFX_ADDR_REG); + outb (saved_gfx_map, VGA_GFX_DATA_REG); + outb (VGA_GFX_MODE_ADDR, VGA_GFX_ADDR_REG); + outb (saved_gfx_mode, VGA_GFX_DATA_REG); + outb (VGA_GFX_MISC_ADDR, VGA_GFX_ADDR_REG); + outb (saved_gfx_misc, VGA_GFX_DATA_REG); +} + + +/* Set FONT_BUFFER_SUPP to FONT_BUFFER if the font is small. */ +void +vga_select_font_buffer (int font_buffer, int font_buffer_supp) +{ + char font = ((font_buffer & 6) >> 1) | ((font_buffer & 1) << 4) + | ((font_buffer_supp & 6) << 1) | ((font_buffer_supp & 1) << 5); + + outb (VGA_SEQ_FONT_ADDR, VGA_SEQ_ADDR_REG); + outb (font, VGA_SEQ_DATA_REG); +} + +/* Set the font height in pixel. */ +void +vga_set_font_height (int height) +{ + char saved; + + outb (VGA_CRT_MAX_SCAN_LINE, VGA_CRT_ADDR_REG); + saved = inb (VGA_CRT_DATA_REG); + saved &= ~31; + saved |= (height - 1) & 31; + outb (saved, VGA_CRT_DATA_REG); +} + + +/* Set the font height in pixel. WIDTH can be 8 or 9. */ +void +vga_set_font_width (int width) +{ + char saved; + + if (width != 8 && width != 9) + return; + + outb (VGA_SEQ_CLOCK_MODE_ADDR, VGA_SEQ_ADDR_REG); + saved = inb (VGA_SEQ_DATA_REG); + if (width == 8) + saved |= VGA_SEQ_CLOCK_MODE_8; + else + saved &= ~VGA_SEQ_CLOCK_MODE_8; + outb (saved, VGA_SEQ_DATA_REG); +} + + +/* Enable (if ON is true) or disable (otherwise) the cursor. Expects + the VGA hardware to be locked. */ +void +vga_display_cursor (int on) +{ + char crs_start; + + outb (VGA_CRT_CURSOR_START, VGA_CRT_ADDR_REG); + crs_start = inb (VGA_CRT_DATA_REG); + if (on) + crs_start &= ~VGA_CRT_CURSOR_DISABLE; + else + crs_start |= VGA_CRT_CURSOR_DISABLE; + outb (crs_start, VGA_CRT_DATA_REG); +} + + +/* Set cursor size from START to END (set to -1 to not set one of the + values). */ +void +vga_set_cursor_size (int start, int end) +{ + char saved; + + if (start >= 0) + { + outb (VGA_CRT_CURSOR_START, VGA_CRT_ADDR_REG); + saved = inb (VGA_CRT_DATA_REG); + saved &= ~31; + saved |= start & 31; + outb (saved, VGA_CRT_DATA_REG); + } + if (end >= 0) + { + outb (VGA_CRT_CURSOR_END, VGA_CRT_ADDR_REG); + saved = inb (VGA_CRT_DATA_REG); + saved &= ~31; + saved |= end & 31; + outb (saved, VGA_CRT_DATA_REG); + } +} + + +/* Set the cursor position to POS, which is (x_pos + y_pos * width). */ +void +vga_set_cursor_pos (unsigned int pos) +{ + outb (VGA_CRT_CURSOR_HIGH, VGA_CRT_ADDR_REG); + outb (pos >> 8, VGA_CRT_DATA_REG); + outb (VGA_CRT_CURSOR_LOW, VGA_CRT_ADDR_REG); + outb (pos & 0xff, VGA_CRT_DATA_REG); +} + + +/* Read NR entries from the color palette, starting from INDEX. DATA + must be able to hold at least 3 * NR bytes and will contain the + desired colors in RGB form. Only the lower six bits of each + component are significant. */ +void +vga_read_palette (unsigned char index, unsigned char *data, int nr) +{ + /* Every color has three components. */ + nr *= 3; + + outb (index, VGA_DAC_READ_ADDR_REG); + while (nr--) + *(data++) = inb (VGA_DAC_DATA_REG); +} + + +/* Write NR entries to the color palette, starting from INDEX. DATA + must be at least 3 * NR of bytes long and contains the desired + colors in RGB form. Only the lower six bits for each component are + significant. */ +void +vga_write_palette (unsigned char index, const unsigned char *data, int nr) +{ + /* Every color has three components. */ + nr *= 3; + + outb (index, VGA_DAC_WRITE_ADDR_REG); + while (nr--) + outb (*(data++), VGA_DAC_DATA_REG); +} + + +/* Exchange NR entries in the internal palette with the values in + PALETTE_ATTR, starting from the internal palette entry INDEX (which + can be betweern 0 and 15). Only the lower six bits of each entry + in PALETTE_ATTR is significant. + + The internal palette entry is used to look up a color in the + palette for the color attribute in text mode with the corresponding + value. + + Example: The attribute byte specifies 3 as the foreground color. + This means that the character with this attribute gets the color of + the palette entry specified by the internal palette entry 3 (the + fourth one). */ +void +vga_exchange_palette_attributes (unsigned char index, + unsigned char *palette_attr, + int nr) +{ + if (!nr) + return; + + /* We want to read and change the palette attribute register. */ + while (nr--) + { + unsigned char attr; + + /* Side effect of reading the input status #1 register is to + reset the attribute mixed address/data register so that the + next write it expects is the address, not the data. */ + inb (VGA_INPUT_STATUS_1_REG); + + /* Set the address. */ + outb (index++, VGA_ATTR_ADDR_DATA_REG); + attr = inb (VGA_ATTR_DATA_READ_REG); + outb ((attr & ~077) | (*palette_attr & 077), VGA_ATTR_ADDR_DATA_REG); + *(palette_attr++) = attr & 077; + } + + /* Re-enable the screen, which was blanked during the palette + operation. */ + inb (VGA_INPUT_STATUS_1_REG); + outb (0x20, VGA_ATTR_ADDR_DATA_REG); +} diff --git a/console-client/vga-support.h b/console-client/vga-support.h new file mode 100644 index 00000000..51dec782 --- /dev/null +++ b/console-client/vga-support.h @@ -0,0 +1,86 @@ +/* vga-support.h - Interface for VGA hardware access. + Copyright (C) 2002 Free Software Foundation, Inc. + Written by Marcus Brinkmann. + + This file is part of the GNU Hurd. + + The GNU Hurd 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; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#ifndef _VGA_SUPPORT_H_ +#define _VGA_SUPPORT_H_ 1 + +#include +#include + + +/* The VGA interface does not do locking on its own, for maximum + efficiency it relies on locking by the caller (because usually the + caller has some other data structures to lock along with the + VGA hardware. */ + +/* The mapped video memory. */ +extern char *vga_videomem; + +/* Initialize the VGA hardware and set up the permissions and memory + mappings. */ +error_t vga_init (void); + +/* Release the resources and privileges associated with the VGA + hardware access. */ +void vga_fini (void); + +/* Write DATALEN bytes from DATA to the font buffer BUFFER, starting + from glyph index. */ +void vga_write_font_buffer (int buffer, int index, + char *data, size_t datalen); + +/* Read DATALEN bytes into DATA from the font buffer BUFFER, starting + from glyph INDEX. */ +void vga_read_font_buffer (int buffer, int index, + char *data, size_t datalen); + +/* Set FONT_BUFFER_SUPP to FONT_BUFFER if the font is small. */ +void vga_select_font_buffer (int font_buffer, int font_buffer_supp); + +/* Set the font height in pixel. */ +void vga_set_font_height (int height); + +/* Set the font height in pixel. WIDTH can be 8 or 9. */ +void vga_set_font_width (int width); + +/* Enable (if ON is true) or disable (otherwise) the cursor. Expects + the VGA hardware to be locked. */ +void vga_display_cursor (int on); + +/* Set cursor size from START to END (set to -1 to not set one of the + values). */ +void vga_set_cursor_size (int start, int end); + +/* Set the cursor position to POS, which is (x_pos + y_pos * + width). */ +void vga_set_cursor_pos (unsigned int pos); + +/* Read NR entries from the color palette, starting from INDEX. */ +void vga_read_palette (unsigned char index, unsigned char *data, int nr); + +/* Write NR entries to the color palette, starting from INDEX. */ +void vga_write_palette (unsigned char index, + const unsigned char *data, int nr); + +void vga_exchange_palette_attributes (unsigned char index, + unsigned char *saved_palette_attr, + int nr); + +#endif /* _VGA_SUPPORT_H_ */ diff --git a/console-client/vga.c b/console-client/vga.c new file mode 100644 index 00000000..304adbc1 --- /dev/null +++ b/console-client/vga.c @@ -0,0 +1,573 @@ +/* vga.c - The VGA device display driver. + Copyright (C) 2002 Free Software Foundation, Inc. + Written by Marcus Brinkmann. + + This file is part of the GNU Hurd. + + The GNU Hurd 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; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "driver.h" +#include "timer.h" + +#include "vga-hw.h" +#include "vga-support.h" +#include "bdf.h" +#include "vga-dynafont.h" +#include "vga-dynacolor.h" +#include "unicode.h" + + +#define VGA_DISP_WIDTH 80 +#define VGA_DISP_HEIGHT 25 + +/* The font file. */ +#define DEFAULT_VGA_FONT "/lib/hurd/fonts/vga-system.bdf" +static char *vga_display_font; + +#define DEFAULT_VGA_FONT_ITALIC "/lib/hurd/fonts/vga-system-italic.bdf" +static char *vga_display_font_italic; + +#define DEFAULT_VGA_FONT_BOLD "/lib/hurd/fonts/vga-system-bold.bdf" +static char *vga_display_font_bold; + +#define DEFAULT_VGA_FONT_BOLD_ITALIC \ + "/lib/hurd/fonts/vga-system-bold-italic.bdf" +static char *vga_display_font_bold_italic; + +/* The timer used for flashing the screen. */ +static struct timer_list vga_display_timer; + +/* The lock that protects the color palette manipulation. */ +static struct mutex vga_display_lock; + +/* Forward declaration. */ +static struct display_ops vga_display_ops; + + +struct refchr +{ + unsigned int used : 1; + unsigned int chr : 9; + unsigned int attr : 8; +}; + + +struct vga_display +{ + /* The VGA font for this display. */ + dynafont_t df; + int df_size; + + /* The color palette. */ + dynacolor_t dc; + + int width; + int height; + + /* Current attribute. */ + int cur_conchar_attr_init; + conchar_attr_t cur_conchar_attr; + char cur_attr; + + /* Remember for each cell on the display the glyph written to it and + the colors (in the upper byte) assigned. 0 means unassigned. */ + + struct refchr refmatrix[VGA_DISP_HEIGHT][VGA_DISP_WIDTH]; +}; + + +static void +vga_display_invert_border (void) +{ + unsigned char col[3]; + + mutex_lock (&vga_display_lock); + vga_read_palette (0, col, 1); + col[0] = 0xff - col[0]; + col[1] = 0xff - col[1]; + col[2] = 0xff - col[2]; + vga_write_palette (0, col, 1); + mutex_unlock (&vga_display_lock); +} + + +static int +vga_display_flash_off (void *dummy) +{ + vga_display_invert_border (); + return 0; +} + + +static error_t +vga_display_flash (void *handle) +{ + if (timer_remove (&vga_display_timer)) + vga_display_invert_border (); + vga_display_invert_border (); + vga_display_timer.expires = fetch_jiffies () + 10; + timer_add (&vga_display_timer); + return 0; +} + + +/* Parse arguments at startup. */ +static error_t +parse_startup_args (int no_exit, int argc, char *argv[], int *next) +{ +#define PARSE_FONT_OPT(x,y) \ + do { \ + if (!strcmp (argv[*next], x)) \ + { \ + (*next)++; \ + if (*next == argc) \ + { \ + if (no_exit) \ + return EINVAL; \ + else \ + error (1, 0, "option " x \ + " requires an argument"); \ + } \ + if (vga_display_##y) \ + free (vga_display_##y); \ + vga_display_##y = strdup (argv[*next]); \ + if (!vga_display_##y) \ + { \ + if (no_exit) \ + return errno; \ + else \ + error (1, errno, "malloc failed"); \ + } \ + (*next)++; \ + continue; \ + } \ + } while (0) + + while (*next < argc) + { + PARSE_FONT_OPT ("--font", font); + PARSE_FONT_OPT ("--font-italic", font_italic); + PARSE_FONT_OPT ("--font-bold", font_bold); + PARSE_FONT_OPT ("--font-bold-italic", font_bold_italic); + + break; + } + return 0; +} + +/* Initialize the subsystem. */ +static error_t +vga_display_init (void **handle, int no_exit, int argc, char *argv[], int *next) +{ + error_t err; + struct vga_display *disp; + + /* XXX Assert that we are called only once. */ + mutex_init (&vga_display_lock); + timer_clear (&vga_display_timer); + vga_display_timer.fnc = &vga_display_flash_off; + + /* Parse the arguments. */ + err = parse_startup_args (no_exit, argc, argv, next); + if (err) + return err; + + /* Create and initialize the display srtucture as much as + possible. */ + disp = calloc (1, sizeof *disp); + if (!disp) + return ENOMEM; + + /* Set this to 256 for full color support. */ + disp->df_size = 512; + disp->width = VGA_DISP_WIDTH; + disp->height = VGA_DISP_HEIGHT; + + *handle = disp; + return 0; +} + + +/* Start the driver. */ +static error_t +vga_display_start (void *handle) +{ + error_t err; + struct vga_display *disp = handle; + bdf_font_t font = NULL; + bdf_font_t font_italic = NULL; + bdf_font_t font_bold = NULL; + bdf_font_t font_bold_italic = NULL; + FILE *font_file; + + err = vga_init (); + if (err) + return err; + + dynacolor_init (); + +#define LOAD_FONT(x,y) \ + do { \ + font_file = fopen (vga_display_##x ?: DEFAULT_VGA_##y, "r"); \ + if (font_file) \ + { \ + bdf_error_t bdferr = bdf_read (font_file, &x, NULL); \ + if (bdferr) \ + x = NULL; \ + else \ + bdf_sort_glyphs (x); \ + fclose (font_file); \ + } \ + } while (0) + + LOAD_FONT (font, FONT); + LOAD_FONT (font_italic, FONT_ITALIC); + LOAD_FONT (font_bold, FONT_BOLD); + LOAD_FONT (font_bold_italic, FONT_BOLD_ITALIC); + + err = dynafont_new (font, font_italic, font_bold, font_bold_italic, + disp->df_size, &disp->df); + if (err) + { + free (disp); + vga_fini (); + return err; + } + dynafont_activate (disp->df); + + disp->dc = (disp->df_size == 512) ? dynacolor_init_8 : dynacolor_init_16; + dynacolor_activate (&disp->dc); + + err = driver_add_display (&vga_display_ops, disp); + if (err) + { + dynafont_free (disp->df); + dynacolor_fini (); + vga_fini (); + free (disp); + } + return err; +} + + +/* Destroy the display HANDLE. */ +static error_t +vga_display_fini (void *handle, int force) +{ + struct vga_display *disp = handle; + driver_remove_display (&vga_display_ops, disp); + if (timer_remove (&vga_display_timer)) + vga_display_flash_off (0); + + dynafont_free (disp->df); + free (disp); + dynacolor_fini (); + vga_fini (); + if (vga_display_font) + free (vga_display_font); + if (vga_display_font_italic) + free (vga_display_font_italic); + if (vga_display_font_bold) + free (vga_display_font_bold); + if (vga_display_font_bold_italic) + free (vga_display_font_bold_italic); + + return 0; +} + + +/* Set the cursor's position on display HANDLE to column COL and row + ROW. */ +static error_t +vga_display_set_cursor_pos (void *handle, uint32_t col, uint32_t row) +{ + struct vga_display *disp = handle; + unsigned int pos = row * disp->width + col; + + vga_set_cursor_pos (pos); + + return 0; +} + + +/* Set the cursor's state to STATE on display HANDLE. */ +static error_t +vga_display_set_cursor_status (void *handle, uint32_t state) +{ + struct vga_display *disp = handle; + + if (state != CONS_CURSOR_INVISIBLE) + dynafont_set_cursor (disp->df, state == CONS_CURSOR_VERY_VISIBLE ? 1 : 0); + vga_display_cursor (state == CONS_CURSOR_INVISIBLE ? 0 : 1); + + return 0; +} + + +/* Scroll the display by the desired amount. The area that becomes + free will be filled in a subsequent write call. */ +static error_t +vga_display_scroll (void *handle, int delta) +{ + struct vga_display *disp = handle; + int count = abs(delta) * disp->width; + int i; + struct refchr *refpos; + + if (delta > 0) + { + memmove (vga_videomem, vga_videomem + 2 * count, + 2 * disp->width * (disp->height - delta)); + refpos = &disp->refmatrix[0][0]; + } + else + { + memmove (vga_videomem + 2 * count, vga_videomem, + 2 * disp->width * (disp->height + delta)); + refpos = &disp->refmatrix[disp->height + delta][0]; + } + + for (i = 0; i < count; i++) + { + if (refpos->used) + { + dynafont_release (disp->df, refpos->chr); + /* We intimately know that reference counting is only done + for the up to 8 colors mode. */ + dynacolor_release (disp->dc, refpos->attr & 7); + dynacolor_release (disp->dc, (refpos->attr >> 4) & 7); + } + refpos++; + } + + if (delta > 0) + { + memmove (&disp->refmatrix[0][0], &disp->refmatrix[0][0] + count, + sizeof (struct refchr) * disp->width * (disp->height - delta)); + refpos = &disp->refmatrix[disp->height - delta][0]; + } + else + { + memmove (&disp->refmatrix[0][0] + count, &disp->refmatrix[0][0], + sizeof (struct refchr) * disp->width * (disp->height + delta)); + refpos = &disp->refmatrix[0][0]; + } + + for (i = 0; i < count; i++) + (refpos++)->used = 0; + + return 0; +} + + +/* Change the font on the console CONSOLE to font. The old font will + not be accessed by the vga console subsystem anymore after this + call completed. */ +static void +vga_display_change_font (void *handle, bdf_font_t font) +{ + struct vga_display *disp = handle; + + dynafont_change_font (disp->df, font); +} + + +static inline char +vga_display_recalculate_attr (dynacolor_t *dc, conchar_attr_t attr) +{ + char vga_attr; + signed char res_fgcol; + signed char res_bgcol; + signed char fgcol; + signed char bgcol; + + /* VGA has way too few bits for this stuff. The highest background + color bit is also the blinking bit if blinking is enabled. The + highest foreground color bit is the font selector bit, + unfortunately. Underlining is enabled if foreground is ?001 and + background ?000. */ + + /* Reversed means colors are reversed. Note that this does not + reverse the intensity. */ + if (attr.reversed) + { + fgcol = attr.bgcol; + bgcol = attr.fgcol; + } + else + { + fgcol = attr.fgcol; + bgcol = attr.bgcol; + } + + /* Set the foreground color. */ + if (attr.concealed) + fgcol = bgcol; + else + { + /* Intensity bold and dim also affect the font selection bit. */ + switch (attr.intensity) + { + case CONS_ATTR_INTENSITY_BOLD: + fgcol |= 1 << 3; + break; + case CONS_ATTR_INTENSITY_DIM: + fgcol = CONS_COLOR_BLACK | 1 << 3; + break; + case CONS_ATTR_INTENSITY_NORMAL: + break; + } + } + + /* Try to get the colors as desired. This might change the palette, + so we need to take the lock (in case a flash operation times + out). */ + mutex_lock (&vga_display_lock); + res_bgcol = dynacolor_lookup (*dc, bgcol); + res_fgcol = dynacolor_lookup (*dc, fgcol); + mutex_unlock (&vga_display_lock); + if (res_bgcol == -1 || res_fgcol == -1) + dynacolor_replace_colors (dc, fgcol, bgcol, &res_fgcol, &res_bgcol); + vga_attr = res_bgcol << 4 | res_fgcol; + + vga_attr |= attr.blinking << 7; + + /* XXX We can support underlined in a monochrome mode. */ + return vga_attr; +} + + +/* Deallocate any scarce resources occupied by the LENGTH characters + from column COL and row ROW. */ +static error_t +vga_display_clear (void *handle, size_t length, uint32_t col, uint32_t row) +{ + struct vga_display *disp = handle; + struct refchr *refpos = &disp->refmatrix[row][col]; + + while (length > 0) + { + if (refpos->used) + { + dynafont_release (disp->df, refpos->chr); + /* We intimately know that reference counting is only done + for the up to 8 colors mode. */ + dynacolor_release (disp->dc, refpos->attr & 7); + dynacolor_release (disp->dc, (refpos->attr >> 4) & 7); + refpos->used = 0; + } + refpos++; + length--; + } + return 0; +} + +/* Write the text STR with LENGTH characters to column COL and row + ROW. */ +static error_t +vga_display_write (void *handle, conchar_t *str, size_t length, + uint32_t col, uint32_t row) +{ + struct vga_display *disp = handle; + char *pos = vga_videomem + 2 * (row * disp->width + col); + struct refchr *refpos = &disp->refmatrix[row][col]; + + /* Although all references to the current fgcol or bgcol could have + been released here, for example due to a scroll operation, we + know that the color slots have not been reused yet, as no routine + but ours does color allocation. This ensures that cur_attr is + still valid. XXX consider recalculating the attribute for more + authentic (but less homogenous) colors anyway. */ + + while (length--) + { + int charval = dynafont_lookup (disp->df, str); + if (!disp->cur_conchar_attr_init + || *(uint32_t *) &disp->cur_conchar_attr != *(uint32_t *) &str->attr) + { + if (!disp->cur_conchar_attr_init) + disp->cur_conchar_attr_init = 1; + disp->cur_conchar_attr = str->attr; + disp->cur_attr = vga_display_recalculate_attr (&disp->dc, str->attr); + } + else + { + /* Add two references to the colors. See comment above for + why we can assume that this will succeed. */ + /* We intimately know that reference counting is only done + for the up to 8 colors mode. */ + dynacolor_add_ref (disp->dc, disp->cur_attr & 7); + dynacolor_add_ref (disp->dc, (disp->cur_attr >> 4) & 7); + } + + *(pos++) = charval & 0xff; + *(pos++) = disp->cur_attr + | (disp->df_size == 512 ? (charval >> 5) & 0x8 : 0); + + /* Perform reference counting. */ + if (refpos->used) + { + dynafont_release (disp->df, refpos->chr); + /* We intimately know that reference counting is only done + for the up to 8 colors mode. */ + dynacolor_release (disp->dc, refpos->attr & 7); + dynacolor_release (disp->dc, (refpos->attr >> 4) & 7); + } + refpos->used = 1; + refpos->chr = charval; + refpos->attr = disp->cur_attr; + refpos++; + + /* Wrap around displayed area. */ + str++; + } + return 0; +} + + +struct driver_ops driver_vga_ops = + { + vga_display_init, + vga_display_start, + vga_display_fini, + }; + +static struct display_ops vga_display_ops = + { + vga_display_set_cursor_pos, + vga_display_set_cursor_status, + vga_display_scroll, + vga_display_clear, + vga_display_write, + NULL, + vga_display_flash, + NULL + }; -- cgit v1.2.3