diff --git a/ChangeLog.keyboard_layouts b/ChangeLog.keyboard_layouts new file mode 100644 index 000000000..65af097ff --- /dev/null +++ b/ChangeLog.keyboard_layouts @@ -0,0 +1,7 @@ +2010-01-18 Carles Pina i Estany + + Adds keyboard layouts support. + + * conf/common.rmk (bin_UTILITIES): Add grub-mklayouts rules. + * include/grub/keyboard_layouts.h: New file. + * util/grub-mklayouts.c: New file. diff --git a/conf/common.rmk b/conf/common.rmk index ee6b94032..745b1837e 100644 --- a/conf/common.rmk +++ b/conf/common.rmk @@ -80,6 +80,10 @@ endif bin_UTILITIES += grub-mkrelpath grub_mkrelpath_SOURCES = gnulib/progname.c util/grub-mkrelpath.c util/misc.c kern/emu/misc.c +# For grub-mklayouts. +bin_UTILITIES += grub-mklayouts +grub_mklayouts_SOURCES = gnulib/progname.c util/grub-mklayouts.c util/misc.c kern/emu/misc.c + bin_UTILITIES += grub-bin2h grub_bin2h_SOURCES = gnulib/progname.c util/bin2h.c diff --git a/include/grub/at_keyboard.h b/include/grub/at_keyboard.h index 3f72fa879..8bb090d2a 100644 --- a/include/grub/at_keyboard.h +++ b/include/grub/at_keyboard.h @@ -52,7 +52,7 @@ #endif #define GRUB_AT_KEY_KEYBOARD_MAP(name) \ -static const int name[128] = \ +static const unsigned name[128] = \ { \ '\0', GRUB_TERM_ESC, '1', '2', '3', '4', '5', '6', \ '7', '8', '9', '0', '-', '=', GRUB_TERM_BACKSPACE, GRUB_TERM_TAB, \ @@ -75,4 +75,17 @@ static const int name[128] = \ OLPC_RIGHT \ } +#define GRUB_AT_KEY_KEYBOARD_MAP_SHIFT(name) \ +static unsigned name[128] = \ +{ \ + '\0', '\0', '!', '@', '#', '$', '%', '^', \ + '&', '*', '(', ')', '_', '+', '\0', '\0', \ + 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', \ + 'O', 'P', '{', '}', '\n', '\0', 'A', 'S', \ + 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', \ + '\"', '~', '\0', '|', 'Z', 'X', 'C', 'V', \ + 'B', 'N', 'M', '<', '>', '?', \ + [0x56] = GRUB_TERM_KEY_SHIFT_102 \ +} + #endif diff --git a/include/grub/keyboard_layouts.h b/include/grub/keyboard_layouts.h new file mode 100644 index 000000000..1d263e268 --- /dev/null +++ b/include/grub/keyboard_layouts.h @@ -0,0 +1,28 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2010 Free Software Foundation, Inc. + * + * GRUB 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 3 of the License, or + * (at your option) any later version. + * + * GRUB 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 GRUB. If not, see . + */ + +#ifndef GRUB_KEYBOARD_LAYOUTS_H +#define GRUB_KEYBOARD_LAYOUTS_H 1 + +#define GRUB_KEYBOARD_LAYOUTS_FILEMAGIC "GRUBLAYO" +#define GRUB_KEYBOARD_LAYOUTS_FILEMAGIC_SIZE (sizeof(GRUB_KEYBOARD_LAYOUTS_FILEMAGIC) - 1) +#define GRUB_KEYBOARD_LAYOUTS_VERSION 2 + +#define GRUB_KEYBOARD_LAYOUTS_ARRAY_SIZE 256 + +#endif /* GRUB_KEYBOARD_LAYOUTS */ diff --git a/include/grub/term.h b/include/grub/term.h index dbafef0e1..fa813d1b5 100644 --- a/include/grub/term.h +++ b/include/grub/term.h @@ -53,8 +53,8 @@ #define GRUB_TERM_KEY_INSERT (GRUB_TERM_EXTENDED | 22) /* Used by keylayouts code. Never returned in grub_getkey. */ -#define GRUB_TERM_KEY_102 (GRUB_TERM_EXTENDED | 23) -#define GRUB_TERM_KEY_SHIFT_102 (GRUB_TERM_EXTENDED | 24) +#define GRUB_TERM_KEY_102 0x80 +#define GRUB_TERM_KEY_SHIFT_102 0x81 #define GRUB_TERM_ESC '\e' #define GRUB_TERM_TAB '\t' diff --git a/term/at_keyboard.c b/term/at_keyboard.c index e89f3bf5b..4c9c2a23a 100644 --- a/term/at_keyboard.c +++ b/term/at_keyboard.c @@ -42,18 +42,7 @@ static grub_uint8_t led_status; #define KEYBOARD_LED_CAPS (1 << 2) GRUB_AT_KEY_KEYBOARD_MAP (keyboard_map); - -static int keyboard_map_shift[128] = -{ - '\0', '\0', '!', '@', '#', '$', '%', '^', - '&', '*', '(', ')', '_', '+', '\0', '\0', - 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', - 'O', 'P', '{', '}', '\n', '\0', 'A', 'S', - 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', - '\"', '~', '\0', '|', 'Z', 'X', 'C', 'V', - 'B', 'N', 'M', '<', '>', '?', - [0x56] = GRUB_TERM_KEY_SHIFT_102 -}; +GRUB_AT_KEY_KEYBOARD_MAP_SHIFT (keyboard_map_shift); static grub_uint8_t grub_keyboard_controller_orig; diff --git a/util/grub-mklayouts.c b/util/grub-mklayouts.c new file mode 100644 index 000000000..48b17e798 --- /dev/null +++ b/util/grub-mklayouts.c @@ -0,0 +1,279 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2010 Free Software Foundation, Inc. + * + * GRUB 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 3 of the License, or + * (at your option) any later version. + * + * GRUB 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 GRUB. If not, see . + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "progname.h" + +#define CKBCOMP "ckbcomp" + +static struct option options[] = { + {"output", required_argument, 0, 'o'}, + {"help", no_argument, 0, 'h'}, + {"version", no_argument, 0, 'V'}, + {"verbose", no_argument, 0, 'v'}, + {0, 0, 0, 0} +}; + +struct console_grub_equivalence +{ + char *layout; + grub_uint32_t grub; +}; + +static struct console_grub_equivalence console_grub_equivalences[] = { + {"KP_1", '1'}, + {"KP_2", '2'}, + {"KP_3", '3'}, + {"KP_4", '4'}, + {"KP_5", '5'}, + {"KP_6", '6'}, + {"KP_7", '7'}, + {"KP_8", '8'}, + {"KP_9", '9'}, + + {"KP_Multiply", '*'}, + {"KP_Substract", '-'}, + {"KP_Add", '+'}, + {"KP_Divide", '/'}, + + {"KP_Enter", '\n'}, + {"Return", '\n'}, + {"", '\0'} +}; + +GRUB_AT_KEY_KEYBOARD_MAP (us_keyboard_map); +GRUB_AT_KEY_KEYBOARD_MAP_SHIFT (us_keyboard_map_shifted); + +static void +usage (int status) +{ + if (status) + fprintf (stderr, "Try `%s --help' for more information.\n", program_name); + else + printf ("\ +Usage: %s [OPTIONS] LAYOUT\n\ + -o, --output set output base name file. Default is LAYOUT.gkb\n\ + -h, --help display this message and exit.\n\ + -V, --version print version information and exit.\n\ + -v, --verbose print verbose messages.\n\ +\n\ +Report bugs to <%s>.\n", program_name, PACKAGE_BUGREPORT); + + exit (status); +} + +char +lookup (char *code) +{ + int i; + + for (i = 0; console_grub_equivalences[i].grub != '\0'; i++) + if (strcmp (code, console_grub_equivalences[i].layout) == 0) + return console_grub_equivalences[i].grub; + + return '\0'; +} + +unsigned int +get_grub_code (char *layout_code) +{ + unsigned int code; + + if (strncmp (layout_code, "U+", sizeof ("U+") - 1) == 0) + sscanf (layout_code, "U+%x", &code); + else if (strncmp (layout_code, "+U+", sizeof ("+U+") - 1) == 0) + sscanf (layout_code, "+U+%x", &code); + else + code = lookup (layout_code); + return code; +} + +void +write_file (char* filename, grub_uint32_t *keyboard_map) +{ + FILE *fp_output; + grub_uint32_t version; + unsigned i; + + version = grub_cpu_to_le32 (GRUB_KEYBOARD_LAYOUTS_VERSION); + + for (i = 0; i < GRUB_KEYBOARD_LAYOUTS_ARRAY_SIZE; i++) + keyboard_map[i] = grub_cpu_to_le32 (keyboard_map[i]); + + fp_output = fopen (filename, "w"); + + if (!fp_output) + { + grub_util_error ("cannot open `%s'", filename); + exit (1); + } + + fwrite (GRUB_KEYBOARD_LAYOUTS_FILEMAGIC, 1, + GRUB_KEYBOARD_LAYOUTS_FILEMAGIC_SIZE, fp_output); + fwrite (&version, sizeof (version), 1, fp_output); + fwrite (keyboard_map, sizeof (keyboard_map[0]), + GRUB_KEYBOARD_LAYOUTS_ARRAY_SIZE, fp_output); + fclose (fp_output); +} + +void +write_keymaps (char *keymap, char *file_basename) +{ + grub_uint32_t keyboard_map[GRUB_KEYBOARD_LAYOUTS_ARRAY_SIZE]; + + char line[2048]; + pid_t pid; + int pipe_communication[2]; + int ok; + + FILE *fp_pipe; + + if (pipe (pipe_communication) == -1) + { + grub_util_error ("cannot prepare the pipe"); + exit (2); + } + + pid = fork (); + if (pid < 0) + { + grub_util_error ("cannot fork"); + exit (2); + } + else if (pid == 0) + { + close (1); + dup (pipe_communication[1]); + close (pipe_communication[0]); + execlp (CKBCOMP, CKBCOMP, keymap, NULL); + grub_util_error ("%s %s cannot be executed", CKBCOMP, keymap); + exit (3); + } + close (pipe_communication[1]); + fp_pipe = fdopen (pipe_communication[0], "r"); + + memset (keyboard_map, 0, sizeof (keyboard_map)); + + /* Process the ckbcomp output and prepare the layouts. */ + ok = 0; + while (fgets (line, sizeof (line), fp_pipe)) + { + if (strncmp (line, "keycode", sizeof ("keycode") - 1) == 0) + { + unsigned keycode; + char normal[64]; + char shift[64]; + sscanf (line, "keycode %u = %60s %60s", &keycode, normal, shift); + if (keycode < ARRAY_SIZE (us_keyboard_map) + && us_keyboard_map[keycode] < ARRAY_SIZE (keyboard_map)) + { + keyboard_map[us_keyboard_map[keycode]] = get_grub_code (normal); + ok = 1; + } + if (keycode < ARRAY_SIZE (us_keyboard_map_shifted) + && us_keyboard_map_shifted[keycode] < ARRAY_SIZE (keyboard_map)) + { + keyboard_map[us_keyboard_map_shifted[keycode]] + = get_grub_code (shift); + ok = 1; + } + } + } + + if (ok == 0) + { + fprintf (stderr, "ERROR: no keycodes found. Check output of %s %s.\n", + CKBCOMP, keymap); + exit (1); + } + + write_file (file_basename, keyboard_map); +} + +int +main (int argc, char *argv[]) +{ + int verbosity; + char *file_basename = NULL; + + set_program_name (argv[0]); + + verbosity = 0; + + /* Check for options. */ + while (1) + { + int c = getopt_long (argc, argv, "o:hVv", options, 0); + + if (c == -1) + break; + else + switch (c) + { + case 'h': + usage (0); + break; + + case 'o': + file_basename = optarg; + break; + + case 'V': + printf ("%s (%s) %s\n", program_name, PACKAGE_NAME, + PACKAGE_VERSION); + return 0; + + case 'v': + verbosity++; + break; + + default: + usage (1); + break; + } + } + + /* Obtain LAYOUT. */ + if (optind >= argc) + { + fprintf (stderr, "No layout is specified.\n"); + usage (1); + } + + if (file_basename == NULL) + { + file_basename = xasprintf ("%s.gkb", argv[optind]); + write_keymaps (argv[optind], file_basename); + free (file_basename); + } + else + write_keymaps (argv[optind], file_basename); + + return 0; +}