diff options
-rw-r--r-- | console/Makefile | 34 | ||||
-rw-r--r-- | console/bdf.c | 986 | ||||
-rw-r--r-- | console/bdf.h | 272 | ||||
-rw-r--r-- | console/console.c | 511 | ||||
-rw-r--r-- | console/console.h | 98 | ||||
-rw-r--r-- | console/display.h | 79 | ||||
-rw-r--r-- | console/dynafont.c | 371 | ||||
-rw-r--r-- | console/dynafont.h | 74 | ||||
-rw-r--r-- | console/focus.c | 110 | ||||
-rw-r--r-- | console/focus.h | 31 | ||||
-rw-r--r-- | console/input.h | 62 | ||||
-rw-r--r-- | console/main.c | 461 | ||||
-rw-r--r-- | console/mutations.h | 26 | ||||
-rw-r--r-- | console/priv.h | 1 | ||||
-rw-r--r-- | console/vga-display.c | 832 | ||||
-rw-r--r-- | console/vga-hw.h | 111 | ||||
-rw-r--r-- | console/vga.c | 189 | ||||
-rw-r--r-- | console/vga.h | 62 |
18 files changed, 4310 insertions, 0 deletions
diff --git a/console/Makefile b/console/Makefile new file mode 100644 index 00000000..b44c583d --- /dev/null +++ b/console/Makefile @@ -0,0 +1,34 @@ +# +# 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. + +dir := console +makemode := server + +target = console +SRCS = main.c console.c focus.c vga-display.c vga.c dynafont.c bdf.c +LCLHDRS = focus.h input.h console.h display.h vga.h vga-hw.h dynafont.h bdf.h \ + mutations.h priv.h + +HURDLIBS = trivfs fshelp iohelp threads ports ihash shouldbeinlibc +OBJS = $(subst .c,.o,$(SRCS)) tioctlServer.o + +MIGSFLAGS += -imacros $(srcdir)/mutations.h + +include ../Makeconf diff --git a/console/bdf.c b/console/bdf.c new file mode 100644 index 00000000..2e9e02bd --- /dev/null +++ b/console/bdf.c @@ -0,0 +1,986 @@ +/* Adobe Glyph Bitmap Distribution Format (BDF) parser. + Copyright (C) 2002 Free Software Foundation, Inc. + Written by Marcus Brinkmann <marcus@gnu.org>. + + 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 <config.h> +#endif +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <malloc.h> +#include <ctype.h> +#include <stdarg.h> +#include <search.h> + +#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' \ + : (((c) >= 'A' && (c) <= 'F') ? (c) - 'A' : 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; + 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/bdf.h b/console/bdf.h new file mode 100644 index 00000000..968c9f59 --- /dev/null +++ b/console/bdf.h @@ -0,0 +1,272 @@ +/* bdf.h - Interface to Adobe Glyph Bitmap Distribution Format (BDF) parser. + Copyright (C) 2002 Free Software Foundation, Inc. + Written by Marcus Brinkmann <marcus@gnu.org>. + + 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 <stdio.h> + +/* 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 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/console.c b/console/console.c new file mode 100644 index 00000000..ac7c807d --- /dev/null +++ b/console/console.c @@ -0,0 +1,511 @@ +/* console.c - The device independant part of a console. + 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 <errno.h> +#include <assert.h> +#include <string.h> +#include <cthreads.h> + +#include "console.h" +#include "display.h" +#include "input.h" + + +struct vcons +{ + /* Protected by cons_list_lock. */ + vcons_t next; + vcons_t prev; + int refcnt; + + /* The following members remain constant over the lifetime of the + object and don't need to be locked. */ + cons_t cons; + int id; + void *display_console; + + struct mutex lock; + /* Indicates if OWNER_ID is initialized. */ + int has_owner; + /* Specifies the ID of the process that should receive the WINCH + signal for this virtual console. */ + int owner_id; + + /* The output queue holds the characters that are to be outputted. + The display driver might refuse to handle some incomplete + multi-byte or composed character at the end of the buffer, so we + have to keep them around. */ + struct mutex output_lock; + int output_stopped; + struct condition output_resumed; + char *output; + size_t output_allocated; + size_t output_size; + + struct mutex input_lock; + /* XXX input queue. */ + char *input; + size_t input_allocated; + size_t input_size; +}; + + +struct cons +{ + /* Protected by cons_list_lock. */ + cons_t prev; + cons_t next; + int refcnt; + vcons_t vcons_list; + size_t vcons_length; + vcons_t vcons_active; + + /* The following members are static and don't need to be locked. */ + char *name; + display_ops_t display_ops; +}; + + +/* The lock protects the console list, all virtual console lists of + all consoles, and their reference counters. */ +struct mutex cons_list_lock; +cons_t cons_list; +size_t cons_length; + + +struct mutex config_lock; +/* The default encoding. */ +const char *config_encoding; +display_ops_t config_display; + + +/* Lookup the console with name NAME, acquire a reference for it, and + return it in R_CONS. If NAME doesn't exist, return ESRCH. */ +error_t +cons_lookup (const char *name, cons_t *r_cons) +{ + cons_t cons; + + mutex_lock (&cons_list_lock); + cons = cons_list; + while (cons) + { + if (!strcmp (name, cons->name)) + { + cons->refcnt++; + mutex_unlock (&cons_list_lock); + *r_cons = cons; + return 0; + } + cons = cons->next; + } + mutex_unlock (&cons_list_lock); + return ESRCH; +} + + +/* Release a reference to CONS. */ +void +cons_release (cons_t cons) +{ + mutex_lock (&cons_list_lock); + cons->refcnt--; + mutex_unlock (&cons_list_lock); +} + + +/* Lookup the virtual console with number ID in the console CONS, + acquire a reference for it, and return it in R_VCONS. If CREATE is + true, the virtual console will be created if it doesn't exist yet. + If CREATE is true, and ID 0, the first free virtual console id is + used. */ +error_t +vcons_lookup (cons_t cons, int id, int create, vcons_t *r_vcons) +{ + error_t err; + vcons_t previous_cons = 0; + vcons_t vcons; + + if (!id && !create) + return EINVAL; + + mutex_lock (&cons_list_lock); + if (id) + { + if (cons->vcons_list && cons->vcons_list->id <= id) + { + previous_cons = cons->vcons_list; + while (previous_cons->next && previous_cons->next->id <= id) + previous_cons = previous_cons->next; + if (previous_cons->id == id) + { + previous_cons->refcnt++; + mutex_unlock (&cons_list_lock); + *r_vcons = previous_cons; + return 0; + } + else if (!create) + { + mutex_unlock (&cons_list_lock); + return ESRCH; + } + } + } + else + { + id = 1; + if (cons->vcons_list && cons->vcons_list->id == 1) + { + previous_cons = cons->vcons_list; + while (previous_cons && previous_cons->id == id) + { + id++; + previous_cons = previous_cons->next; + } + } + } + + vcons = calloc (1, sizeof (struct vcons)); + if (!vcons) + { + mutex_unlock (&cons_list_lock); + return ENOMEM; + } + mutex_init (&vcons->lock); + mutex_init (&vcons->output_lock); + condition_init (&vcons->output_resumed); + vcons->refcnt = 1; + err = (*(cons->display_ops->create)) (&vcons->display_console, + config_encoding); + if (err) + { + mutex_unlock (&cons_list_lock); + free (vcons); + return err; + } + /* XXX Set up keyboard input etc. */ + vcons->cons = cons; + cons->refcnt++; + cons->vcons_length++; + /* Insert the virtual console into the doubly linked list. */ + if (previous_cons) + { + vcons->prev = previous_cons; + if (previous_cons->next) + { + previous_cons->next->prev = vcons; + vcons->next = previous_cons->next; + } + previous_cons->next = vcons; + } + else + { + if (cons->vcons_list) + { + cons->vcons_list->prev = vcons; + vcons->next = cons->vcons_list; + } + cons->vcons_list = vcons; + } + mutex_unlock (&cons_list_lock); + *r_vcons = vcons; + return 0; +} + + +/* Release a reference to the virtual console VCONS. If this was the + last reference the virtual console is destroyed. */ +void +vcons_release (vcons_t vcons) +{ + mutex_lock (&cons_list_lock); + if (--vcons->refcnt) + { + /* As we keep a reference for all input focus groups pointing to + the virtual console, and a reference for the active console, + we know that without references, this virtual console is + neither active nor used by any input group. */ + + (*vcons->cons->display_ops->destroy) (vcons->display_console); + /* XXX Destroy the rest of the state. */ + + if (vcons->prev) + vcons->prev->next = vcons->next; + if (vcons->next) + vcons->next->prev = vcons->prev; + vcons->cons->vcons_length--; + vcons->cons->refcnt--; + free (vcons); + } + mutex_unlock (&cons_list_lock); +} + + +/* Activate virtual console VCONS for WHO. WHO is a unique identifier + for the entity requesting the activation (which can be used by the + display driver to group several activation requests together). */ +void +vcons_activate (vcons_t vcons, int who) +{ + mutex_lock (&cons_list_lock); + if (vcons->cons->vcons_active != vcons) + { + (*vcons->cons->display_ops->activate) (vcons->display_console, who); + vcons->cons->vcons_active->refcnt--; + vcons->refcnt++; + vcons->cons->vcons_active = vcons; + } + mutex_unlock (&cons_list_lock); +} + + +/* Resume the output on the virtual console VCONS. */ +void +vcons_start_output (vcons_t vcons) +{ + mutex_lock (&vcons->output_lock); + if (vcons->output_stopped) + { + vcons->output_stopped = 0; + condition_broadcast (&vcons->output_resumed); + } + mutex_unlock (&vcons->output_lock); +} + + +/* Stop all output on the virtual console VCONS. */ +void +vcons_stop_output (vcons_t vcons) +{ + mutex_lock (&vcons->output_lock); + vcons->output_stopped = 1; + mutex_unlock (&vcons->output_lock); +} + + +/* Return the number of pending output bytes for VCONS. */ +size_t +vcons_pending_output (vcons_t vcons) +{ + int output_size; + mutex_lock (&vcons->output_lock); + output_size = vcons->output_size; + mutex_unlock (&vcons->output_lock); + return output_size; +} + + +/* Fush the input buffer, discarding all pending data. */ +void +vcons_flush_input (vcons_t vcons) +{ + mutex_lock (&vcons->input_lock); + vcons->input_size = 0; + mutex_unlock (&vcons->input_lock); +} + + +/* Flush the output buffer, discarding all pending data. */ +void +vcons_discard_output (vcons_t vcons) +{ + mutex_lock (&vcons->output_lock); + vcons->output_size = 0; + mutex_unlock (&vcons->output_lock); +} + + +/* Output DATALEN characters from the buffer DATA on the virtual + console VCONS. The DATA must be supplied in the system encoding + configured for VCONS. The function returns the amount of bytes + written (might be smaller than DATALEN) or -1 and the error number + in errno. If NONBLOCK is not zero, return with -1 and set errno + to EWOULDBLOCK if operation would block for a long time. */ +ssize_t +vcons_output (vcons_t vcons, int nonblock, char *data, size_t datalen) +{ + error_t err; + char *output; + size_t output_size; + ssize_t amount; + + error_t ensure_output_buffer_size (size_t new_size) + { + /* Must be a power of two. */ +#define OUTPUT_ALLOCSIZE 32 + + if (vcons->output_allocated < new_size) + { + char *new_output; + new_size = (new_size + OUTPUT_ALLOCSIZE - 1) & ~(OUTPUT_ALLOCSIZE - 1); + new_output = realloc (vcons->output, new_size); + if (!new_output) + return ENOMEM; + vcons->output = new_output; + vcons->output_allocated = new_size; + } + return 0; + } + + mutex_lock (&vcons->output_lock); + while (vcons->output_stopped) + { + if (nonblock) + { + mutex_unlock (&vcons->output_lock); + errno = EWOULDBLOCK; + return -1; + } + if (hurd_condition_wait (&vcons->output_resumed, &vcons->output_lock)) + { + mutex_unlock (&vcons->output_lock); + errno = EINTR; + return -1; + } + } + + if (vcons->output_size) + { + err = ensure_output_buffer_size (vcons->output_size + datalen); + if (err) + { + mutex_unlock (&vcons->output_lock); + errno = ENOMEM; + return -1; + } + output = vcons->output; + output_size = vcons->output_size; + memcpy (output + output_size, data, datalen); + output_size += datalen; + } + else + { + output = data; + output_size = datalen; + } + amount = output_size; + err = (*vcons->cons->display_ops->output) (vcons->display_console, + &output, &output_size); + amount -= output_size; + + if (err && !amount) + { + mutex_unlock (&vcons->output_lock); + errno = err; + return err; + } + + /* XXX What should be done with invalid characters etc? */ + if (output_size) + { + /* If we used the caller's buffer DATA, the remaining bytes + might not fit in our internal output buffer. In this case we + can reallocate the buffer in VCONS without needing to update + OUTPUT (as it points into DATA). */ + err = ensure_output_buffer_size (output_size); + if (err) + { + mutex_unlock (&vcons->output_lock); + return err; + } + memmove (vcons->output, output, output_size); + } + vcons->output_size = output_size; + amount += output_size; + + mutex_unlock (&vcons->output_lock); + return amount; +} + + +/* Add DATALEN bytes starting from DATA to the input queue in + VCONS. */ +error_t +vcons_input (vcons_t vcons, char *data, size_t datalen) +{ + error_t err = 0; + + error_t ensure_input_buffer_size (size_t new_size) + { + /* Must be a power of two. */ +#define INPUT_ALLOCSIZE 32 + + if (vcons->input_allocated < new_size) + { + char *new_input; + new_size = (new_size + INPUT_ALLOCSIZE - 1) & ~(INPUT_ALLOCSIZE - 1); + new_input = realloc (vcons->input, new_size); + if (!new_input) + return ENOMEM; + vcons->input = new_input; + vcons->input_allocated = new_size; + } + return 0; + } + + mutex_lock (&vcons->input_lock); + err = ensure_input_buffer_size (vcons->input_size + datalen); + if (err) + { + mutex_unlock (&vcons->input_lock); + return err; + } + memcpy (vcons->input + vcons->input_size, data, datalen); + vcons->input_size += datalen; + mutex_unlock (&vcons->input_lock); + return 0; +} + + +/* Return the dimension of the virtual console VCONS in WINSIZE. */ +void +vcons_getsize (vcons_t vcons, struct winsize *size) +{ + (*vcons->cons->display_ops->getsize) (vcons->display_console, size); +} + + +/* Set the owner of the virtual console VCONS to PID. The owner + receives the SIGWINCH signal when the terminal size changes. */ +error_t +vcons_set_owner (vcons_t vcons, pid_t pid) +{ + mutex_lock (&vcons->lock); + vcons->has_owner = 1; + vcons->owner_id = pid; + mutex_unlock (&vcons->lock); + return 0; +} + + +/* Return the owner of the virtual console VCONS in PID. If there is + no owner, return ENOTTY. */ +error_t +vcons_get_owner (vcons_t vcons, pid_t *pid) +{ + error_t err = 0; + mutex_lock (&vcons->lock); + if (!vcons->has_owner) + err = ENOTTY; + else + *pid = vcons->owner_id; + mutex_unlock (&vcons->lock); + return err; +} diff --git a/console/console.h b/console/console.h new file mode 100644 index 00000000..b768afa4 --- /dev/null +++ b/console/console.h @@ -0,0 +1,98 @@ +/* console.h - Interface for the device independant part of a virtual console. + 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 <errno.h> +#include <argp.h> +#include <sys/types.h> +#include <sys/ioctl.h> + + +/* A handle for a console device. */ +typedef struct cons *cons_t; + +/* A handle for a virtual console device. */ +typedef struct vcons *vcons_t; + + +/* Lookup the console with name NAME, acquire a reference for it, and + return it in R_CONS. If NAME doesn't exist, return ESRCH. */ +error_t cons_lookup (const char *name, cons_t *r_cons); + +/* Release a reference to CONS. */ +void cons_release (cons_t cons); + +/* Lookup the virtual console with number ID in the console CONS, + acquire a reference for it, and return it in R_VCONS. If CREATE is + true, the virtual console will be created if it doesn't exist yet. + If CREATE is true, and ID 0, the first free virtual console id is + used. */ +error_t vcons_lookup (cons_t cons, int id, int create, vcons_t *r_vcons); + +/* Release a reference to the virtual console VCONS. If this was the + last reference the virtual console is destroyed. */ +void vcons_release (vcons_t vcons); + + +/* Activate virtual console VCONS for WHO. WHO is a unique identifier + for the entity requesting the activation (which can be used by the + display driver to group several activation requests together). */ +void vcons_activate (vcons_t vcons, int who); + +/* Resume the output on the virtual console VCONS. */ +void vcons_start_output (vcons_t vcons); + +/* Stop all output on the virtual console VCONS. */ +void vcons_stop_output (vcons_t vcons); + +/* Return the number of pending output bytes for VCONS. */ +size_t vcons_pending_output (vcons_t vcons); + +/* Fush the input buffer, discarding all pending data. */ +void vcons_flush_input (vcons_t vcons); + +/* Fush the output buffer, discarding all pending data. */ +void vcons_discard_output (vcons_t vcons); + +/* Add DATALEN bytes starting from DATA to the input queue in + VCONS. */ +error_t vcons_input (vcons_t vcons, char *data, size_t datalen); + +/* Output DATALEN characters from the buffer DATA on the virtual + console VCONS. The DATA must be supplied in the system encoding + configured for VCONS. The function returns the amount of bytes + written (might be smaller than DATALEN) or -1 and the error number + in errno. If NONBLOCK is not zero, return with -1 and set errno + to EWOULDBLOCK if operation would block for a long time. */ +ssize_t vcons_output (vcons_t vcons, int nonblock, char *data, size_t datalen); + +/* Hang interruptible until one of the conditions in TYPE is + fulfilled. Return the conditions fulfilled in TYPE. */ +error_t vcons_select (vcons_t vcons, int *type); + +/* Return the dimension of the virtual console VCONS in WINSIZE. */ +void vcons_getsize (vcons_t vcons, struct winsize *winsize); + +/* Set the owner of the virtual console VCONS to PID. The owner + receives the SIGWINCH signal when the terminal size changes. */ +error_t vcons_set_owner (vcons_t vcons, pid_t pid); + +/* Return the owner of the virtual console VCONS in PID. If there is + no owner, return ENOTTY. */ +error_t vcons_get_owner (vcons_t vcons, pid_t *pid); diff --git a/console/display.h b/console/display.h new file mode 100644 index 00000000..803d3180 --- /dev/null +++ b/console/display.h @@ -0,0 +1,79 @@ +/* display.h - The interface to 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 <errno.h> +#include <argp.h> +#include <sys/ioctl.h> + + +struct display_ops +{ + /* Initialize the subsystem. */ + error_t (*init) (void); + + /* Create a new (virtual) console display, with the system encoding + being ENCODING. A failure at the first time this is called is + critical. Subsequent calls might return an error if multiple + virtual consoles are not supported. Further operations on this + console will be called with the first parameter being *HOOK, + which should be set to some unique identifier for this + console. */ + error_t (*create) (void **console, const char *encoding); + + /* Destroy the console CONSOLE. The caller will first activate a + different console as the active one. */ + void (*destroy) (void *console); + + /* Change the active console of WHO to CONSOLE. WHO is a unique identifier + for the entity requesting the activation (which can be used by the + display driver to group several activation requests together). */ + void (*activate) (void *console, int who); + + /* Scroll the console CONSOLE by the desired amount. This is only a + hint, the actual amount scrolled might depend on the capability + of the subsystem. Negative AMOUNT scrolls back in history. */ + error_t (*scroll) (void *console, int amount); + + /* Output LENGTH bytes starting from BUFFER in the system encoding. + Set BUFFER and LENGTH to the new values. The exact semantics are + just as in the iconv interface. */ + error_t (*output) (void *console, char **buffer, size_t *length); + + /* Return the current size of CONSOLE in WINSIZE. */ + void (*getsize) (void *console, struct winsize *size); +}; +typedef struct display_ops *display_ops_t; + +extern struct display_ops vga_display_ops; +extern struct display_ops mach_display_ops; + +display_ops_t display_driver[] = + { +#if defined(__i386__) + &vga_display_ops, +#endif + &mach_display_ops, + 0 + }; + +#endif /* _DISPLAY_H_ */ diff --git a/console/dynafont.c b/console/dynafont.c new file mode 100644 index 00000000..588457e9 --- /dev/null +++ b/console/dynafont.c @@ -0,0 +1,371 @@ +/* dynafont.c - Dynamic font handling. + 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 <assert.h> +#include <malloc.h> +#include <wchar.h> +#include <hurd/ihash.h> + +#include "vga-hw.h" +#include "vga.h" +#include "bdf.h" +#include "dynafont.h" + + +/* The currently active (visible) dynafont. */ +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. */ + 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 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; + + /* The font memory as stored on the card. */ + vga_font_glyph *vga_font; +}; + + +/* 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, int size, dynafont_t *dynafont) +{ + error_t err = 0; + dynafont_t df; + struct bdf_glyph *glyph = NULL; + + df = malloc (sizeof *df); + if (!df) + return ENOMEM; + + df->font = font; + df->size = size; + 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; + } + df->vga_font_free_indices = df->size; + df->vga_font_last_free_index = 0; + + /* The encoding of the unknown glyph. Some fonts provide an empty + box for this encoding. Undefine this if you always want to use + the built-in font. XXX Make this a command line option. */ +#define ENCODING_UNKNOWN 0 + /* The encoding of the space. All fonts should provide it. */ +#define ENCODING_SPACE 32 + + { + struct mapped_character *chr = &df->charmap_data[FONT_INDEX_UNKNOWN]; + df->vga_font_free_indices--; + chr->refs = 1; + +#if defined(ENCODING_UNKNOWN) + glyph = bdf_find_glyph (df->font, ENCODING_UNKNOWN, 0); + if (!glyph) + glyph = bdf_find_glyph (df->font, -1, ENCODING_UNKNOWN); +#endif + if (glyph) + { + /* XXX Take glyph size into account. */ + memcpy (df->vga_font[FONT_INDEX_UNKNOWN], glyph->bitmap, 32); + /* Update the hash table. */ + ihash_add (df->charmap, ENCODING_UNKNOWN, chr, &chr->locp); + } + else + { + int i; + char *gl = df->vga_font[FONT_INDEX_UNKNOWN]; + /* XXX Take font height into account. */ + gl[0] = 0xFF; /* ******** */ + gl[1] = 0xC3; /* ** ** */ + gl[2] = 0x99; /* * ** * */ + gl[3] = 0x99; /* * ** * */ + gl[4] = 0xF9; /* ***** * */ + gl[5] = 0xF3; /* **** ** */ + gl[6] = 0xF3; /* **** ** */ + gl[7] = 0xE7; /* *** *** */ + gl[8] = 0xE7; /* *** *** */ + gl[9] = 0xFF; /* ******** */ + gl[10] = 0xE7; /* *** *** */ + gl[11] = 0xFF; /* ******** */ + for (i = 12; i < 32; i++) + gl[i] = 0; + } + +#if defined(ENCODING_SPACE) + glyph = bdf_find_glyph (df->font, ENCODING_SPACE, 0); + if (!glyph) + glyph = bdf_find_glyph (df->font, -1, ENCODING_SPACE); +#endif + if (glyph) + { + /* XXX Take glyph size into account. */ + memcpy (df->vga_font[FONT_INDEX_SPACE], glyph->bitmap, 32); + /* Update the hash table. */ + ihash_add (df->charmap, ENCODING_SPACE, chr, &chr->locp); + } + else + { + int i; + char *gl = df->vga_font[FONT_INDEX_SPACE]; + for (i = 0; i < 32; i++) + gl[i] = 0; + } + } + *dynafont = df; + return err; +} + + +/* Release a dynafont object and its associated resources. */ +void +dynafont_free (dynafont_t df) +{ + ihash_free (df->charmap); + free (df->charmap_data); + free (df->vga_font); + free (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, wchar_t wide_chr) +{ + struct mapped_character *chr = ihash_find (df->charmap, (int) wide_chr); + + if (chr) + { + if (!chr->refs++) + df->vga_font_free_indices--; + return (chr - df->charmap_data) / sizeof (struct mapped_character); + } + + /* The character is not currently mapped. Look for an empty + slot and add it. */ + if (df->vga_font_free_indices) + { + int pos = (df->vga_font_last_free_index + 1) % df->size; + struct bdf_glyph *glyph; + + glyph = bdf_find_glyph (df->font, (int) wide_chr, 0); + if (!glyph) + glyph = bdf_find_glyph (df->font, -1, (int) wide_chr); + if (glyph) + { + while (pos != df->vga_font_last_free_index) + { + if (df->charmap_data[pos].refs == 0) + { + /* Ok, we found a new entry, use it. */ + df->vga_font_last_free_index = pos; + chr = &df->charmap_data[pos]; + chr->refs = 1; + memcpy (df->vga_font[pos], glyph->bitmap, VGA_FONT_HEIGHT); + /* XXX This will have to be updated when fonts are + cached. */ + if (active_dynafont == df) + vga_write_font_buffer (0, pos, (char *) glyph->bitmap, + VGA_FONT_HEIGHT); + /* Update the hash table. */ + if (chr->locp) + ihash_locp_remove (df->charmap, chr->locp); + ihash_add (df->charmap, (int) wide_chr, chr, &chr->locp); + return pos; + } + pos = (pos + 1) % df->size; + } + /* Should never be reached. */ + assert (!"No free vga font index."); + } + } + + df->charmap_data[FONT_INDEX_UNKNOWN].refs++; + return FONT_INDEX_UNKNOWN; +} + + +/* 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) + df->vga_font_free_indices++; +} + + +/* Load the VGA font to the card and make it active. */ +void +dynafont_activate (dynafont_t df) +{ + /* XXX Replace this with some caching method. We have eight font + slots available. */ + 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); + active_dynafont = df; +} + + +/* 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 +#ifndef ENCODING_SPACE + if (i == FONT_INDEX_SPACE) + 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 +#ifdef ENCODING_SPACE + /* The new font doesn't have a representation for the + blank glyph at the desired place, so keep the old + one. XXX Should load the built-in one here. */ + if (i == FONT_INDEX_SPACE) + continue; +#endif + memcpy (df->vga_font[i], df->vga_font[FONT_INDEX_UNKNOWN], + 32); + } + else + /* XXX Take font size and glyph size into account. */ + memcpy (df->vga_font[i], glyph->bitmap, 32); + } + } + + /* XXX This will have to be changed when we use font caching. */ + if (active_dynafont == df) + vga_write_font_buffer (0, 0, (char *) df->vga_font, + df->size * VGA_FONT_HEIGHT); +} diff --git a/console/dynafont.h b/console/dynafont.h new file mode 100644 index 00000000..8eb8d3dc --- /dev/null +++ b/console/dynafont.h @@ -0,0 +1,74 @@ +/* dynafont.h - Interfaces for the dynamic font handling. + 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 <wchar.h> + +#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; + +/* Some glyphs are always available at a fixed location. This + includes the representation for the unknown glyph and the blank. + Conveniently, the blank is located at its position in ASCII. */ +#define FONT_INDEX_UNKNOWN 0 +#define FONT_INDEX_SPACE 32 + +/* 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, 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, wchar_t wide_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); + +/* 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); diff --git a/console/focus.c b/console/focus.c new file mode 100644 index 00000000..7eedbd6a --- /dev/null +++ b/console/focus.c @@ -0,0 +1,110 @@ +#include <argp.h> +#include <cthreads.h> + +#include "console.h" +#include "input.h" +#include "focus.h" + +struct argp_option focus_argp_options[] = + { + { "focus-group", 'f', "NAME", OPTION_ARG_OPTIONAL, "Start a new focus group" }, + { "input-device", 'i', "NAME", 0, "Add input device NAME to focus group" }, + { "focus", 'F', "CONS[/VCONS]", 0, "Put focus on console CONS, and virtual console VCONS within if specified" }, + { 0 } + }; + +struct +{ + focus_t focus; +} focus_config; + +static error_t +parse_opt (int opt, char *arg, struct argp_state *state) +{ + switch (opt) + { + default: + return ARGP_ERR_UNKNOWN; + case ARGP_KEY_INIT: + case ARGP_KEY_SUCCESS: + case ARGP_KEY_ERROR: + break; + case 'f': + /* Start a new focus group. */ + focus_config.focus = 0; + break; + case 'i': + { + /* Start a new input device. */ + int found = 0; + int i = 0; + while (input_driver[i]) + { + // if (state->child_inputs[i]) + // { + // input_driver->end_config + if (! strcmp (input_driver[i]->name, arg)) + { + found = 1; + state->child_inputs[i] = (void *) 1; + } + else + state->child_inputs[i] = NULL; + } + if (!found) + argp_error (state, "unknown input device %s", arg); + } + } + return 0; +} + +struct argp focus_argp = + { focus_argp_options, parse_opt, 0, 0, 0 /* XXX input_argp_childs */}; + +typedef struct input *input_t; +struct input +{ + input_t next; +}; + +struct focus +{ + struct mutex lock; + + /* The virtual console receiving our input characters. */ + vcons_t vcons; + + /* The list of input devices. */ + input_t input_list; +}; + + +error_t +focus_create (focus_t *r_focus) +{ + focus_t focus = calloc (1, sizeof (*focus)); + if (!focus) + return ENOMEM; + mutex_init (&focus->lock); + + return 0; +} + + +void +focus_add (focus_t focus, input_t input) +{ + mutex_lock (&focus->lock); + input->next = focus->input_list; + focus->input_list = input; + mutex_unlock (&focus->lock); +} + +void +focus_switch_to (focus_t focus, vcons_t vcons) +{ + mutex_lock (&focus->lock); + vcons_activate (vcons, (int) focus); + focus->vcons = vcons; + mutex_unlock (&focus->lock); +} diff --git a/console/focus.h b/console/focus.h new file mode 100644 index 00000000..bd9d8e1c --- /dev/null +++ b/console/focus.h @@ -0,0 +1,31 @@ +/* focus.h - The interface to an input group. + 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 _FOCUS_H_ +#define _FOCUS_H_ 1 + +typedef struct focus *focus_t; + +extern struct argp focus_argp; + +#define FOCUS_ARGP_CHILD \ + { &focus_argp, 0, "Multiple input devices in the same group share focus" } + +#endif /* _FOCUS_H_ */ diff --git a/console/input.h b/console/input.h new file mode 100644 index 00000000..e660fa22 --- /dev/null +++ b/console/input.h @@ -0,0 +1,62 @@ +/* input.h - The interface to 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 <errno.h> +#include <argp.h> + +#include "focus.h" + + +struct input_ops +{ + const char *name; + + /* Fill in ARGP with the argp_child structure for this display. */ + error_t (*argp) (struct argp_child *argp); + + /* Initialize the subsystem. */ + error_t (*init) (void); + + /* Assign the input device to the focus group FOCUS. */ + error_t (*set_focus) (focus_t *focus); + + /* Output LENGTH bytes starting from BUFFER in the system encoding. + Set BUFFER and LENGTH to the new values. The exact semantics are + just as in the iconv interface. */ + error_t (*input) (char **buffer, size_t *length); +}; +typedef struct input_ops *input_ops_t; + +extern struct input_ops pckbd_input_ops; +extern struct input_ops mach_input_ops; + +input_ops_t input_driver[] = + { +#if defined (__i386__) + &pckbd_input_ops, +#endif + &mach_input_ops, + 0 + }; + +#endif diff --git a/console/main.c b/console/main.c new file mode 100644 index 00000000..b571b580 --- /dev/null +++ b/console/main.c @@ -0,0 +1,461 @@ +/* main.c - The main routine of the console server. + 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 <hurd.h> +#include <fcntl.h> +#include <hurd/trivfs.h> +#include <stdio.h> +#include <argp.h> +#include <hurd/fsys.h> +#include <string.h> + +#include <version.h> + +#include "priv.h" +#include "tioctl_S.h" +#include "console.h" +#include "focus.h" + + +const char *argp_program_version = STANDARD_HURD_VERSION (console); + +int trivfs_fstype = FSTYPE_DEV; +int trivfs_fsid = 0; +int trivfs_support_read = 1; +int trivfs_support_write = 1; +int trivfs_support_exec = 0; + +int trivfs_allow_open = O_READ|O_WRITE; + +/* Properties of the underlying node. */ +int console_mode; +int console_owner; +int console_group; + +/* The argument line options. */ +struct +{ + char *encoding; + int rdev; +} main_config; + +static struct argp_option options[] = + { + {"rdev", 'n', "ID", 0, + "The stat rdev number for this node; may be either a" + " single integer, or of the form MAJOR,MINOR"}, + {"encoding", 'e', "NAME", 0, + "The encoding to use for input and output"}, + {0} + }; + +static struct argp_child argp_childs[] = + { + FOCUS_ARGP_CHILD, + /* XXX CONSOLE_ARGP_CHILD, */ + { 0 } + }; + +static error_t +parse_opt (int opt, char *arg, struct argp_state *state) +{ + switch (opt) + { + default: + return ARGP_ERR_UNKNOWN; + case ARGP_KEY_INIT: + case ARGP_KEY_SUCCESS: + case ARGP_KEY_ERROR: + break; + case 'n': + { + char *start = arg; + char *end; + int rdev; + + rdev = strtoul (start, &end, 0); + if (*end == ',') + /* MAJOR,MINOR form */ + { + start = end; + rdev = (rdev << 8) + strtoul (start, &end, 0); + } + + if (end == start || *end != '\0') + { + argp_error (state, "%s: Invalid argument to --rdev", arg); + return EINVAL; + } + main_config.rdev = rdev; + } + break; + } + return 0; +} + +static struct argp main_argp = + { options, parse_opt, 0, "A translator providing a console device.\v"\ + "The translator provides access to a hardeware console usable\n"\ + "by the HURDIO backend of the term translator.\n", argp_childs }; + + +int +main (int argc, char **argv) +{ + error_t err; + mach_port_t bootstrap; + struct trivfs_control *fsys; + struct stat st; + + argp_parse (&main_argp, argc, argv, 0, 0, 0); + + task_get_bootstrap_port (mach_task_self (), &bootstrap); + if (bootstrap == MACH_PORT_NULL) + { + fprintf (stderr, "Must be started as a translator\n"); + exit (1); + } + + /* Set our node */ + err = trivfs_startup (bootstrap, 0, 0, 0, 0, 0, &fsys); + mach_port_deallocate (mach_task_self (), bootstrap); + if (err) + { + perror ("Starting translator"); + exit (1); + } + + /* Initialize status from underlying node. */ + err = io_stat (fsys->underlying, &st); + if (err) + { + /* We cannot stat the underlying node. Fallback to the defaults. */ + console_owner = console_group = 0; + console_mode = S_IRUSR | S_IWUSR; + err = 0; + } + else + { + console_owner = st.st_uid; + console_group = st.st_gid; + console_mode = (st.st_mode & ACCESSPERMS); + } + console_mode |= S_IFCHR | S_IROOT; + + /* Launch. */ + ports_manage_port_operations_multithread (fsys->pi.bucket, trivfs_demuxer, + 0, 0, 0); + + return 0; +} + + +kern_return_t +S_tioctl_tiocflush (struct trivfs_protid *cred, int queue_selector) +{ + vcons_t vcons; + + if (!cred) + return EOPNOTSUPP; + if (!(cred->po->openmodes & (O_READ | O_WRITE))) + return EBADF; + + vcons = (vcons_t) cred->po->hook; + if (!queue_selector) + queue_selector = O_READ | O_WRITE; + + if (queue_selector & O_READ) + vcons_flush_input (vcons); + if (queue_selector & O_WRITE) + vcons_discard_output (vcons); + + return 0; +} + + +kern_return_t +S_tioctl_tiogwinsz (struct trivfs_protid *cred, struct winsize *size) +{ + vcons_t vcons; + + if (!cred) + return EOPNOTSUPP; + + vcons = (vcons_t) cred->po->hook; + vcons_getsize (vcons, size); + return 0; +} + + +kern_return_t +S_tioctl_tiocstart (struct trivfs_protid *cred) +{ + vcons_t vcons; + + if (!cred) + return EOPNOTSUPP; + if (!(cred->po->openmodes & (O_READ | O_WRITE))) + return EBADF; + + vcons = (vcons_t) cred->po->hook; + vcons_start_output (vcons); + return 0; +} + + +kern_return_t +S_tioctl_tiocstop (struct trivfs_protid *cred) +{ + vcons_t vcons; + + if (!cred) + return EOPNOTSUPP; + if (!(cred->po->openmodes & (O_READ | O_WRITE))) + return EBADF; + + vcons = (vcons_t) cred->po->hook; + vcons_stop_output (vcons); + return 0; +} + + +kern_return_t +S_tioctl_tiocoutq (struct trivfs_protid *cred, int *queue_size) +{ + vcons_t vcons; + + if (!cred) + return EOPNOTSUPP; + if (!(cred->po->openmodes & (O_READ | O_WRITE))) + return EBADF; + + vcons = (vcons_t) cred->po->hook; + *queue_size = vcons_pending_output (vcons); + return 0; +} + + +kern_return_t +S_tioctl_tiocspgrp (struct trivfs_protid *cred, int pgrp) +{ + vcons_t vcons; + + if (!cred) + return EOPNOTSUPP; + if (!(cred->po->openmodes & (O_READ | O_WRITE))) + return EBADF; + + vcons = (vcons_t) cred->po->hook; + vcons_set_owner (vcons, -pgrp); + return 0; +} + + +kern_return_t +S_tioctl_tiocgpgrp (struct trivfs_protid *cred, int *pgrp) +{ + error_t err; + vcons_t vcons; + + if (!cred) + return EOPNOTSUPP; + if (!(cred->po->openmodes & (O_READ | O_WRITE))) + return EBADF; + + vcons = (vcons_t) cred->po->hook; + err = vcons_get_owner (vcons, pgrp); + if (!err) + *pgrp = -*pgrp; + + return err; +} + + +error_t +trivfs_S_file_set_size (struct trivfs_protid *cred, off_t size) +{ + if (!cred) + return EOPNOTSUPP; + if (!(cred->po->openmodes & (O_READ | O_WRITE))) + return EBADF; + return 0; +} + + +error_t +trivfs_S_io_seek (struct trivfs_protid *cred, off_t off, int whence, + off_t *newp) +{ + return ESPIPE; +} + + +void +trivfs_modify_stat (struct trivfs_protid *cred, struct stat *st) +{ + st->st_blksize = 512; + st->st_ino = 0; + st->st_rdev = main_config.rdev; + st->st_mode = console_mode; + st->st_uid = console_owner; + st->st_gid = console_group; +} + + +/* Called for user writes to the console as described in + <hurd/io.defs>. */ +error_t +trivfs_S_io_write (struct trivfs_protid *cred, char *data, u_int datalen, + off_t offset, int *amount) +{ + error_t err = 0; + vcons_t vcons; + + if (!cred) + return EOPNOTSUPP; + if (! (cred->po->openmodes & O_WRITE)) + return EBADF; + + vcons = (vcons_t) cred->po->hook; + *amount = vcons_output (vcons, cred->po->openmodes & O_NONBLOCK, + data, datalen); + if (*amount == -1) + err = errno; + + return err; +} + + +/* Called for user reads from the console. */ +error_t +trivfs_S_io_read (struct trivfs_protid *cred, char **data, u_int *datalen, + off_t offset, int amount) +{ + if (!cred) + return EOPNOTSUPP; + if (! (cred->po->openmodes & O_READ)) + return EBADF; + + /* XXX */ + return EOPNOTSUPP; +} + + +error_t +trivfs_S_io_select (struct trivfs_protid *cred, int *type) +{ + error_t err = 0; + vcons_t vcons; + + if (!cred) + return EOPNOTSUPP; + + if (! (cred->po->openmodes & O_READ)) + *type &= ~SELECT_READ; + if (! (cred->po->openmodes & O_WRITE)) + *type &= ~SELECT_WRITE; + + vcons = (vcons_t) cred->po->hook; + if (type) + err = vcons_select (vcons, type); + return err; +} + + +error_t +trivfs_S_io_readable (struct trivfs_protid *cred, int *amt) +{ + if (!cred) + return EOPNOTSUPP; + if ((cred->po->openmodes & O_READ) == 0) + return EBADF; + + /* XXX */ + // *amt = qsize (inputq); + return 0; +} + + +kern_return_t +trivfs_S_io_get_openmodes (struct trivfs_protid *cred, int *bits) +{ + return EOPNOTSUPP; +} + + +error_t +trivfs_S_io_set_all_openmodes (struct trivfs_protid *cred, int bits) +{ + return EOPNOTSUPP; +} + + +error_t +trivfs_S_io_set_some_openmodes (struct trivfs_protid *cred, int bits) +{ + return EOPNOTSUPP; +} + + +error_t +trivfs_S_io_clear_some_openmodes (struct trivfs_protid *cred, int bits) +{ + return EOPNOTSUPP; +} + + +error_t +trivfs_S_io_mod_owner (struct trivfs_protid *cred, pid_t owner) +{ + vcons_t vcons; + + if (!cred) + return EOPNOTSUPP; + if (!(cred->po->openmodes & (O_READ | O_WRITE))) + return EBADF; + + vcons = (vcons_t) cred->po->hook; + vcons_set_owner (vcons, owner); + return 0; +} + + +error_t +trivfs_S_io_get_owner (struct trivfs_protid *cred, pid_t *owner) +{ + error_t err; + vcons_t vcons; + + if (!cred) + return EOPNOTSUPP; + if (!(cred->po->openmodes & (O_READ | O_WRITE))) + return EBADF; + + vcons = (vcons_t) cred->po->hook; + err = vcons_get_owner (vcons, owner); + return err; +} + + +error_t +trivfs_goaway (struct trivfs_control *cntl, int flags) +{ + return EBUSY; +} diff --git a/console/mutations.h b/console/mutations.h new file mode 100644 index 00000000..396bf1e5 --- /dev/null +++ b/console/mutations.h @@ -0,0 +1,26 @@ +/* mutations.h - Automagic type transformation for MiG interfaces. + 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. */ + +/* Only CPP macro definitions should go in this file. */ + +#define IO_INTRAN trivfs_protid_t trivfs_begin_using_protid (io_t) +#define IO_DESTRUCTOR trivfs_end_using_protid (trivfs_protid_t) + +#define IO_IMPORTS import "priv.h"; diff --git a/console/priv.h b/console/priv.h new file mode 100644 index 00000000..98e61bdf --- /dev/null +++ b/console/priv.h @@ -0,0 +1 @@ +typedef struct trivfs_protid *trivfs_protid_t; diff --git a/console/vga-display.c b/console/vga-display.c new file mode 100644 index 00000000..6b7dc146 --- /dev/null +++ b/console/vga-display.c @@ -0,0 +1,832 @@ +/* vga-display.c - The VGA device dependant part of a (virtual) console. + 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 <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <iconv.h> +#include <argp.h> + +#include <sys/io.h> +#include <sys/mman.h> +#include <sys/fcntl.h> +#include <cthreads.h> + +#include "vga.h" +#include "dynafont.h" +#include "display.h" + + +struct vga_display_console +{ + /* The lock for the vga display console structure. */ + struct mutex lock; + + /* The state of the conversion of output characters. */ + iconv_t cd; + + /* The parsing state of output characters, needed to handle escape + character sequences. */ + enum + { + STATE_NORMAL = 0, + /* An escape character has just been parsed. */ + STATE_ESC, + STATE_ESC_BRACKET_INIT, + STATE_ESC_BRACKET, + STATE_ESC_BRACKET_QUESTION + } parse_state; + + /* How many parameters an escape sequence may have. */ +#define PARSE_MAX_PARAMS 10 + int parse_params[PARSE_MAX_PARAMS]; + int parse_nparams; + + /* The VGA font for this virtual console. */ + dynafont_t df; + + /* The complete video buffer, including the scroll back buffer. */ + char *video_buffer; + + /* The size of the video buffer, in lines. */ + int video_buffer_lines; + + /* The top most line of the screen in the video buffer. */ + int current_line; + + /* The number of lines scrolled back. */ + int scrolling; + + /* Maximum number of lines scrolled back. */ + int scrolling_max; + + /* True if the cursor is not displayed. */ + int cursor_off; + + int width; + int height; + int cursor_x; + int cursor_y; + /* Current attribute. */ + char attr; +}; + + +/* Protects the VGA hardware and all global variables, like the active + console. */ +static struct mutex vga_lock; + +/* The currently active console. */ +static struct vga_display_console *active_console; + + +/* Initialize the subsystem. */ +error_t +vga_display_init () +{ + mutex_init (&vga_lock); + return vga_init (); +} + + +/* Create a new (virtual) console display, with the system encoding + being ENCODING. A failure at the first time this is called is + critical. Subsequent calls might return an error if multiple + virtual consoles are not supported. Further operations on this + console will be called with the first parameter being *HOOK, which + should be set to some unique identifier for this console. */ +error_t +vga_display_create (void **console, const char *encoding) +{ + error_t err = 0; + struct vga_display_console *cons; + + cons = calloc (1, sizeof *cons); + if (cons) + return ENOMEM; + mutex_init (&cons->lock); + err = dynafont_new (0 /*XXX*/, 256, &cons->df); + if (err) + { + free (cons); + return err; + } + + cons->width = 80; + cons->height = 25; + cons->video_buffer_lines = 200; /* XXX For now. */ + cons->video_buffer = malloc (cons->video_buffer_lines * cons->width * 2); + if (!cons->video_buffer) + { + dynafont_free (cons->df); + free (cons); + return ENOMEM; + } + cons->current_line = 0; + /* XXX Fill out the first cons->height lines of the buffer here (or + later). */ + + /* WCHAR_T happens to be UCS-4 on the GNU system. */ + cons->cd = iconv_open ("WCHAR_T", encoding); + if (cons->cd == (iconv_t) -1) + { + err = errno; + dynafont_free (cons->df); + free (cons->video_buffer); + free (cons); + } + return err; +} + + +/* Destroy the console CONSOLE. The caller will first activate a + different console. */ +void +vga_display_destroy (void *console) +{ + struct vga_display_console *cons = console; + iconv_close (cons->cd); + dynafont_free (cons->df); + free (cons); +} + + +/* Update the cursor position on the screen. Expects CONS and the VGA + hardware to be locked and CONS to be the active console. */ +static void +vga_display_update_cursor (struct vga_display_console *cons) +{ + int pos; + + if (cons->cursor_off) + return; + + pos = cons->cursor_x + (cons->cursor_y + cons->scrolling) * cons->width; + + vga_set_cursor (pos); +} + + +/* Update the screen content. Expects CONS and the VGA hardware to be + locked and CONS to be the active console. XXX Maybe inline/macro. */ +static void +vga_display_update_screen (struct vga_display_console *cons) +{ + int start_line = cons->current_line - cons->scrolling; + int lines; + + if (start_line < 0) + start_line += cons->video_buffer_lines; + lines = cons->video_buffer_lines - start_line; + if (lines > cons->height) + lines = cons->height; + + memcpy (vga_videomem, cons->video_buffer + start_line * cons->width * 2, + lines * cons->width * 2); + if (lines < cons->height) + memcpy (vga_videomem, cons->video_buffer, + (cons->height - lines) * cons->width * 2); +} + + +/* Change the active console to CONSOLE. */ +void +vga_display_activate (void *console, int key) +{ + struct vga_display_console *cons = console; + + mutex_lock (&cons->lock); + mutex_lock (&vga_lock); + active_console = cons; + dynafont_activate (cons->df); + vga_display_update_screen (cons); + vga_display_update_cursor (cons); + vga_display_cursor (!cons->cursor_off); + mutex_unlock (&vga_lock); + mutex_unlock (&cons->lock); +} + + +/* Scroll the console CONSOLE by the desired amount. This is only a + hint, the actual amount scrolled might depend on the capability of + the subsystem. Negative AMOUNT scrolls back in history. */ +error_t +vga_display_scroll (void *console, int amount) +{ + struct vga_display_console *cons = console; + int old_scrolling; + int cursor_state_change = 0; + + mutex_lock (&cons->lock); + old_scrolling = cons->scrolling; + cons->scrolling -= amount; + if (cons->scrolling < 0) + cons->scrolling = 0; + else if (cons->scrolling > cons->scrolling_max) + cons->scrolling = cons->scrolling_max; + + if (old_scrolling != cons->scrolling) + { + if ((!cons->cursor_off + && cons->cursor_y + cons->scrolling >= cons->height) + || (cons->cursor_off + && cons->cursor_y + cons->scrolling < cons->height)) + { + cons->cursor_off = !cons->cursor_off; + cursor_state_change = 1; + } + + mutex_lock (&vga_lock); + if (active_console == cons) + { + /* XXX Replace this with a fast memmove in the video + memory. */ + vga_display_update_screen (cons); + if (cursor_state_change) + vga_display_cursor (!cons->cursor_off); + } + mutex_unlock (&vga_lock); + } + mutex_unlock (&cons->lock); + 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. */ +void +vga_display_change_font (void *console, bdf_font_t font) +{ + struct vga_display_console *cons = console; + mutex_lock (&cons->lock); + dynafont_change_font (cons->df, font); + mutex_unlock (&cons->lock); +} + + +#if 0 +static void +limit_cursor (struct emu *emu) +{ + if (emu->x >= emu->width) + emu->x = emu->width - 1; + else if (emu->x < 0) + emu->x = 0; + + if (emu->y >= emu->height) + emu->y = emu->height - 1; + else if (emu->y < 0) + emu->y = 0; +} + +static void +handle_esc_bracket_hl (struct emu *emu, int code, int flag) +{ + switch (code) + { + case 34: /* cursor standout: <cnorm>, <cvvis> */ + emu->cursor_standout = flag; + recalc_cursor (emu); + break; + } +} + +static void +handle_esc_bracket_m (struct emu *emu, int code) +{ + switch (code) + { + case 0: /* all attributes off: <sgr0> */ + emu->fg = emu->def_fg; + emu->bg = emu->def_bg; + emu->reverse = emu->bold = emu->blink = + emu->invisible = emu->dim = emu->underline = 0; + /* Cursor attributes aren't text attributes. */ + break; + case 1: /* bold on: <bold> */ + emu->bold = 1; + break; + case 2: /* dim on: <dim> */ + emu->dim = 1; + break; + case 4: /* underline on: <smul> */ + emu->underline = 1; + break; + case 5: /* blink on: <blink> */ + emu->blink = 1; + break; + case 7: /* reverse video on: <rev>, <smso> */ + emu->reverse = 1; + break; + case 8: /* concealed on: <invis> */ + emu->invisible = 1; + break; + case 21: /* bold off */ + emu->bold = 0; + break; + case 22: /* dim off */ + emu->dim = 0; + break; + case 24: /* underline off: <rmul> */ + emu->underline = 0; + break; + case 25: /* blink off */ + emu->blink = 0; + break; + case 27: /* reverse video off: <rmso> */ + emu->reverse = 0; + break; + case 28: /* concealed off */ + emu->invisible = 0; + break; + /* Case ranges are a GCC extension. */ + case 30 ... 37: /* set foreground color: <setaf> */ + emu->fg = code - 30; + break; + case 39: /* default foreground color; ANSI? */ + emu->fg = emu->def_fg; + break; + case 40 ... 47: /* set background color: <setab> */ + emu->bg = code - 40; + break; + case 49: /* default background color; ANSI? */ + emu->bg = emu->def_bg; + break; + } +} + +static void +handle_esc_bracket (struct emu *emu, char op) +{ + int i; + switch (op) + { + case 'H': case 'f': /* cursor position: <cup> */ + emu->x = emu->params[1] - 1; + emu->y = emu->params[0] - 1; + limit_cursor (emu); + break; + case 'G': /* horizontal position: <hpa> */ + emu->x = emu->params[0] - 1; + limit_cursor (emu); + break; + case 'F': /* beginning of previous line */ + emu->x = 0; + /* fall through */ + case 'A': /* cursor up: <cuu>, <cuu1> */ + /* (a ?: b) is a GCC extension meaning (a ? a : b). */ + emu->y -= (emu->params[0] ?: 1); + limit_cursor (emu); + break; + case 'E': /* beginning of next line */ + emu->x = 0; + /* fall through */ + case 'B': /* cursor down: <cud1>, <cud> */ + emu->y += (emu->params[0] ?: 1); + limit_cursor (emu); + break; + case 'C': /* cursor right: <cuf1>, <cuf> */ + emu->x += (emu->params[0] ?: 1); + limit_cursor (emu); + break; + case 'D': /* cursor left: <cub>, <cub1> */ + emu->x -= (emu->params[0] ?: 1); + limit_cursor (emu); + break; + case 's': /* save cursor position: <sc> */ + emu->saved_x = emu->x; + emu->saved_y = emu->y; + break; + case 'u': /* restore cursor position: <rc> */ + emu->x = emu->saved_x; + emu->y = emu->saved_y; + limit_cursor (emu); /* in case the screen was larger before */ + break; + case 'h': /* reset mode */ + for (i = 0; i < emu->nparams; ++i) + handle_esc_bracket_hl (emu, emu->params[i], 0); + break; + case 'l': /* set mode */ + for (i = 0; i < emu->nparams; ++i) + handle_esc_bracket_hl (emu, emu->params[i], 1); + break; + case 'm': + for (i = 0; i < emu->nparams; ++i) + handle_esc_bracket_m (emu, emu->params[i]); + recalc_attr (emu); + break; + case 'J': + switch (emu->params[0]) + { + case 0: /* clear to end of screen: <ed> */ + screen_fill (emu->screen, emu->x, emu->y, emu->width - emu->x, 1, + emu->attr | ' '); + screen_fill (emu->screen, 0, emu->y+1, + emu->width, emu->height - emu->y, + emu->attr | ' '); + break; + case 1: /* clear to beginning of screen */ + screen_fill (emu->screen, 0, 0, emu->width, emu->y, + emu->attr | ' '); + screen_fill (emu->screen, 0, emu->y, emu->x + 1, 1, + emu->attr | ' '); + break; + case 2: /* clear entire screen */ + screen_fill (emu->screen, 0, 0, emu->width, emu->height, + emu->attr | ' '); + break; + } + break; + case 'K': + switch (emu->params[0]) + { + case 0: /* clear to end of line: <el> */ + screen_fill (emu->screen, emu->x, emu->y, emu->width - emu->x, 1, + emu->attr | ' '); + break; + case 1: /* clear to beginning of line: <el1> */ + screen_fill (emu->screen, 0, emu->y, emu->x + 1, 1, + emu->attr | ' '); + break; + case 2: /* clear entire line */ + screen_fill (emu->screen, 0, emu->y, emu->width, 1, + emu->attr | ' '); + break; + } + break; + case 'L': /* insert line(s): <il1>, <il> */ + screen_scroll_down (emu->screen, 0, emu->y, + emu->width, emu->height - emu->y, + emu->params[0] ?: 1, emu->attr | ' '); + break; + case 'M': /* delete line(s): <dl1>, <dl> */ + screen_scroll_up (emu->screen, 0, emu->y, + emu->width, emu->height - emu->y, + emu->params[0] ?: 1, emu->attr | ' '); + break; + case '@': /* insert character(s): <ich1>, <ich> */ + screen_scroll_right (emu->screen, emu->x, emu->y, + emu->width - emu->x, 1, + emu->params[0] ?: 1, emu->attr | ' '); + break; + case 'P': /* delete character(s): <dch1>, <dch> */ + screen_scroll_left (emu->screen, emu->x, emu->y, + emu->width - emu->x, 1, + emu->params[0] ?: 1, emu->attr | ' '); + break; + case 'S': /* scroll up: <ind>, <indn> */ + screen_scroll_up (emu->screen, 0, 0, emu->width, emu->height, + emu->params[0] ?: 1, emu->attr | ' '); + break; + case 'T': /* scroll down: <ri>, <rin> */ + screen_scroll_down (emu->screen, 0, 0, emu->width, emu->height, + emu->params[0] ?: 1, emu->attr | ' '); + break; + case 'X': /* erase character(s): <ech> */ + screen_fill (emu->screen, emu->x, emu->y, emu->params[0] ?: 1, 1, + emu->attr | ' '); + break; + } +} + +static void +handle_esc_bracket_question_hl (struct emu *emu, int code, int flag) +{ + switch (code) + { + case 25: /* cursor invisibility: <civis>, <cnorm> */ + emu->cursor_invisible = flag; + recalc_cursor (emu); + break; + } +} + +static void +handle_esc_bracket_question (struct emu *emu, char op) +{ + int i; + switch (op) + { + case 'h': /* reset mode */ + for (i = 0; i < emu->nparams; ++i) + handle_esc_bracket_question_hl (emu, emu->params[i], 0); + break; + case 'l': /* set mode */ + for (i = 0; i < emu->nparams; ++i) + handle_esc_bracket_question_hl (emu, emu->params[i], 1); + break; + } +} +#endif + +/* Console must be locked. */ +static void +vga_display_output_one (struct vga_display_console *cons, wchar_t chr) +{ +#if 0 + switch (cons->parse_state) + { + case STATE_NORMAL: + switch (chr) + { + case '\r': + /* Carriage return: <cr>. */ + if (cons->cursor_x) + { + cons->cursor_x = 0; + mutex_lock (&vga_lock); + if (active_console == cons) + vga_display_update_cursor (cons); + mutex_unlock (&vga_lock); + } + break; + case '\n': + /* Cursor down: <cud1>, scroll up: <ind>. */ + if (cons->cursor_y < cons->height - 1) + { + cons->cursor_y++; + mutex_lock (&vga_lock); + if (active_console == cons) + vga_display_update_cursor (cons); + mutex_unlock (&vga_lock); + } + else + { + if (cons->current_line == cons->video_buffer_lines - 1) + cons->current_line = 0; + else + cons->current_line++; + /* XXX Empty out current line with spaces. */ + if (cons->scrolling_max + < cons->video_buffer_lines - cons->height) + cons->scrolling_max++; + mutex_lock (&vga_lock); + if (active_console == cons) + vga_display_update_screen (cons); + mutex_unlock (&vga_lock); + } + break; + case '\b': + /* Cursor backward: <cub1>. */ + if (cons->cursor_x > 0 || cons->cursor_y > 0) + { + if (cons->cursor_x > 0) + cons->cursor_x--; + else + { + /* XXX This implements the <bw> functionality. + The alternative is to cut off and set x to 0. */ + cons->cursor_x = cons->width - 1; + cons->cursor_y--; + } + mutex_lock (&vga_lock); + if (active_console == cons) + vga_display_update_cursor (cons); + mutex_unlock (&vga_lock); + } + break; + case '\t': /* horizontal tab: <ht> */ + cons->cursor_x = (cons->cursor_x | 7) + 1; + if (cons->cursor_x >= cons->width) + { + cons->cursor_x = 0; + if (cons->cursor_y < cons->height - 1) + cons->cursor_y++; + else + { + if (cons->current_line == cons->video_buffer_lines - 1) + cons->current_line = 0; + else + cons->current_line++; + /* XXX Empty out current line with spaces. */ + if (cons->scrolling_max + < cons->video_buffer_lines - cons->height) + cons->scrolling_max++; + mutex_lock (&vga_lock); + if (active_console == cons) + { + vga_display_update_screen (cons); + vga_display_update_cursor (cons); + } + mutex_unlock (&vga_lock); + /* Out. */ + break; + } + } + mutex_lock (&vga_lock); + if (active_console == cons) + vga_display_update_cursor (cons); + mutex_unlock (&vga_lock); + break; + case '\033': + cons->parse_state = STATE_ESC; + break; + case '\0': + /* Padding character: <pad>. */ + break; + default: + { + int charval = dynafont_lookup (cons->df, chr); + int line = (cons->current_line + cons->cursor_y) + % cons->video_buffer_lines; + + cons->video_buffer[(line * cons->width + cons->cursor_x) * 2] + = charval & 0xff; + cons->video_buffer[(line * cons->width + cons->cursor_x) * 2 + 1] + = cons->attr | (cons->size == 512 ? (charval >> 5) & 0x8 : 0); + + /* XXX Taking the lock twice, once here, and once below. */ + if (cons->cursor_y + cons->scrolling < cons->height) + { + mutex_lock (&vga_lock); + if (active_console == cons) + { + vga_videomem[((cons->cursor_y + cons->scrolling) + * cons->width + + cons->cursor_x) * 2] = charval; + vga_videomem[((cons->cursor_y + cons->scrolling) + * cons->width + + cons->cursor_x) * 2 + 1] = cons->attr; + } + mutex_unlock (&vga_lock); + } + + cons->cursor_x++; + if (cons->cursor_x == cons->height) + { + cons->cursor_x = 0; + if (cons->cursor_y < cons->height - 1) + cons->cursor_y++; + else + { + if (cons->current_line == cons->video_buffer_lines - 1) + cons->current_line = 0; + else + cons->current_line++; + /* XXX Empty out current line with spaces. */ + if (cons->scrolling_max + < cons->video_buffer_lines - cons->height) + cons->scrolling_max++; + mutex_lock (&vga_lock); + if (active_console == cons) + { + vga_display_update_screen (cons); + vga_display_update_cursor (cons); + } + mutex_unlock (&vga_lock); + /* Out. */ + break; + } + } + mutex_lock (&vga_lock); + if (active_console == cons) + vga_display_update_cursor (cons); + mutex_unlock (&vga_lock); + break; + } + } + break; + + case STATE_ESC: + switch (chr) + { + case '[': + cons->parse_state = STATE_ESC_BRACKET_INIT; + break; + case 'c': + /* Clear screen and home cursor: <clear>. */ + /* XXX */ + // screen_fill (cons->screen, 0, 0, cons->width, cons->height, + // emu->attr | ' '); + cons->cursor_x = cons->cursor_y = 0; + cons->parse_state = STATE_NORMAL; + break; + default: + /* Unsupported escape sequence. */ + cons->parse_state = STATE_NORMAL; + break; + } + break; + + case STATE_ESC_BRACKET_INIT: + memset (&cons->parse_params, 0, sizeof cons->parse_params); + cons->parse_nparams = 0; + if (chr == '?') + { + cons->parse_state = STATE_ESC_BRACKET_QUESTION; + break; /* Consume the question mark. */ + } + else + cons->parse_state = STATE_ESC_BRACKET; + /* Fall through. */ + case STATE_ESC_BRACKET: + case STATE_ESC_BRACKET_QUESTION: + if (chr >= '0' && chr <= '9') + cons->parse_params[cons->parse_nparams] + = cons->parse_params[cons->parse_nparams]*10 + chr - '0'; + else if (chr == ';') + { + if (++(cons->parse_nparams) >= PARSE_MAX_PARAMS) + cons->parse_state = STATE_NORMAL; /* too many */ + } + else + { + cons->parse_nparams++; + if (cons->parse_state == STATE_ESC_BRACKET) + handle_esc_bracket (emu, chr); + else + handle_esc_bracket_question (emu, chr); + cons->parse_state = STATE_NORMAL; + } + break; + default: + abort (); + } +#endif +} + + +/* Output LENGTH bytes starting from BUFFER in the system encoding. + Set BUFFER and LENGTH to the new values. The exact semantics are + just as in the iconv interface. */ +error_t +vga_display_output (void *console, char **buffer, size_t *length) +{ +#define CONV_OUTBUF_SIZE 256 + struct vga_display_console *cons = console; + error_t err = 0; + + mutex_lock (&cons->lock); + while (!err && *length > 0) + { + size_t nconv; + wchar_t outbuf[CONV_OUTBUF_SIZE]; + char *outptr = (char *) outbuf; + size_t outsize = CONV_OUTBUF_SIZE; + error_t saved_err; + int i; + + nconv = iconv (cons->cd, buffer, length, &outptr, &outsize); + saved_err = errno; + + /* First process all successfully converted characters. */ + for (i = 0; i < CONV_OUTBUF_SIZE - outsize; i++) + vga_display_output_one (cons, outbuf[i]); + + if (nconv == (size_t) -1) + { + /* Conversion didn't work out. */ + if (saved_err == EINVAL) + /* This is only an unfinished byte sequence at the end of + the input buffer. */ + break; + else if (saved_err != E2BIG) + err = saved_err; + } + } + mutex_unlock (&cons->lock); + return err; +} + + +void +vga_display_getsize (void *console, struct winsize *winsize) +{ + struct vga_display_console *cons = console; + mutex_lock (&cons->lock); + winsize->ws_row = cons->height; + winsize->ws_col = cons->width; + winsize->ws_xpixel = 0; + winsize->ws_ypixel = 0; + mutex_unlock (&cons->lock); +} + + +struct display_ops vga_display_ops = +{ + vga_display_init, + vga_display_create, + vga_display_destroy, + vga_display_activate, + vga_display_scroll, + vga_display_output, + vga_display_getsize +}; diff --git a/console/vga-hw.h b/console/vga-hw.h new file mode 100644 index 00000000..dc147e00 --- /dev/null +++ b/console/vga-hw.h @@ -0,0 +1,111 @@ +/* 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_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 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 cursor start subregister. */ +#define VGA_CRT_CURSOR_START 0x0a +#define VGA_CRT_CURSOR_DISABLE 0x10 + +#define VGA_CRT_CURSOR_HIGH 0x0e +#define VGA_CRT_CURSOR_LOW 0x0f + +#endif diff --git a/console/vga.c b/console/vga.c new file mode 100644 index 00000000..9ad9c08c --- /dev/null +++ b/console/vga.c @@ -0,0 +1,189 @@ +/* vga.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 <errno.h> +#include <assert.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/io.h> +#include <sys/mman.h> +#include <sys/types.h> + +#include "vga-hw.h" +#include "vga.h" + + +/* The base of the video memory mapping. */ +char *vga_videomem; + +/* Initialize the VGA hardware and set up the permissions and memory + mappings. */ +error_t +vga_init (void) +{ + error_t err; + int fd; + + if (io_perm (VGA_MIN_REG, VGA_MAX_REG - VGA_MIN_REG + 1, 1) < 0) + 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; + return 0; +} + + +/* Release the resources and privileges associated with the VGA + hardware access. */ +void +vga_deinit (void) +{ + io_perm (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_p (VGA_SEQ_MAP_ADDR, VGA_SEQ_ADDR_REG); + saved_seq_map = inb_p (VGA_SEQ_DATA_REG); + outb_p (VGA_SEQ_MAP_PLANE2, VGA_SEQ_DATA_REG); + outb_p (VGA_SEQ_MODE_ADDR, VGA_SEQ_ADDR_REG); + saved_seq_mode = inb_p (VGA_SEQ_DATA_REG); + outb_p (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_p (VGA_GFX_MODE_ADDR, VGA_GFX_ADDR_REG); + saved_gfx_mode = inb_p (VGA_GFX_DATA_REG); + outb_p (VGA_GFX_MODE_WRITE0, VGA_GFX_DATA_REG); + outb_p (VGA_GFX_MISC_ADDR, VGA_GFX_ADDR_REG); + saved_gfx_misc = inb_p (VGA_GFX_DATA_REG); + outb_p (VGA_GFX_MISC_A0TOAF, VGA_GFX_DATA_REG); + + memcpy (vga_videomem + offset, data, datalen); + + /* Restore sequencer and graphic register values. */ + outb_p (VGA_SEQ_MAP_ADDR, VGA_SEQ_ADDR_REG); + outb_p (saved_seq_map, VGA_SEQ_DATA_REG); + outb_p (VGA_SEQ_MODE_ADDR, VGA_SEQ_ADDR_REG); + outb_p (saved_seq_mode, VGA_SEQ_DATA_REG); + + outb_p (VGA_GFX_MODE_ADDR, VGA_GFX_ADDR_REG); + outb_p (saved_gfx_mode, VGA_GFX_DATA_REG); + outb_p (VGA_GFX_MISC_ADDR, VGA_GFX_ADDR_REG); + outb_p (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_p (VGA_GFX_MAP_ADDR, VGA_GFX_ADDR_REG); + saved_gfx_map = inb_p (VGA_GFX_DATA_REG); + outb_p (VGA_GFX_MAP_PLANE2, VGA_GFX_DATA_REG); + outb_p (VGA_GFX_MODE_ADDR, VGA_GFX_ADDR_REG); + saved_gfx_mode = inb_p (VGA_GFX_DATA_REG); + outb_p (VGA_GFX_MODE_READ0, VGA_GFX_DATA_REG); + outb_p (VGA_GFX_MISC_ADDR, VGA_GFX_ADDR_REG); + saved_gfx_misc = inb_p (VGA_GFX_DATA_REG); + outb_p (VGA_GFX_MISC_A0TOBF, VGA_GFX_DATA_REG); + + memcpy (data, vga_videomem + offset, datalen); + + outb_p (VGA_GFX_MAP_ADDR, VGA_GFX_ADDR_REG); + outb_p (saved_gfx_map, VGA_GFX_DATA_REG); + outb_p (VGA_GFX_MODE_ADDR, VGA_GFX_ADDR_REG); + outb_p (saved_gfx_mode, VGA_GFX_DATA_REG); + outb_p (VGA_GFX_MISC_ADDR, VGA_GFX_ADDR_REG); + outb_p (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_p (VGA_SEQ_FONT_ADDR, VGA_SEQ_ADDR_REG); + outb_p (font, 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 the cursor position to POS, which is (x_pos + y_pos * width). */ +void +vga_set_cursor (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); +} diff --git a/console/vga.h b/console/vga.h new file mode 100644 index 00000000..43307980 --- /dev/null +++ b/console/vga.h @@ -0,0 +1,62 @@ +/* vga.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_H_ +#define _VGA_H_ 1 + +#include <errno.h> +#include <sys/types.h> + + +/* 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_deinit (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); + +/* Enable (if ON is true) or disable (otherwise) the cursor. Expects + the VGA hardware to be locked. */ +void vga_display_cursor (int on); + +/* Set the cursor position to POS, which is (x_pos + y_pos * width). */ +void vga_set_cursor (int pos); + +#endif /* _VGA_H_ */ |