aboutsummaryrefslogtreecommitdiff
path: root/console-client/pc-kbd.c
diff options
context:
space:
mode:
Diffstat (limited to 'console-client/pc-kbd.c')
-rw-r--r--console-client/pc-kbd.c1194
1 files changed, 1194 insertions, 0 deletions
diff --git a/console-client/pc-kbd.c b/console-client/pc-kbd.c
new file mode 100644
index 00000000..d66e94b3
--- /dev/null
+++ b/console-client/pc-kbd.c
@@ -0,0 +1,1194 @@
+/* pc-kbd.c - The PC Keyboard input driver.
+ Copyright (C) 2002, 2004, 2005 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 <iconv.h>
+#include <sys/mman.h>
+#include <argp.h>
+
+#include <device/device.h>
+#include <cthreads.h>
+
+#include <hurd/console.h>
+#include <hurd/cons.h>
+
+#include "driver.h"
+#include "mach-inputdev.h"
+
+
+/* The default name of the node of the repeater. */
+#define DEFAULT_REPEATER_NODE "kbd"
+
+/* The keyboard device in the kernel. */
+static device_t kbd_dev;
+
+/* The converter. */
+static iconv_t cd;
+
+/* The status of various LEDs. */
+struct {
+ int scroll_lock : 1;
+ int num_lock : 1;
+ int caps_lock : 1;
+} led_state;
+
+/* True if we are in the GNU Mach v1 compatibility mode. */
+int gnumach_v1_compat;
+
+/* Forward declaration. */
+static struct input_ops pc_kbd_ops;
+
+/* The name of the repeater node. */
+static char *repeater_node;
+
+/* The repeater node. */
+static consnode_t cnode;
+
+/* A list of scan codes generated by the keyboard, in the set 2 encoding. */
+enum scancode
+ {
+ SC_F9 = 0x01,
+ SC_F5 = 0x03,
+ SC_F3 = 0x04,
+ SC_F1 = 0x05,
+ SC_F2 = 0x06,
+ SC_F12 = 0x07,
+ SC_F10 = 0x09,
+ SC_F8 = 0x0A,
+ SC_F6 = 0x0B,
+ SC_F4 = 0x0C,
+ SC_TAB = 0x0D,
+ SC_BACKQUOTE = 0x0E, /* ` */
+ SC_LEFT_ALT = 0x11,
+ SC_LEFT_SHIFT = 0x12,
+ SC_LEFT_CTRL = 0x14,
+ SC_Q = 0x15,
+ SC_1 = 0x16,
+ SC_Z = 0x1A,
+ SC_S = 0x1B,
+ SC_A = 0x1C,
+ SC_W = 0x1D,
+ SC_2 = 0x1E,
+ SC_C = 0x21,
+ SC_X = 0x22,
+ SC_D = 0x23,
+ SC_E = 0x24,
+ SC_4 = 0x25,
+ SC_3 = 0x26,
+ SC_SPACE = 0x29,
+ SC_V = 0x2A,
+ SC_F = 0x2B,
+ SC_T = 0x2C,
+ SC_R = 0x2D,
+ SC_5 = 0x2E,
+ SC_N = 0x31,
+ SC_B = 0x32,
+ SC_H = 0x33,
+ SC_G = 0x34,
+ SC_Y = 0x35,
+ SC_6 = 0x36,
+ SC_M = 0x3A,
+ SC_J = 0x3B,
+ SC_U = 0x3C,
+ SC_7 = 0x3D,
+ SC_8 = 0x3E,
+ SC_COMMA = 0x41, /* , */
+ SC_K = 0x42,
+ SC_I = 0x43,
+ SC_O = 0x44,
+ SC_0 = 0x45,
+ SC_9 = 0x46,
+ SC_PERIOD = 0x49, /* . */
+ SC_SLASH = 0x4A, /* / */
+ SC_L = 0x4B,
+ SC_SEMICOLON = 0x4C, /* ; */
+ SC_P = 0x4D,
+ SC_MINUS = 0x4E, /* - */
+ SC_APOSTROPHE = 0x52, /* ' */
+ SC_LEFT_BRACKET = 0x54, /* [ */
+ SC_EQUAL = 0x55, /* = */
+ SC_CAPSLOCK = 0x58,
+ SC_RIGHT_SHIFT = 0x59,
+ SC_ENTER = 0x5A,
+ SC_RIGHT_BRACKET = 0x5B, /* ] */
+ SC_BACKSLASH = 0x5D, /* \ */
+ SC_BACKSPACE = 0x66,
+ SC_PAD_1 = 0x69,
+ SC_PAD_4 = 0x6B,
+ SC_PAD_7 = 0x6C,
+ SC_PAD_0 = 0x70,
+ SC_PAD_DECIMAL = 0x71,
+ SC_PAD_2 = 0x72,
+ SC_PAD_5 = 0x73,
+ SC_PAD_6 = 0x74,
+ SC_PAD_8 = 0x75,
+ SC_ESC = 0x76,
+ SC_NUMLOCK = 0x77,
+ SC_F11 = 0x78,
+ SC_PAD_PLUS = 0x79,
+ SC_PAD_3 = 0x7A,
+ SC_PAD_MINUS = 0x7B,
+ SC_PAD_ASTERISK = 0x7C,
+ SC_PAD_9 = 0x7D,
+ SC_SCROLLLOCK = 0x7E,
+ SC_F7 = 0x83,
+ SC_EXTENDED1 = 0xE0, /* One code follows. */
+ SC_EXTENDED2 = 0xE1, /* Two codes follow (only used for Pause). */
+ SC_ERROR = 0xFF, /* Too many keys held down. */
+ SC_FLAG_UP = 0xF000 /* ORed to basic scancode. */
+ };
+
+/* In set 2 function keys don't have a logical order. This macro can
+ determine if a function key was pressed. */
+#define IS_FUNC_KEY(c) ((sc >= SC_F9 && sc <= SC_F4) || \
+ sc == SC_F7 || sc == SC_F11)
+
+/* Codes which can follow SC_EXTENDED1. */
+enum scancode_x1
+ {
+ SC_X1_RIGHT_ALT = 0x11,
+ SC_X1_PRTSC = 0x12,
+/* SC_X1_PRTSC = 0x7C, */
+ SC_X1_RIGHT_CTRL = 0x14,
+ SC_X1_LEFT_GUI = 0x1F,
+ SC_X1_RIGHT_GUI = 0x27,
+ SC_X1_APPS = 0x2F,
+ SC_X1_POWER = 0x37,
+ SC_X1_SLEEP = 0x3F,
+ SC_X1_PAD_SLASH = 0x4A,
+ SC_X1_PAD_ENTER = 0x5A,
+ SC_X1_WAKEUP = 0x5E,
+ SC_X1_END = 0x69,
+ SC_X1_LEFT = 0x6B,
+ SC_X1_HOME = 0x6C,
+ SC_X1_INS = 0x70,
+ SC_X1_DEL = 0x71,
+ SC_X1_DOWN = 0x72,
+ SC_X1_RIGHT = 0x74,
+ SC_X1_UP = 0x75,
+ SC_X1_PGDN = 0x7A,
+ SC_X1_PGUP = 0x7D
+ };
+
+/* Codes which can follow SC_EXTENDED2. */
+enum scancode_x2
+ {
+ SC_X2_BREAK = 0x1477,
+ };
+
+
+/* Scancode to Unicode mapping. The empty string stands for the NULL
+ character. */
+char *sc_to_kc[][7] =
+ {
+ /*None, Shift, Ctrl, LAlt, S+LAlt, C+LAlt, RAlt */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { CONS_KEY_F9, 0, 0, 0, 0, 0, 0 }, /* SC_F9. */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { CONS_KEY_F5, CONS_KEY_F17, 0, 0, 0, 0, 0 }, /* SC_F5. */
+ { CONS_KEY_F3, CONS_KEY_F15, 0, 0, 0, 0, 0 }, /* SC_F3. */
+ { CONS_KEY_F1, CONS_KEY_F13, 0, 0, 0, 0, 0 }, /* SC_F1. */
+ { CONS_KEY_F2, CONS_KEY_F14, 0, 0, 0, 0, 0 }, /* SC_F2. */
+ { CONS_KEY_F12, 0, 0, 0, 0, 0, 0 }, /* SC_F12. */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { CONS_KEY_F10, 0, 0, 0, 0, 0, 0 }, /* SC_F10. */
+ { CONS_KEY_F8, CONS_KEY_F20, 0, 0, 0, 0, 0 }, /* SC_F8. */
+ { CONS_KEY_F6, CONS_KEY_F18, 0, 0, 0, 0, 0 }, /* SC_F6. */
+ { CONS_KEY_F4, CONS_KEY_F16, 0, 0, 0, 0, 0 }, /* SC_F4. */
+ { "\t", "\t", "\t", "\e\t", "\e\t", "\e\t", "\t" }, /* SC_TAB. */
+ { "`", "~", 0, "\e`", "\e~", 0, 0 }, /* SC_BACKQUOTE. */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, /* SC_LEFT_ALT. XXX */
+ { 0, 0, 0, 0, 0, 0, 0 }, /* SC_LEFT_SHIFT. */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, /* SC_LEFT_CTRL. XXX */
+ { "q", "Q", "\x11", "\eq", "\eQ", "\e\x11", "q" }, /* SC_Q. */
+ { "1", "!", 0, "\e1", "\e!", 0, "1" }, /* SC_1. */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { "z", "Z", "\x1a", "\ez", "\eZ", "\e\x1a", "z" }, /* SC_Z. */
+ { "s", "S", "\x13", "\es", "\eS", "\e\x13", "s" }, /* SC_S. */
+ { "a", "A", "\x01", "\ea", "\eA", "\e\x01", "a" }, /* SC_A. */
+ { "w", "W", "\x17", "\ew", "\eW", "\e\x17", "w" }, /* SC_W. */
+ { "2", "@", "", "\e2", "\e@", 0, "2" }, /* SC_2. */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { "c", "C", "\x03", "\ec", "\eC", "\e\x03", "\xc2\xa2" }, /* SC_C. */
+ { "x", "X", "\x18", "\ex", "\eX", "\e\x18", "x" }, /* SC_X. */
+ { "d", "D", "\x04", "\ed", "\eD", "\e\x04", "d" }, /* SC_D. */
+ { "e", "E", "\x05", "\ee", "\eE", "\e\x05","\xe2\x82\xac" }, /* SC_E. */
+ { "4", "$", "\x1c", "\e4", "\e$", "\e\x1c", "4" }, /* SC_4. */
+ { "3", "#", "\e", "\e3", "\e#", 0, "3" }, /* SC_3. */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { " ", " ", "", "\e ", "\e ", /*XXX*/0, " " }, /* SC_SPACE. */
+ { "v", "V", "\x16", "\ev", "\eV", "\e\x16", "v" }, /* SC_V. */
+ { "f", "F", "\x06", "\ef", "\eF", "\e\x06", "f" }, /* SC_F. */
+ { "t", "T", "\x14", "\et", "\eT", "\e\x14", "t" }, /* SC_T. */
+ { "r", "R", "\x12", "\er", "\eR", "\e\x12", "r" }, /* SC_R. */
+ { "5", "%", "\x1d", "\e5", "\e%", 0, "5" }, /* SC_5. */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { "n", "N", "\x0e", "\en", "\eN", "\e\x0e", "n" }, /* SC_N. */
+ { "b", "B", "\x02", "\eb", "\eB", "\e\x02", "b" }, /* SC_B. */
+ { "h", "H", "\x08", "\eh", "\eH", "\e\x08", "h" }, /* SC_H. */
+ { "g", "G", "\x07", "\eg", "\eG", "\e\x07", "g" }, /* SC_G. */
+ { "y", "Y", "\x19", "\ey", "\eY", "\e\x19", "y" }, /* SC_Y. */
+ { "6", "^", "\x1e", "\e6", "\e^", 0, "6" }, /* SC_6. */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { "m", "M", "\x0d", "\em", "\eM", "\e\x0d", "m" }, /* SC_M. */
+ { "j", "J", "\x0a", "\ej", "\eJ", "\e\x0a", "j" }, /* SC_J. */
+ { "u", "U", "\x15", "\eu", "\eU", "\e\x15", "u" }, /* SC_U. */
+ { "7", "&", "\x1f", "\e7", "\e&", "\e\x1f", "7" }, /* SC_7. */
+ { "8", "*", "\x7f", "\e8", "\e*", 0, "8" }, /* SC_8. */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { ",", "<", 0, "\e,", "\e<", 0, 0 }, /* SC_COMMA. */
+ { "k", "K", "\x0b", "\ek", "\eK", "\e\x0b", "k" }, /* SC_K. */
+ { "i", "I", "\x09", "\ei", "\eI", "\e\x09", "i" }, /* SC_I. */
+ { "o", "O", "\x0f", "\eo", "\eO", "\e\x0f", "o" }, /* SC_O. */
+ { "0", ")", 0, "\e0", "\e)", 0, "0" }, /* SC_0. */
+ { "9", "(", 0, "\e9", "\e(", 0, "9" }, /* SC_9. */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { ".", ">", 0, "\e.", "\e>", 0, 0 }, /* SC_PERIOD. */
+ { "/", "?", "\x7f", "\e/", "\e?", 0, 0 }, /* SC_SLASH. */
+ { "l", "L", "\x0c", "\el", "\eL", "\e\x0c", "l" }, /* SC_L. */
+ { ";", ":", 0, "\e;", "\e:", 0, 0 }, /* SC_SEMICOLON. */
+ { "p", "P", "\x10", "\ep", "\eP", "\e\x10", "p" }, /* SC_P. */
+ { "-", "_", "\x1f", "\e-", "\e_", "\e\x1f", "-" }, /* SC_MINUS. */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { "'", "\"", "\x07", "\e'", "\e\"", 0, 0 }, /* SC_APOSTROPHE. */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { "[", "{", "\e", "\e[", "\e{", 0, 0 }, /* SC_LEFT_BRACKET. */
+ { "=", "+", 0, "\e=", "\e+", 0, "=" }, /* SC_EQUAL. */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, /* SC_CAPSLOCK. */
+ { 0, 0, 0, 0, 0, 0, 0 }, /* SC_RIGHT_SHIFT. */
+ {"\x0d","\x0d", "\x0d","\e\x0d","\e\x0d","\e\x0d","\x0d" }, /* SC_ENTER. */
+ { "]", "}", "\x1d", "\e]", "\e}", "\e\x1d", "~" }, /* SC_RIGHT_BRACKET. */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { "\\", "|", "\x1c", "\e\\", "\e|", 0, 0 }, /* SC_BACKSLASH. */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { CONS_KEY_BACKSPACE, CONS_KEY_BACKSPACE, CONS_KEY_BACKSPACE,
+ "\e" CONS_KEY_BACKSPACE, "\e" CONS_KEY_BACKSPACE,
+ "\e" CONS_KEY_BACKSPACE, CONS_KEY_BACKSPACE }, /* SC_BACKSPACE. */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { CONS_KEY_END, CONS_KEY_END, CONS_KEY_END, 0, 0, 0, 0 }, /* SC_PAD_1. */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { CONS_KEY_LEFT, CONS_KEY_LEFT, CONS_KEY_LEFT, 0, 0, 0, 0 }, /* SC_PAD_4. */
+ { CONS_KEY_HOME, CONS_KEY_HOME, CONS_KEY_HOME, 0, 0, 0, 0 }, /* SC_PAD_7. */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { CONS_KEY_IC, CONS_KEY_IC, CONS_KEY_IC, 0, 0, 0, 0 }, /* SC_PAD_0. */
+ { CONS_KEY_DC, CONS_KEY_DC, CONS_KEY_DC, 0, 0, 0, 0 }, /* SC_PAD_DECIMAL. */
+ { CONS_KEY_DOWN, CONS_KEY_DOWN, CONS_KEY_DOWN, 0, 0, 0, 0 }, /* SC_PAD_2. */
+ {/* XXX */ "\e[G", "\e[G", "\e[G", 0, 0, 0, 0 }, /* SC_PAD_5. */
+ { CONS_KEY_RIGHT, CONS_KEY_RIGHT, CONS_KEY_RIGHT,0, 0, 0, 0 }, /* SC_PAD_6. */
+ { CONS_KEY_UP, CONS_KEY_UP, CONS_KEY_UP, 0, 0, 0, 0 }, /* SC_PAD_8. */
+ { "\e", "\e", "\e", "\e\e", "\e\e", "\e\e", "\e" }, /* SC_ESC. */
+ { 0, 0, 0, 0, 0, 0, 0 }, /* SC_NUMLOCK. */
+ { CONS_KEY_F11, 0, 0, 0, 0, 0, 0 }, /* SC_F11. */
+ { "+", "+", "+", "+", "+", "+", "+" }, /* SC_PAD_PLUS. */
+ { CONS_KEY_NPAGE, CONS_KEY_NPAGE, CONS_KEY_NPAGE,0, 0, 0, 0 }, /* SC_PAD_3. */
+ { "-", "-", "-", "-", "-", "-", "-" }, /* SC_PAD_MINUS. */
+ { "*", "*", "*", "*", "*", "*", "*" }, /* SC_PAD_ASTERISK. XXX */
+ { CONS_KEY_PPAGE, CONS_KEY_PPAGE, CONS_KEY_PPAGE,0, 0, 0, 0 }, /* SC_PAD_9. */
+ { 0, 0, 0, 0, 0, 0, 0 }, /* SC_SCROLLLOCK. */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { CONS_KEY_F7, CONS_KEY_F19, 0, 0, 0, 0, 0 } /* SC_F7. */
+ };
+
+char *sc_x1_to_kc[][7] =
+ {
+ /* None, Shift, Ctrl, LAlt, S+LAlt, C+LAlt, RAlt */
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, /* SC_X1_RIGHT_ALT. */
+ { 0, 0, 0, 0, 0, 0, 0 }, /* SC_X1_PRTSC. */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, /* SC_X1_RIGHT_CTRL. */
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, /* SC_X1_LEFT_GUI. */
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, /* SC_X1_RIGHT_GUI. */
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, /* SC_X1_APPS. */
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, /* SC_X1_POWER. */
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, /* SC_X1_SLEEP. */
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { "/", "/", "/", "/", "/", "/", 0 }, /* SC_X1_PAD_SLASH. */
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { "\n", "\n", "\n", "\n", "\n", "\n", 0 }, /* SC_X1_PAD_ENTER. */
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, /* SC_X1_WAKEUP. */
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { CONS_KEY_END, CONS_KEY_END, CONS_KEY_END, CONS_KEY_END,
+ CONS_KEY_END, CONS_KEY_END, CONS_KEY_END }, /* SC_X1_END. */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { CONS_KEY_LEFT, CONS_KEY_LEFT, CONS_KEY_LEFT, CONS_KEY_LEFT,
+ CONS_KEY_LEFT, CONS_KEY_LEFT, CONS_KEY_LEFT }, /* SC_X1_LEFT. */
+ { CONS_KEY_HOME, CONS_KEY_HOME, CONS_KEY_HOME, CONS_KEY_HOME,
+ CONS_KEY_HOME, CONS_KEY_HOME, CONS_KEY_HOME }, /* SC_X1_HOME. */
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { CONS_KEY_IC, CONS_KEY_IC, CONS_KEY_IC, CONS_KEY_IC,
+ CONS_KEY_IC, CONS_KEY_IC, CONS_KEY_IC }, /* SC_X1_INS. */
+ { CONS_KEY_DC, CONS_KEY_DC, CONS_KEY_DC, CONS_KEY_DC,
+ CONS_KEY_DC, CONS_KEY_DC, CONS_KEY_DC }, /* SC_X1_DEL. */
+ { CONS_KEY_DOWN, CONS_KEY_DOWN, CONS_KEY_DOWN, CONS_KEY_DOWN,
+ CONS_KEY_DOWN, CONS_KEY_DOWN, CONS_KEY_DOWN }, /* SC_X1_DOWN. */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { CONS_KEY_RIGHT, CONS_KEY_RIGHT, CONS_KEY_RIGHT, CONS_KEY_RIGHT,
+ CONS_KEY_RIGHT, CONS_KEY_RIGHT, CONS_KEY_RIGHT }, /* SC_X1_RIGHT. */
+ { CONS_KEY_UP, CONS_KEY_UP, CONS_KEY_UP, CONS_KEY_UP,
+ CONS_KEY_UP, CONS_KEY_UP, CONS_KEY_UP }, /* SC_X1_UP. */
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { CONS_KEY_NPAGE, CONS_KEY_NPAGE, CONS_KEY_NPAGE, CONS_KEY_NPAGE,
+ CONS_KEY_NPAGE, CONS_KEY_NPAGE, CONS_KEY_NPAGE }, /* SC_X1_PGDN. */
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { CONS_KEY_PPAGE, CONS_KEY_PPAGE, CONS_KEY_PPAGE, CONS_KEY_PPAGE,
+ CONS_KEY_PPAGE, CONS_KEY_PPAGE, CONS_KEY_PPAGE } /* SC_X1_PGUP. */
+ };
+
+char *sc_x2_to_kc[][7] =
+ {
+ /* We don't add all those zero entries here. It's just one key,
+ so it's special cased. */
+ { "\e[P", "\e[P", "\e[P", "\e[P", "\e[P", "\e[P","\e[P" }, /* SC_X1_BREAK. */
+ };
+
+
+/* GNU Mach v1 compatibility code. */
+
+/* This is a conversion table from i8042 scancode set 1 to set 2. */
+enum scancode sc_set1_to_set2[] =
+ {
+ 0x00,
+ SC_ESC,
+ SC_1,
+ SC_2,
+ SC_3,
+ SC_4,
+ SC_5,
+ SC_6,
+ SC_7,
+ SC_8,
+ SC_9,
+ SC_0,
+ SC_MINUS,
+ SC_EQUAL,
+ SC_BACKSPACE,
+ SC_TAB,
+ SC_Q,
+ SC_W,
+ SC_E,
+ SC_R,
+ SC_T,
+ SC_Y,
+ SC_U,
+ SC_I,
+ SC_O,
+ SC_P,
+ SC_LEFT_BRACKET,
+ SC_RIGHT_BRACKET,
+ SC_ENTER,
+ SC_LEFT_CTRL,
+ SC_A,
+ SC_S,
+ SC_D,
+ SC_F,
+ SC_G,
+ SC_H,
+ SC_J,
+ SC_K,
+ SC_L,
+ SC_SEMICOLON,
+ SC_APOSTROPHE,
+ SC_BACKQUOTE,
+ SC_LEFT_SHIFT,
+ SC_BACKSLASH,
+ SC_Z,
+ SC_X,
+ SC_C,
+ SC_V,
+ SC_B,
+ SC_N,
+ SC_M,
+ SC_COMMA,
+ SC_PERIOD,
+ SC_SLASH,
+ SC_RIGHT_SHIFT,
+ SC_PAD_ASTERISK,
+ SC_LEFT_ALT,
+ SC_SPACE,
+ SC_CAPSLOCK,
+ SC_F1,
+ SC_F2,
+ SC_F3,
+ SC_F4,
+ SC_F5,
+ SC_F6,
+ SC_F7,
+ SC_F8,
+ SC_F9,
+ SC_F10,
+ SC_NUMLOCK,
+ SC_SCROLLLOCK,
+ SC_PAD_7,
+ SC_PAD_8,
+ SC_PAD_9,
+ SC_PAD_MINUS,
+ SC_PAD_4,
+ SC_PAD_5,
+ SC_PAD_6,
+ SC_PAD_PLUS,
+ SC_PAD_1,
+ SC_PAD_2,
+ SC_PAD_3,
+ SC_PAD_0,
+ SC_PAD_DECIMAL,
+ 0x00, /* XXX SYSREQ */
+ 0x00,
+ 0x00,
+ SC_F11,
+ SC_F12,
+ };
+
+/* Conversion table for codes which can follow SC_EXTENDED1. */
+enum scancode sc_set1_to_set2_x1[] =
+ {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ SC_X1_PAD_ENTER,
+ SC_X1_RIGHT_CTRL,
+ 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00,
+ SC_X1_PAD_SLASH,
+ 0x00,
+ SC_X1_PRTSC,
+ SC_X1_RIGHT_ALT,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, /* XXX SC_X1_BREAK */
+ SC_X1_HOME,
+ SC_X1_UP,
+ SC_X1_PGUP,
+ 0x00,
+ SC_X1_LEFT,
+ 0x00,
+ SC_X1_RIGHT,
+ 0x00,
+ SC_X1_END,
+ SC_X1_DOWN,
+ SC_X1_PGDN,
+ SC_X1_INS,
+ SC_X1_DEL
+ };
+
+static enum scancode
+gnumach_v1_input_next ()
+{
+ kd_event data_buf;
+ int up;
+ enum scancode sc;
+
+ do
+ {
+ /* io_buf_ptr_t is (char *), not (void *). So I have a few
+ casts to quiet warnings. */
+ mach_msg_type_number_t data_cnt = sizeof (data_buf);
+ error_t err = device_read_inband (kbd_dev, 0, -1, sizeof (kd_event),
+ (void *) &data_buf, &data_cnt);
+
+ /* XXX The error occurred likely because KBD_DEV was closed, so
+ terminate. */
+ if (err)
+ return 0;
+
+ if (kbd_repeater_opened && data_buf.type == KEYBD_EVENT)
+ {
+ kbd_repeat_key (&data_buf);
+ data_buf.type = 0;
+ continue;
+ }
+ }
+ while (data_buf.type != KEYBD_EVENT);
+
+ /* Some fixed codes which are the same in set 1 and set 2, and have
+ the UP flag set. */
+ if (data_buf.value.sc == SC_EXTENDED1
+ || data_buf.value.sc == SC_EXTENDED2
+ || data_buf.value.sc == SC_ERROR)
+ return data_buf.value.sc;
+
+#define SC_SET1_FLAG_UP 0x80
+ up = data_buf.value.sc & SC_SET1_FLAG_UP;
+ sc = sc_set1_to_set2[data_buf.value.sc &~ SC_SET1_FLAG_UP];
+
+ return sc | (up ? SC_FLAG_UP : 0);
+}
+
+
+static void
+update_leds (void)
+{
+ error_t err;
+
+ if (gnumach_v1_compat)
+ {
+ int led = (led_state.scroll_lock ? 1 : 0)
+ | (led_state.num_lock ? 2 : 0)
+ | (led_state.caps_lock ? 4 : 0);
+
+ err = device_set_status (kbd_dev, KDSETLEDS, &led, 1);
+ /* Just ignore the error, GNUMach 1.3 and older cannot set the
+ keyboard LEDs. */
+ }
+ else
+ {
+ char leds[2];
+ mach_msg_type_number_t data_cnt = 2;
+
+ leds[0] = '\xed';
+ leds[1] = (led_state.scroll_lock ? 1 : 0)
+ | (led_state.num_lock ? 2 : 0)
+ | (led_state.caps_lock ? 4 : 0);
+
+ err = device_write_inband (kbd_dev, 0, -1, (void *) leds, 2, &data_cnt);
+ if (!err && data_cnt == 1)
+ err = device_write_inband (kbd_dev, 0, -1, (void *) &leds[1], 1,
+ &data_cnt);
+ }
+}
+
+static enum scancode
+input_next ()
+{
+ enum scancode sc = 0;
+ unsigned char next;
+
+ /* GNU Mach v1 does provide keyboard input in a different format. */
+ if (gnumach_v1_compat)
+ return gnumach_v1_input_next ();
+
+ /* XXX This should read several characters at once. */
+ do
+ {
+ mach_msg_type_number_t data_cnt = 1;
+ error_t err = device_read_inband (kbd_dev, 0, -1, 1,
+ (void *) &next, &data_cnt);
+
+ /* XXX The error occurred likely because KBD_DEV was closed, so
+ terminate. */
+ if (err)
+ return 0;
+
+ if (next == 0xF0)
+ /* XXX Magic constant. */
+ sc |= SC_FLAG_UP;
+ }
+ while (next == 0xF0); /* XXX Magic constant. */
+
+ sc |= next;
+ return sc;
+}
+
+
+/* The input loop. */
+static any_t
+input_loop (any_t unused)
+{
+ while (1)
+ {
+ enum scancode fsc = input_next ();
+ enum scancode sc = fsc & ~SC_FLAG_UP;
+ int down = !(fsc & SC_FLAG_UP);
+ char buf[100];
+ size_t size = 0;
+ int modifier = -1;
+
+ static struct {
+ wchar_t direct;
+ unsigned int extended : 2;
+ unsigned int left_shift : 1;
+ unsigned int right_shift : 1;
+ unsigned int caps_lock : 1;
+ unsigned int caps_lock_pressed : 1;
+ unsigned int left_ctrl : 1;
+ unsigned int right_ctrl : 1;
+ unsigned int left_alt : 1;
+ unsigned int right_alt : 1;
+ unsigned int num_lock : 1;
+ unsigned int num_lock_pressed : 1;
+ } state;
+
+ if (!state.left_alt && !state.right_alt)
+ {
+ if (state.left_ctrl || state.right_ctrl)
+ modifier = 2;
+ else if (state.left_shift || state.right_shift)
+ modifier = 1;
+ else
+ modifier = 0;
+ }
+ else if (state.left_alt)
+ {
+ if (state.left_ctrl || state.right_ctrl)
+ modifier = 5;
+ if (state.left_shift || state.right_shift)
+ modifier = 4;
+ else
+ modifier = 3;
+ }
+ else if (state.right_alt)
+ {
+ if (!state.left_ctrl && !state.right_ctrl
+ && !state.left_shift && !state.right_shift)
+ modifier = 6;
+ }
+
+ if (!state.extended)
+ {
+ if (fsc == SC_EXTENDED1)
+ state.extended = 1;
+ else if (fsc == SC_EXTENDED2)
+ state.extended = 2;
+ else if (sc == SC_LEFT_SHIFT)
+ state.left_shift = down;
+ else if (sc == SC_RIGHT_SHIFT)
+ state.right_shift = down;
+ else if (sc == SC_CAPSLOCK)
+ {
+ if (down && !state.caps_lock_pressed)
+ {
+ state.caps_lock = !state.caps_lock;
+ state.caps_lock_pressed = 1;
+ led_state.caps_lock = state.caps_lock;
+ update_leds ();
+ }
+ else if (!down)
+ state.caps_lock_pressed = 0;
+ }
+ else if (sc == SC_LEFT_CTRL)
+ state.left_ctrl = down;
+ else if (sc == SC_LEFT_ALT)
+ state.left_alt = down;
+ else if (state.left_alt && down && IS_FUNC_KEY (sc))
+ {
+ /* The virtual console to switch to. */
+ int vc = 0;
+
+ /* Check if a function key was pressed.
+ Choose the virtual console corresponding to that key. */
+ switch (sc)
+ {
+ case SC_F1:
+ vc = 1;
+ break;
+ case SC_F2:
+ vc = 2;
+ break;
+ case SC_F3:
+ vc = 3;
+ break;
+ case SC_F4:
+ vc = 4;
+ break;
+ case SC_F5:
+ vc = 5;
+ break;
+ case SC_F6:
+ vc = 6;
+ break;
+ case SC_F7:
+ vc = 7;
+ break;
+ case SC_F8:
+ vc = 8;
+ break;
+ case SC_F9:
+ vc = 9;
+ break;
+ case SC_F10:
+ vc = 10;
+ break;
+ case SC_F11:
+ vc = 11;
+ break;
+ case SC_F12:
+ vc = 12;
+ break;
+ /* No function key was pressed, don't
+ switch to another vc. */
+ default:
+ vc = 0;
+ }
+
+ if (vc)
+ console_switch (vc, 0);
+ }
+ else if (state.left_alt && state.left_ctrl && down && sc == SC_BACKSPACE)
+ console_exit ();
+ else if (state.right_alt && down && sc == SC_PAD_0) /* XXX */
+ state.direct = (state.direct << 4) | 0x0;
+ else if (state.right_alt && down && sc == SC_PAD_1) /* XXX */
+ state.direct = (state.direct << 4) | 0x1;
+ else if (state.right_alt && down && sc == SC_PAD_2) /* XXX */
+ state.direct = (state.direct << 4) | 0x2;
+ else if (state.right_alt && down && sc == SC_PAD_3) /* XXX */
+ state.direct = (state.direct << 4) | 0x3;
+ else if (state.right_alt && down && sc == SC_PAD_4) /* XXX */
+ state.direct = (state.direct << 4) | 0x4;
+ else if (state.right_alt && down && sc == SC_PAD_5) /* XXX */
+ state.direct = (state.direct << 4) | 0x5;
+ else if (state.right_alt && down && sc == SC_PAD_6) /* XXX */
+ state.direct = (state.direct << 4) | 0x6;
+ else if (state.right_alt && down && sc == SC_PAD_7) /* XXX */
+ state.direct = (state.direct << 4) | 0x7;
+ else if (state.right_alt && down && sc == SC_PAD_8) /* XXX */
+ state.direct = (state.direct << 4) | 0x8;
+ else if (state.right_alt && down && sc == SC_PAD_9) /* XXX */
+ state.direct = (state.direct << 4) | 0x9;
+ else if (state.right_alt && down && sc == SC_NUMLOCK) /* XXX */
+ state.direct = (state.direct << 4) | 0xa;
+ else if (state.right_alt && down && sc == SC_PAD_ASTERISK) /* XXX */
+ state.direct = (state.direct << 4) | 0xc;
+ else if (state.right_alt && down && sc == SC_PAD_MINUS) /* XXX */
+ state.direct = (state.direct << 4) | 0xd;
+ else if (state.right_alt && down && sc == SC_PAD_PLUS) /* XXX */
+ state.direct = (state.direct << 4) | 0xe;
+ else if (sc == SC_NUMLOCK)
+ {
+ if (down && !state.num_lock_pressed)
+ {
+ state.num_lock = !state.num_lock;
+ state.num_lock_pressed = 1;
+ led_state.num_lock = state.num_lock;
+ update_leds ();
+ }
+ else if (!down)
+ state.num_lock_pressed = 0;
+ }
+ else if (down && sc < sizeof (sc_to_kc)/sizeof (sc_to_kc[0]))
+ {
+#if QUAERENDO_INVENIETIS
+ if (state.left_alt && state.right_alt
+ && sc_to_kc[sc][0][0] >= '0' && sc_to_kc[sc][0][0] <= '9'
+ && sc_to_kc[sc][0][1] == '\0')
+ console_deprecated (sc_to_kc[sc][0][0] - '0');
+ else
+#endif
+ {
+ /* Special rule for caps lock. */
+ if (modifier == 0 && state.caps_lock
+ && sc_to_kc[sc][modifier]
+ && sc_to_kc[sc][modifier][0] >= 'a'
+ && sc_to_kc[sc][modifier][0] <= 'z'
+ && sc_to_kc[sc][modifier][1] == '\0')
+ modifier = 1;
+ else if (state.num_lock && sc == SC_PAD_0)
+ {
+ modifier = 0;
+ sc = SC_0;
+ }
+ else if (state.num_lock && sc == SC_PAD_1)
+ {
+ modifier = 0;
+ sc = SC_1;
+ }
+ else if (state.num_lock && sc == SC_PAD_2)
+ {
+ modifier = 0;
+ sc = SC_2;
+ }
+ else if (state.num_lock && sc == SC_PAD_3)
+ {
+ modifier = 0;
+ sc = SC_3;
+ }
+ else if (state.num_lock && sc == SC_PAD_4)
+ {
+ modifier = 0;
+ sc = SC_4;
+ }
+ else if (state.num_lock && sc == SC_PAD_5)
+ {
+ modifier = 0;
+ sc = SC_5;
+ }
+ else if (state.num_lock && sc == SC_PAD_6)
+ {
+ modifier = 0;
+ sc = SC_6;
+ }
+ else if (state.num_lock && sc == SC_PAD_7)
+ {
+ modifier = 0;
+ sc = SC_7;
+ }
+ else if (state.num_lock && sc == SC_PAD_8)
+ {
+ modifier = 0;
+ sc = SC_8;
+ }
+ else if (state.num_lock && sc == SC_PAD_9)
+ {
+ modifier = 0;
+ sc = SC_9;
+ }
+ else if (state.num_lock && sc == SC_PAD_DECIMAL)
+ {
+ modifier = 0;
+ sc = SC_PERIOD;
+ }
+
+ if (modifier >= 0 && sc_to_kc[sc][modifier])
+ {
+ if (!sc_to_kc[sc][modifier][0])
+ {
+ /* Special meaning, emit NUL. */
+ assert (size < 100);
+ buf[size++] = '\0';
+ }
+ else
+ {
+ assert (size
+ < 101 - strlen(sc_to_kc[sc][modifier]));
+ strcpy (&buf[size], sc_to_kc[sc][modifier]);
+ size += strlen (sc_to_kc[sc][modifier]);
+ }
+ }
+ }
+ }
+ }
+ else if (state.extended == 1)
+ {
+ state.extended = 0;
+ if (sc == SC_X1_RIGHT_CTRL)
+ state.right_ctrl = down;
+ else if (sc == SC_X1_RIGHT_ALT)
+ {
+ state.right_alt = down;
+
+ /* Handle the AltGR+Keypad direct input. */
+ if (down)
+ state.direct = (wchar_t) 0;
+ else
+ {
+ if (state.direct != (wchar_t) 0)
+ {
+ char *buffer = &buf[size];
+ size_t left = sizeof (buf) - size;
+ char *inbuf = (char *) &state.direct;
+ size_t inbufsize = sizeof (wchar_t);
+ size_t nr;
+
+ nr = iconv (cd, &inbuf, &inbufsize, &buffer, &left);
+ if (nr == (size_t) -1)
+ {
+ if (errno == E2BIG)
+ console_error (L"Input buffer overflow");
+ else if (errno == EILSEQ)
+ console_error
+ (L"Input contained invalid byte sequence");
+ else if (errno == EINVAL)
+ console_error
+ (L"Input contained incomplete byte sequence");
+ else
+ console_error
+ (L"Input caused unexpected error");
+ }
+ size = sizeof (buf) - left;
+ }
+ }
+ }
+ else if (state.right_alt && down && sc == SC_X1_PAD_SLASH) /* XXX */
+ state.direct = (state.direct << 4) | 0xb;
+ else if (state.right_alt && down && sc == SC_X1_PAD_ENTER) /* XXX */
+ state.direct = (state.direct << 4) | 0xf;
+ else if (state.left_alt && down && sc == SC_X1_RIGHT) /* XXX */
+ console_switch (0, 1);
+ else if (state.left_alt && down && sc == SC_X1_LEFT) /* XXX */
+ console_switch (0, -1);
+ else if (state.left_alt && down && sc == SC_X1_UP) /* XXX */
+ console_scrollback (CONS_SCROLL_DELTA_LINES, 1);
+ else if (state.left_alt && down && sc == SC_X1_DOWN) /* XXX */
+ console_scrollback (CONS_SCROLL_DELTA_LINES, -1);
+ else if ((state.right_shift || state.left_shift)
+ && down && sc == SC_X1_PGUP) /* XXX */
+ console_scrollback (CONS_SCROLL_DELTA_SCREENS, 0.5);
+ else if ((state.right_shift || state.left_shift)
+ && down && sc == SC_X1_PGDN) /* XXX */
+ console_scrollback (CONS_SCROLL_DELTA_SCREENS, -0.5);
+ else if (down && sc < sizeof (sc_x1_to_kc)/sizeof (sc_x1_to_kc[0]))
+ {
+ if (modifier >= 0 && sc_x1_to_kc[sc][modifier])
+ {
+ assert (size < 101 - strlen(sc_x1_to_kc[sc][modifier]));
+ strcpy (&buf[size], sc_x1_to_kc[sc][modifier]);
+ size += strlen (sc_x1_to_kc[sc][modifier]);
+ }
+ }
+ }
+ else if (state.extended == 2)
+ state.extended = 3;
+ else if (state.extended == 3)
+ state.extended = 0;
+
+ if (size)
+ console_input (buf, size);
+ }
+ return 0;
+}
+
+
+
+
+static const char doc[] = "PC Keyboard Driver";
+
+static const struct argp_option options[] =
+ {
+ {"repeat", 'r', "NODE", OPTION_ARG_OPTIONAL,
+ "Set a repeater translator on NODE (default: " DEFAULT_REPEATER_NODE ")"},
+ { 0 }
+ };
+
+static error_t
+parse_opt (int key, char *arg, struct argp_state *state)
+{
+ int *pos = (int *) state->input;
+
+ switch (key)
+ {
+ case 'r':
+ repeater_node = arg ? arg: DEFAULT_REPEATER_NODE;
+ break;
+
+ case ARGP_KEY_END:
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+
+ *pos = state->next;
+ return 0;
+}
+
+static struct argp argp = {options, parse_opt, 0, doc};
+
+/* Initialize the PC keyboard driver. */
+static error_t
+pc_kbd_init (void **handle, int no_exit, int argc, char *argv[], int *next)
+{
+ error_t err;
+ int pos = 1;
+
+ /* Parse the arguments. */
+ err = argp_parse (&argp, argc, argv, ARGP_IN_ORDER | ARGP_NO_EXIT
+ | ARGP_SILENT, 0 , &pos);
+ *next += pos - 1;
+
+ if (err && err != EINVAL)
+ return err;
+
+
+ return 0;
+}
+
+
+/* Start the PC keyboard driver. */
+static error_t
+pc_kbd_start (void *handle)
+{
+ error_t err;
+ device_t device_master;
+
+ cd = iconv_open ("UTF-8", "WCHAR_T");
+ if (cd == (iconv_t) -1)
+ return errno;
+
+ err = get_privileged_ports (0, &device_master);
+ if (err)
+ {
+ iconv_close (cd);
+ return err;
+ }
+
+ err = device_open (device_master, D_READ | D_WRITE, "@>=kbd", &kbd_dev);
+ if (err == D_NO_SUCH_DEVICE)
+ {
+ /* GNU Mach v1 has a different device. */
+ gnumach_v1_compat = 1;
+ err = device_open (device_master, D_READ, "kbd", &kbd_dev);
+ }
+
+ mach_port_deallocate (mach_task_self (), device_master);
+ if (err)
+ {
+ iconv_close (cd);
+ return err;
+ }
+
+ if (gnumach_v1_compat)
+ {
+ int data = KB_EVENT;
+ err = device_set_status (kbd_dev, KDSKBDMODE, &data, 1);
+ if (err)
+ {
+ device_close (kbd_dev);
+ mach_port_deallocate (mach_task_self (), kbd_dev);
+ iconv_close (cd);
+ return err;
+ }
+ }
+ update_leds ();
+
+ err = driver_add_input (&pc_kbd_ops, NULL);
+ if (err)
+ {
+ if (gnumach_v1_compat)
+ {
+ int data = KB_ASCII;
+ device_set_status (kbd_dev, KDSKBDMODE, &data, 1);
+ }
+ device_close (kbd_dev);
+ mach_port_deallocate (mach_task_self (), kbd_dev);
+ iconv_close (cd);
+ return err;
+ }
+
+ if (repeater_node)
+ kbd_setrepeater (repeater_node, &cnode);
+
+ cthread_detach (cthread_fork (input_loop, NULL));
+
+ return 0;
+}
+
+/* Deinitialize the PC keyboard driver. */
+static error_t
+pc_kbd_fini (void *handle, int force)
+{
+ driver_remove_input (&pc_kbd_ops, NULL);
+ if (gnumach_v1_compat)
+ {
+ int data = KB_ASCII;
+ device_set_status (kbd_dev, KDSKBDMODE, &data, 1);
+ }
+ device_close (kbd_dev);
+ mach_port_deallocate (mach_task_self (), kbd_dev);
+ iconv_close (cd);
+
+ console_unregister_consnode (cnode);
+ console_destroy_consnode (cnode);
+
+ return 0;
+}
+
+
+/* Set the scroll lock status indication (Scroll LED) to ONOFF. */
+static error_t
+pc_kbd_set_scroll_lock_status (void *handle, int onoff)
+{
+ led_state.scroll_lock = onoff;
+ update_leds ();
+ return 0;
+}
+
+
+struct driver_ops driver_pc_kbd_ops =
+ {
+ pc_kbd_init,
+ pc_kbd_start,
+ pc_kbd_fini
+ };
+
+static struct input_ops pc_kbd_ops =
+ {
+ pc_kbd_set_scroll_lock_status,
+ NULL
+ };