diff options
Diffstat (limited to 'console-client')
-rw-r--r-- | console-client/Makefile | 52 | ||||
-rw-r--r-- | console-client/pc-kbd.c | 282 | ||||
-rw-r--r-- | console-client/xkb/MISSING-FEATURES | 32 | ||||
-rw-r--r-- | console-client/xkb/README | 178 | ||||
-rw-r--r-- | console-client/xkb/TODO | 7 | ||||
-rw-r--r-- | console-client/xkb/compose.c | 593 | ||||
-rw-r--r-- | console-client/xkb/kstoucs.c | 50 | ||||
-rw-r--r-- | console-client/xkb/kstoucs_map.sh | 20 | ||||
-rw-r--r-- | console-client/xkb/lex.l | 386 | ||||
-rw-r--r-- | console-client/xkb/parser.y | 1605 | ||||
-rw-r--r-- | console-client/xkb/xkb-data/keymap/hurd | 392 | ||||
-rw-r--r-- | console-client/xkb/xkb-data/symbols/hurd | 125 | ||||
-rw-r--r-- | console-client/xkb/xkb-data/types/hurd | 18 | ||||
-rw-r--r-- | console-client/xkb/xkb.c | 1380 | ||||
-rw-r--r-- | console-client/xkb/xkb.h | 431 | ||||
-rw-r--r-- | console-client/xkb/xkbdata.c | 464 | ||||
-rw-r--r-- | console-client/xkb/xkbtimer.c | 231 |
17 files changed, 6236 insertions, 10 deletions
diff --git a/console-client/Makefile b/console-client/Makefile index 3ec682b3..a5c14ffe 100644 --- a/console-client/Makefile +++ b/console-client/Makefile @@ -31,17 +31,28 @@ NCURSESW_SO_SRCS = ncursesw.c endif SRCS = $(CONSOLE_SRCS) \ $(VGA_SO_SRCS) $(PC_KBD_SO_SRCS) $(PC_MOUSE_SO_SRCS) \ - $(GENERIC_SPEAKER_SO_SRCS) $(CURRENT_VCS_SO_SRCS) $(NCURSESW_SO_SRCS) + $(GENERIC_SPEAKER_SO_SRCS) $(CURRENT_VCS_SO_SRCS) $(NCURSESW_SO_SRCS) \ + $(XKB_SRCS) -OBJS = $(SRCS:.c=.o) kdioctlServer.o +VPATH += $(srcdir)/xkb +OBJS = $(addsuffix .o,$(basename $(notdir $(SRCS)))) kdioctlServer.o HURDLIBS = cons threads ports netfs fshelp iohelp ihash shouldbeinlibc LDLIBS = -ldl module-dir = $(libdir)/hurd/console console-LDFLAGS = -Wl,-E +CPPFLAGS += -I$(srcdir)/xkb +LFLAGS = -i +YFLAGS = -by +XKB_DATA_FILES = keymap/hurd types/hurd symbols/hurd + # In seeking, thou shalt find it! CPPFLAGS += -DQUAERENDO_INVENIETIS +DIST_FILES = xkb/HACKING xkb/MISSING-FEATURES xkb/README xkb/TODO \ + xkb/kstoucs_map.sh \ + $(addprefix xkb/xkb-data/, $(XKB_DATA_FILES)) + include ../Makeconf driver-CPPFLAGS = -D'CONSOLE_DEFPATH="$(module-dir)\0"' \ @@ -57,7 +68,7 @@ modules = vga pc_kbd generic_speaker pc_mouse current_vcs vga-CPPFLAGS = -DDEFAULT_VGA_FONT_DIR=\"${datadir}/hurd/\" vga.so.$(hurd-version): $(patsubst %.c,%_pic.o,$(VGA_SO_SRCS)) -pc_kbd.so.$(hurd-version): $(patsubst %.c,%_pic.o,$(PC_KBD_SO_SRCS)) \ +pc_kbd.so.$(hurd-version): $(patsubst %.c,%_pic.o,$(PC_KBD_SO_SRCS)) \ kdioctlServer_pic.o pc_mouse.so.$(hurd-version): $(patsubst %.c,%_pic.o,$(PC_MOUSE_SO_SRCS)) generic_speaker.so.$(hurd-version): $(patsubst %.c,%_pic.o,$(GENERIC_SPEAKER_SO_SRCS)) @@ -88,3 +99,38 @@ $(module-dir)/%: % $(CC) -shared -Wl,-soname=$@ -o $@ $(rpath) \ $(CFLAGS) $($*-CFLAGS) $(LDFLAGS) \ '-Wl,-(' $($*-LDLIBS) '-Wl,-)' $^ + +lex.c: lex.l parser.tab.h +parser.tab.h: parser.y + if $(YACC) $(YFLAGS) -d $<; then \ + mv y.tab.h $@; \ + rm y.tab.c; \ + fi + +XKB_SRCS = xkb/compose.c xkb/kstoucs.c xkb/parser.y xkb/lex.l \ + xkb/xkb.c xkb/xkbdata.c xkb/xkbtimer.c +ifeq ($(HAVE_X11),yes) +XKB_UNITS = $(basename $(notdir $(XKB_SRCS))) +pc_kbd.so.$(hurd-version): $(addsuffix _pic.o,$(XKB_UNITS)) +pc-kbd-CFLAGS = -DXKB_SUPPORT -DXKB_DATA_DIR=\"$(XKB_BASE)\" $(X11_CFLAGS) +$(foreach XKB_UNIT, $(XKB_UNITS), $(eval $(XKB_UNIT)-CFLAGS = $(X11_CFLAGS))) +compose-CFLAGS += -DDATADIR=\"$(datadir)\" +pc_kbd-LDLIBS = $(X11_LIBS) +install: $(XKB_BASE) $(addprefix $(XKB_BASE)/, $(XKB_DATA_FILES)) + +$(XKB_BASE): + @$(MKINSTALLDIRS) $@ + +$(XKB_BASE)/%: xkb/xkb-data/% + $(INSTALL_DATA) $< $@ + +# FIXME: dependency files won't catch this one. +$(srcdir)/xkb/kstoucs.c: kstoucs_map.c +kstoucs_map.c: $(X11_KEYSYMDEF_H) $(srcdir)/xkb/kstoucs_map.sh + SED=$(SED) \ + AWK=$(AWK) \ + sh $(srcdir)/xkb/kstoucs_map.sh \ + < $< \ + > map.tmp && \ + mv map.tmp $@ +endif diff --git a/console-client/pc-kbd.c b/console-client/pc-kbd.c index d66e94b3..95109e52 100644 --- a/console-client/pc-kbd.c +++ b/console-client/pc-kbd.c @@ -34,6 +34,10 @@ #include "driver.h" #include "mach-inputdev.h" +#ifdef XKB_SUPPORT +#include "xkb/xkb.h" +#endif + /* The default name of the node of the repeater. */ #define DEFAULT_REPEATER_NODE "kbd" @@ -41,8 +45,11 @@ /* The keyboard device in the kernel. */ static device_t kbd_dev; -/* The converter. */ -static iconv_t cd; +/* The converter. + XXX: Here it is used by the fixed US layout code. It's not static because + xkb.c also needs a converter. This variable and it's initialization should + be moved there once XKB is supported in OSKit. */ +iconv_t cd; /* The status of various LEDs. */ struct { @@ -62,6 +69,18 @@ static char *repeater_node; /* The repeater node. */ static consnode_t cnode; + +#ifdef XKB_SUPPORT +/* CTRL + Alt + Backspace will terminate the console client by + default, this hardcoded behaviour can be disabled. */ +static int ctrlaltbs; + +/* The number of jiffies until repetition starts. */ +static int xkb_repeat_delay; + +/* The number of jiffies until the next repetition is generated. */ +static int xkb_repeat_interval; +#endif /* A list of scan codes generated by the keyboard, in the set 2 encoding. */ enum scancode @@ -597,6 +616,10 @@ gnumach_v1_input_next () } while (data_buf.type != KEYBD_EVENT); +#ifdef XKB_SUPPORT + /* XKB code work with set1 scancodes. */ + return data_buf.value.sc; +#else /* not XKB_SUPPORT */ /* 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 @@ -609,6 +632,7 @@ gnumach_v1_input_next () sc = sc_set1_to_set2[data_buf.value.sc &~ SC_SET1_FLAG_UP]; return sc | (up ? SC_FLAG_UP : 0); +#endif /* not XKB_SUPPORT */ } @@ -676,11 +700,139 @@ input_next () return sc; } +#ifdef XKB_SUPPORT +/* Read a keycode using the input_next routine. The translation from + scancodes is hardcoded. A configuration file should be used in the + near future because this is an UGLY HACK. */ +keycode_t +read_keycode (void) +{ + scancode_t sc = input_next (); + int release = 0; + + /* The keypress generated two keycodes. */ + if (sc == 0xE0) + { + sc = input_next (); + + release = sc & 0x80; + sc &= ~0x80; + + switch (sc) + { + case 0x1D: /* RCTRL. */ + sc = 101; + break; + case 0x38: /* RALT. */ + sc = 105; + break; + /* LRGUI MENU. */ + case 0x5B: /* LGUI. */ + sc = 107; + break; + case 0x5C: /* RGUI. */ + sc = 108; + break; + case 0x5D: /* MENU. */ + sc = 109; + break; + case 0x52: /* Insert. */ + sc = 98; + break; + case 0x47: /* Home. */ + sc = 89; + break; + case 0x49: /* Pg Up. */ + sc = 91; + break; + case 0x53: /* Delete. */ + sc = 99; + break; + case 0x4F: /* End. */ + sc = 95; + break; + case 0x51: /* Pg Down. */ + sc = 97; + break; + case 0x48: /* Arrow up. */ + sc = 90; + break; + case 0x50: /* Arrow down. */ + sc = 96; + break; + case 0x4b: /* Arrow left. */ + sc = 92; + break; + case 0x4d: /* Arrow right. */ + sc = 94; + break; + case 0x35: /* '/' */ + sc = 104; + break; + case 0x1C: /* KP_Enter. */ + sc = 100; + break; + default: + sc += 0x78; + } + + sc |= release; + } + else + release = sc & 0x80; + + return sc; +} +#endif /* XKB_SUPPORT */ /* The input loop. */ static any_t input_loop (any_t unused) { +#ifdef XKB_SUPPORT + /* XXX: until conversion from scancode to keycode is properly implemented + XKB won't work on anything but scancode set 1. + In the meanwhile, a fixed US layout implementation is used for OSKit + Mach. **/ + if (gnumach_v1_compat) + { + /* The previous keypress. */ + keycode_t prevkey = 0; + + while (1) + { + keypress_t key; + + key.keycode = read_keycode () + min_keys; + key.rel = key.keycode & 0x80; + key.redir = 0; + + if (!key.rel && key.keycode == prevkey) + key.repeat = 1; + else + key.repeat = 0; + + if (key.repeat) + continue; + + /* The keycombination CTRL+Alt+Backspace terminates the console + client. Keycodes instead of modifiers+symbols are used to + make it able to exit the client, even when the keymaps are + faulty. */ + if ((keystate[64].keypressed || keystate[113].keypressed) /* Alt */ + && (keystate[37].keypressed || keystate[109].keypressed) /* CTRL*/ + && keystate[22].keypressed && ctrlaltbs) /* Backspace. */ + console_exit (); + + if (!key.repeat) + xkb_input_key (key.keycode); + prevkey = key.keycode; + } + + return 0; + } +#endif /* XKB_SUPPORT */ + while (1) { enum scancode fsc = input_next (); @@ -1029,8 +1181,43 @@ input_loop (any_t unused) static const char doc[] = "PC Keyboard Driver"; +struct arguments +{ + int pos; +#ifdef XKB_SUPPORT + char *xkbdir; + char *keymapfile; + char *keymap; + char *composefile; + int ctrlaltbs; + int repeat_delay; + int repeat_interval; +#endif +}; + static const struct argp_option options[] = { +#ifdef XKB_SUPPORT +/* Some random ids for options available only in long form. */ +#define REPEAT_DELAY_ID 25425 +#define REPEAT_INTERVAL_ID 5322 + {"xkbdir", 'x', "DIR", 0, + "Directory containing the XKB configuration files" }, + {"keymapfile", 'f', "FILE", 0, + "File containing the keymap" }, + {"keymap", 'k', "SECTIONNAME" , 0, + "Choose keymap"}, + {"compose", 'o', "COMPOSEFILE", 0, + "Compose file to load (default none)"}, + {"ctrlaltbs", 'c', 0 , 0, + "CTRL + Alt + Backspace will exit the console client (default)."}, + {"no-ctrlaltbs", 'n', 0 , 0, + "CTRL + Alt + Backspace will not exit the console client."}, + {"repeat-delay", REPEAT_DELAY_ID, "DELAY", 0, + "Delay before pressed key starts repeating (measured in jiffies)"}, + {"repeat-interval", REPEAT_INTERVAL_ID, "INTERVAL", 0, + "Time elapsed between repeated keys (measured in jiffies)"}, +#endif /* XKB_SUPPORT */ {"repeat", 'r', "NODE", OPTION_ARG_OPTIONAL, "Set a repeater translator on NODE (default: " DEFAULT_REPEATER_NODE ")"}, { 0 } @@ -1039,10 +1226,43 @@ static const struct argp_option options[] = static error_t parse_opt (int key, char *arg, struct argp_state *state) { - int *pos = (int *) state->input; + struct arguments *arguments = state->input; switch (key) { +#ifdef XKB_SUPPORT + case 'x': + arguments->xkbdir = arg; + break; + + case 'f': + arguments->keymapfile = arg; + break; + + case 'k': + arguments->keymap = arg; + break; + + case 'o': + arguments->composefile = arg; + break; + + case 'c': + arguments->ctrlaltbs = 1; + break; + + case 'n': + arguments->ctrlaltbs = 0; + break; + + case REPEAT_DELAY_ID: + arguments->repeat_delay = atoi(arg); + break; + + case REPEAT_INTERVAL_ID: + arguments->repeat_interval = atoi(arg); + break; +#endif /* XKB_SUPPORT */ case 'r': repeater_node = arg ? arg: DEFAULT_REPEATER_NODE; break; @@ -1054,7 +1274,7 @@ parse_opt (int key, char *arg, struct argp_state *state) return ARGP_ERR_UNKNOWN; } - *pos = state->next; + arguments->pos = state->next; return 0; } @@ -1065,16 +1285,61 @@ static error_t pc_kbd_init (void **handle, int no_exit, int argc, char *argv[], int *next) { error_t err; - int pos = 1; + struct arguments arguments = + { + pos: 1 +#ifdef XKB_SUPPORT + , xkbdir: 0 + , keymapfile: 0 + , keymap: 0 + , composefile: 0 + , ctrlaltbs: 1 + , repeat_delay: -1 + , repeat_interval: -1 +#endif + }; /* Parse the arguments. */ err = argp_parse (&argp, argc, argv, ARGP_IN_ORDER | ARGP_NO_EXIT - | ARGP_SILENT, 0 , &pos); - *next += pos - 1; + | ARGP_SILENT, 0 , &arguments); + *next += arguments.pos - 1; if (err && err != EINVAL) return err; +#ifdef XKB_SUPPORT + if (!arguments.xkbdir) + { + arguments.xkbdir = XKB_DATA_DIR; + } + if (!arguments.keymapfile) + { + arguments.keymapfile = "keymap/hurd"; + } + if (arguments.repeat_delay <= 0) + { + arguments.repeat_delay = 50; + } + if (arguments.repeat_interval <= 0) + { + arguments.repeat_interval = 10; + } + + ctrlaltbs = arguments.ctrlaltbs; + xkb_repeat_delay = arguments.repeat_delay; + xkb_repeat_interval = arguments.repeat_interval; + + err = read_composefile (arguments.composefile); + if (err) + return err; + + xkb_data_init (); + err = xkb_load_layout (arguments.xkbdir, arguments.keymapfile, + arguments.keymap); + + if (err) + return err; +#endif /* XKB_SUPPORT */ return 0; } @@ -1124,6 +1389,9 @@ pc_kbd_start (void *handle) iconv_close (cd); return err; } +#ifdef XKB_SUPPORT + xkb_init_repeat (xkb_repeat_delay, xkb_repeat_interval); +#endif } update_leds (); diff --git a/console-client/xkb/MISSING-FEATURES b/console-client/xkb/MISSING-FEATURES new file mode 100644 index 00000000..237c13d7 --- /dev/null +++ b/console-client/xkb/MISSING-FEATURES @@ -0,0 +1,32 @@ +Required for 100% compatibility with XKB: + +- Jukebox (possibility that this won't ever be done) +- Loading the keydatabase +- Proper indicator support +- ISOLock +- key lock +- radio groups +- overlays +- Latching (properly, mostly works) +- Enabling/disabling controls +- The following controls are not implemented at all: + - SlowKeys + - BounceKeys + - StickyKeys + - MouseKeysAccel + - AccesXKeys + - AccesXTimeout + - AccessXFreedback (jukebox required) + - Overlay1 + Overlay2 + - AudibleBell + - IgnoreGroupLock + - EnabledControls + +Hurd features: + +- Binding a string to a key (requires new action) + (It should be possible to insert this on other vcs too) +- Binding executable to a key (requires new action) + +I'm sure I forgot some missing features, please report them: +metgerards@student.han.nl diff --git a/console-client/xkb/README b/console-client/xkb/README new file mode 100644 index 00000000..de781143 --- /dev/null +++ b/console-client/xkb/README @@ -0,0 +1,178 @@ +-- Some random notes about the XKB module. -- + +This XKB module used by the pc_kbd input driver can load XKB configuration +files. XKB configuration files, the keymap files currently used in XFree, +are very versatile. + +One of the biggest advantages of using XKB configurations is that both +the Hurd and XFree work similarly. Another advantage is that it has many +features: + +* Human readable configuration files with mechanisms like include + files, replacing keys, etc. + +* Many shift levels, all configurable per key type. + +* Support for four groups. These groups can be used for things like + additional alphabets. + +* Accessibility features like SlowKeys, StickyKeys, etc. + +* Dynamic behaviour; the user can define how all keys and indicators operate. + + +Most support for basic operation have been implemented. Still there +are many things not implemented. If some of these things are really +important to you, please tell me! + +List of not implemented XKB features: + +* Accessibility and software repeat. +* Jukebox support (user configurable audible messages) +* Key database; loading new keynames from the X key database. +* Error handling; partially done. +* Indicators (Keyboard LEDs, etc.) not working, partially implemented. +* ISOLock not implemented. +* No support for non UTF-8 locales. +* No support for rarely used features (Many of they don't even work in + X AFAIK). +* Many other stuff doesn't work (lock, radio group, overlays, etc.). + If I forgot something important, please report it :) + +* The scanner and parser are still ugly and far from optimal! + +--- + +Installation: + +This XKB module is an optional feature of pc-kbd input driver. Whether +it is compiled in or not is defined by the precense of XKB_SUPPORT +preprocessor symbol. + +In addition to the usual Hurd dependencies, you need yacc and lex +programs to generate the scanner and parser. + +-- + +Using it: + +To use it you need XKB configuration files. These are included with +XFree and will be placed in /share/X11/xkb on most systems. + +This module adds the following set of options to pc-kbd for overriding +the default behaviour: + +--xkbdir : The root directory of the xkb configuration, by default + this is /share/X11/xkb. + +--keymapfile : The file that hold the descriptions of the default + keymaps file. This file holds the description of all keymaps. This + path should be relative to the path set by `xkbdir'. By default + "keymap/xfree86" is used. + +--keymap : The keymap to use. By default en_US is used. Examples of + some other keymaps are: fr, us, de, dvorak. Example: --keymap=de + +--ctrlaltbs : CTRL+Alt+Backspace will exit the console client. +--no-ctrlaltbs : CTRL+Alt+Backspace will not exit the console client. + +--repeat-delay : The number of jiffies the driver should wait before + starting to repeat keys. By default this is 50 jiffies. + +--repeat-interval : The number of jiffies the driver should wait + between each repeated key. By default this is 10 jiffies. + +I wrote some XKB extensions and configuration files to use these +extensions. You can find these files in the xkb-data directory. + +The build system will copy these files to where your XKB +configuration files are (usually /share/X11/xkb). + +for example: xkb-data/keymap/hurd will be copied to /share/X11/xkb/keymap. + +To use these extensions you have to use the hurd keymaps file. This +holds all keymaps extended with Hurd features. Use "--keymapfile +keymap/hurd" to select this file. + +After that you can use these keybindings: + +Alt + F1...F12: Switch VC. + +Alt + Left, Right: Switch to the console left or right of the current + console. + +Alt + Up, Down: Scroll the console by one line up or down. +Ctrl + PgUp, PgDown: Scroll the console up or down with 1/2 screen. + +Alt + Home: Scroll the console to the 0% of the scrollback buffer. +Ctrl + Home: Scroll the console to the 25% of the scrollback buffer. +Alt + End: Scroll the console to the 100% of the scrollback buffer. +Alt + End: Scroll the console to the 75% of the scrollback buffer. + +-- + +Hurd features: + +If you want to use the Hurd features yourself make sure you put the +changes in files that are not used by Xfree because it cannot parse +these options. See how I did it for the configuration described in the +previous paragraph. + +Function list: + +-- +ConsScroll: scroll the console x lines or to a fixed position. + +parameters: +Screen = pos : Scroll to screen #pos. +Screen += i : Scroll i screens up. +Screen -= i : Scroll i screens down. +line = pos : Scroll to line #pos. +line += i : Scroll i lines up. +line -= i : Scroll i lines down. +percent = percentage : Scroll to PERCENTAGE % of the scrollback + buffer. + +Example: +ConsScroll (screen += 0.5) --> Scroll down a 1/2 screen. + +-- +SwitchScreen: Switch to a VC. + +paramaters: +screen = i : Switch to VC #i. +screen += i : Switch to #i consoles to the right. +screen -= i : Switch to #i consoles to the left. + +sameserver is a flag that will be added. I want to use it to switch to +external consoles like X servers and SVGALib screens. + +example: +SwitchScreen (screen += 1) --> Switch to the console right of the + current console. + + +-- +More functions will be added. One to send keysyms to other VCs (can be +usefull to control a mixer, ogg vorbis player, etc.). It should also +be capable of inserting often used strings like "apfelkorn" :). + +A function to call the configuration dialog should be added. + +Functions to execute things should be added. Think about binding the +reboot command to Ctrl+Alt+Delete for example. Wolfgang also asked me +for scheme support, but I don't know enough about scheme to know what +cool stuff is possible with this (I don't know scheme... Hey! Not +everyone can be perfect ;)). + +Perhaps functions can be added to control the console client in other +ways. + +More ideas would be interesting to hear :) + +-- + +Enjoy this stuff and please send me many bug reports :) + +Marco Gerards +(metgerards@student.han.nl) diff --git a/console-client/xkb/TODO b/console-client/xkb/TODO new file mode 100644 index 00000000..275cbaf5 --- /dev/null +++ b/console-client/xkb/TODO @@ -0,0 +1,7 @@ +For release: + +Indicators. +Use libihash. +Better memory handling. +Better debug output +CLEAN UP! diff --git a/console-client/xkb/compose.c b/console-client/xkb/compose.c new file mode 100644 index 00000000..65854e14 --- /dev/null +++ b/console-client/xkb/compose.c @@ -0,0 +1,593 @@ +/* compose.c -- Keysym composing + + Copyright (C) 2003 Marco Gerards + + Written by Marco Gerards <metgerards@student.han.nl> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <X11/keysymdef.h> +#include "xkb.h" +#include <ctype.h> +#include <string.h> +#include <fcntl.h> +#include <unistd.h> +#include <locale.h> +#include <assert.h> + +/* Tokens that can be recognised by the scanner. */ +enum tokentype + { + UNKNOWN, + EOL, + REQKS, + SEMICOL, + STR, + PRODKS, + END + }; + +/* The current token. */ +struct token +{ + enum tokentype toktype; + char value[50]; +} tok; + +/* The linenumber of the line currently parsed, used for returning + errors and warnings. */ +static int linenum; + +/* Read a token from the file CF. */ +static void +read_token (FILE *cf) +{ + int c = fgetc (cf); + int pos = 0; + + /* Remove whitespaces. */ + while (c == ' ' || c == '\t') + c = fgetc (cf); + + /* Comment, remove until end of line and return a EOL token. */ + if (c == '#') + { + while (c != '\n') + c = fgetc (cf); + tok.toktype = EOL; + linenum++; + return; + } + + /* End of file. */ + if (c == EOF) + { + tok.toktype = END; + return; + } + + /* Semicolon. */ + if (c == ':') + { + tok.toktype = SEMICOL; + return; + } + + /* End of line. */ + if (c == '\n') + { + linenum++; + tok.toktype = EOL; + return; + } + + + /* Required keysym. */ + if (c == '<') + { + while ((c = fgetc (cf)) != '>') + tok.value[pos++] = c; + tok.value[pos] = '\0'; + tok.toktype = REQKS; + return; + } + + /* Character string. */ + if (c == '"') + { + while ((c = fgetc (cf)) != '"') + { + if (c == '\\') + c = fgetc (cf); + + tok.value[pos++] = c; + } + tok.value[pos] = '\0'; + tok.toktype = STR; + return; + } + + /* Produced keysym. */ + if (isalpha (c)) + { + tok.value[pos++] = c; + while (isgraph (c = fgetc (cf))) + tok.value[pos++] = c; + tok.value[pos] = '\0'; + tok.toktype = PRODKS; + ungetc (c, cf); + return; + } + + /* Unknown token. */ + tok.toktype = UNKNOWN; + return; +} + +/* Compose sequence. */ +struct compose +{ + struct compose *left; + struct compose *right; + symbol *expected; + symbol produced; +} *compose_tree; + + +/* Compare symbol sequence s1 to symbol sequence s1. This function + works just like the strcmp function. */ +static int +symbolscmp (symbol *s1, symbol *s2) +{ + while (*s1 && *s2 && (*s1 == *s2)) + { + s1++;s2++; + } + if (*s1 < *s2) + return -1; + if (*s1 > *s2) + return 1; + return 0; +} + +/* Compare symbol sequence s1 to symbol sequence s1, compare a maximum + of N symbols. This function works just like the strcmp function. + */ +static int +symbolsncmp (symbol *s1, symbol *s2, int n) +{ + int cnt = 0; + while (*s1 && *s2 && (*s1 == *s2)) + { + if (++cnt == n) + break; + s1++;s2++; + } + + if (*s1 < *s2) + return -1; + if (*s1 > *s2) + return 1; + return 0; +} + + +/* Add the compose sequence EXP to the binary tree, store RESULT as + the keysym produced by EXP. */ +static struct compose * +composetree_add (struct compose *tree, symbol *exp, symbol result) +{ + int cmp; + + if (tree == NULL) + { + tree = malloc (sizeof (struct compose)); + tree->expected = exp; + tree->produced = result; + tree->left = tree->right = NULL; + + return tree; + } + + cmp = symbolscmp (exp, tree->expected); + if (cmp == 0) + { + printf ("Warning: line %d: Double sequence.\n", linenum); + free (exp); + } + else if (cmp < 0) + tree->left = composetree_add (tree->left, exp, result); + else + tree->right = composetree_add (tree->right, exp, result); + return tree; +} + +/* Parse the composefile CF and put all sequences in the binary tree + COMPOSE_TREE. This function may never fail because of syntactical or + lexalical errors, generate a warning instead. */ +static error_t +parse_composefile (FILE *cf) +{ + void skip_line (void) + { + while (tok.toktype != EOL && tok.toktype != END) + read_token (cf); + } + + for (;;) + { + /* Expected keysyms. */ + symbol exp[50]; + symbol *exps; + size_t expcnt = 0; + symbol sym; + + read_token (cf); + /* Blank line. */ + if (tok.toktype == EOL) + continue; + + /* End of file, done parsing. */ + if (tok.toktype == END) + return 0; + + if (tok.toktype != REQKS) + { + printf ("Warning: line %d: Keysym expected on beginning of line.\n", + linenum); + skip_line (); + continue; + } + + /* Keysym was recognised, add it. */ + sym = XStringToKeysym (tok.value); + if (!sym) + { + printf ("Warning: line %d: Unknown keysym \"%s\".\n", linenum, + tok.value); + skip_line (); + continue; + } + exp[expcnt++] = sym; + + do + { + read_token (cf); + /* If another required keysym is recognised, add it. */ + if (tok.toktype == REQKS) + { + sym = XStringToKeysym (tok.value); + if (!sym) + { + printf ("Warning: line %d: Unknown keysym \"%s\".\n", + linenum, tok.value); + skip_line (); + continue; + } + exp[expcnt++] = sym; + } + } while (tok.toktype == REQKS); + + if (tok.toktype != SEMICOL) + { + printf ("Warning: line %d: Semicolon expected.\n", linenum); + skip_line (); + continue; + } + + read_token (cf); + /* Force token and ignore it. */ + if (tok.toktype != STR) + { + printf ("Warning: line %d: string expected.\n", linenum); + skip_line (); + continue; + } + + read_token (cf); + if (tok.toktype != PRODKS) + { + printf ("Warning: line %d: keysym expected.\n", linenum); + skip_line (); + continue; + } + sym = XStringToKeysym (tok.value); + if (!sym) + { + printf ("Warning: line %d: Unknown keysym \"%s\".\n", linenum, + tok.value); + skip_line (); + continue; + } + + read_token (cf); + if (tok.toktype != EOL && tok.toktype != END) + { + printf ("Warning: line %d: end of line or end of file expected.\n", + linenum); + skip_line (); + continue; + } + + /* Add the production rule. */ + exp[expcnt++] = 0; + exps = malloc (sizeof (symbol) * expcnt); + memcpy (exps, exp, sizeof (symbol) * expcnt); + compose_tree = composetree_add (compose_tree, exps, sym); + } + return 0; +} + +/* Read keysyms passed to this function by S until a keysym can be + composed. If the first keysym cannot start a compose sequence return + the keysym. */ +symbol +compose_symbols (symbol s) +{ + /* Current position in the compose tree. */ + static struct compose *treepos = NULL; + /* Current compose sequence. */ + static symbol syms[100]; + /* Current position in the compose sequence. */ + static int pos = 0; + int cmp; + + if (!treepos) + treepos = compose_tree; + + /* Maximum sequence length reached. Some idiot typed this many + symbols and now we throw it all away, wheee!!! */ + if (pos == 99) + { + treepos = compose_tree; + pos = 0; + } + + /* Put the keysym in the compose sequence array. */ + syms[pos++] = s; + syms[pos] = 0; + + /* Search the tree for a keysym sequence that can match the current one. */ + while (treepos) + { + cmp = symbolsncmp (syms, treepos->expected, pos); + if (cmp == 0) + { + /* The keysym sequence was partially recognised, check if it + can completely match. */ + if (!symbolscmp (syms, treepos->expected)) + { + symbol ret = treepos->produced; + treepos = compose_tree; + pos = 0; + return ret; + } + + /* The sequence was partially recognised. */ + return -1; + } + + if (cmp < 0) + treepos = treepos->left; + else + treepos = treepos->right; + } + + /* Nothing can be found. */ + treepos = compose_tree; + + /* This ks should've started a sequence but couldn't be found, + just return it. */ + if (pos == 1) + { + pos = 0; + return s; + } + + debug_printf ("Invalid\n"); + /* Invalid keysym sequence. */ + pos = 0; + return -1; +} + +struct map_entry +{ + const char *left; + const char *right; +}; + +enum callback_result + { + NEXT, + DONE + }; + +typedef enum callback_result (*map_callback) (void *context, struct map_entry *entry); + +static error_t +map_iterate(const char *map_path, map_callback action, void *context) +{ + FILE *map; + char *buffer = NULL; + size_t buffer_size = 0; + size_t line_length = 0; + + assert (map_path != NULL); + assert (action != NULL); + + map = fopen (map_path, "r"); + + if (map == NULL) + return errno; + + while ( (line_length = getline (&buffer, &buffer_size, map)) != -1) + { + /* skips empty lines and comments */ + if (line_length < 1 || buffer[0] == '#') + continue; + else + { + struct map_entry entry = {NULL, NULL}; + char *end = buffer + line_length; + char *p = buffer; + + while (p != end && isspace(*p)) p++; + + if (p == end) + continue; + + entry.left = p; + + while (p != end && !isspace(*p)) p++; + + if (p != end) + { + *(p++) = 0; + while (p != end && isspace(*p)) p++; + + if (p != end) + { + entry.right = p; + while (p != end && !isspace(*p)) p++; + if (p != end) + *p = 0; + } + } + + if (action (context, &entry) == DONE) + break; + } + } + free (buffer); + fclose (map); + return 0; +} + +struct matcher_context +{ + char *value; + char *result; +}; + +static enum callback_result +match_left_set_right (void *context, struct map_entry *entry) +{ + struct matcher_context *ctx = (struct matcher_context *) context; + + if (strcmp (ctx->value, entry->left) == 0) + { + ctx->result = strdup (entry->right); + return DONE; + } + return NEXT; +} + +static enum callback_result +match_right_set_left (void *context, struct map_entry *entry) +{ + struct matcher_context *ctx = (struct matcher_context *) context; + + if (strcmp (ctx->value, entry->right) == 0) + { + ctx->result = strdup (entry->left); + return DONE; + } + return NEXT; +} + +/* Search for a compose file. + + According to Compose(5) man page the compose file searched in the + following locations: + - XCOMPOSEFILE variable. + - .XCompose at $HOME. + - System wide compose file for the current locale. */ +static char * +get_compose_file_for_locale() +{ + struct matcher_context context = { NULL }; + char *xcomposefile; + char *to_be_freed; + char *home; + int err; + + xcomposefile = getenv ("XCOMPOSEFILE"); + if (xcomposefile != NULL) + return strdup (xcomposefile); + + home = getenv ("HOME"); + if (home != NULL) + { + err = asprintf (&xcomposefile, "%s/.XCompose", home); + if (err != -1) + { + if (faccessat(AT_FDCWD, xcomposefile, R_OK, AT_EACCESS) == 0) + return xcomposefile; + else + { + free (xcomposefile); + /* TODO: check and report whether the compose file doesn't exist or + read permission was not granted to us. */ + } + } + } + + context.value = setlocale (LC_ALL, NULL); + map_iterate (DATADIR "/X11/locale/locale.alias", match_left_set_right, &context); + to_be_freed = context.result; + + if (context.result != NULL) + { + /* Current locale is an alias. Use the real name to index the database. */ + context.value = context.result; + } + context.result = NULL; + map_iterate (DATADIR "/X11/locale/compose.dir", match_right_set_left, &context); + free (to_be_freed); + + /* compose.dir contains relative paths to compose files. */ + to_be_freed = context.result; + err = asprintf (&context.result, DATADIR "/X11/locale/%s", context.result); + if (err == -1) + context.result = NULL; + + free (to_be_freed); + return context.result; +} + +/* Read a Compose file. */ +error_t +read_composefile (char *composefn) +{ + FILE *cf; + + error_t err; + + if (composefn == NULL) + composefn = get_compose_file_for_locale (); + + cf = fopen (composefn, "r"); + if (cf == NULL) + return errno; + + err = parse_composefile (cf); + if (err) + fclose (cf); + + return err; +} diff --git a/console-client/xkb/kstoucs.c b/console-client/xkb/kstoucs.c new file mode 100644 index 00000000..b4602e17 --- /dev/null +++ b/console-client/xkb/kstoucs.c @@ -0,0 +1,50 @@ +struct ksmap { + int keysym; + unsigned int ucs; +}; + +#include "kstoucs_map.c" + +unsigned int +KeySymToUcs4 (int keysym) +{ +#ifdef XKB_DEBUG + char *XKeysymToString(int keysym); + printf ("KeySymToUcs4: %s (%d) -> ", XKeysymToString (keysym), keysym); +unsigned int doit (int keysym) +{ +#endif + + /* Control characters not covered by keysym map. */ + if (keysym > 0 && keysym < 32) + return keysym; + + /* 'Unicode keysym' */ + if ((keysym & 0xff000000) == 0x01000000) + return (keysym & 0x00ffffff); + + unsigned int + find_ucs (int keysym, struct ksmap *first, struct ksmap *last) + { + struct ksmap *middle = first + (last - first) / 2; + + if (middle->keysym == keysym) + return middle->ucs; /* base case: needle found. */ + else if (middle == first && middle == last) + return 0; /* base case: empty search space. */ + /* recursive cases: halve search space. */ + else if (middle->keysym < keysym) + return find_ucs (keysym, middle, last); + else if (middle->keysym > keysym) + return find_ucs (keysym, first, middle); + } + + #define NUM_KEYSYMS (sizeof kstoucs_map / sizeof(struct ksmap)) + return find_ucs(keysym, &kstoucs_map[0], &kstoucs_map[NUM_KEYSYMS - 1]); +#ifdef XKB_DEBUG +} + unsigned int ret = doit (keysym); + printf ("%d\n", ret); + return ret; +#endif +} diff --git a/console-client/xkb/kstoucs_map.sh b/console-client/xkb/kstoucs_map.sh new file mode 100644 index 00000000..624d3518 --- /dev/null +++ b/console-client/xkb/kstoucs_map.sh @@ -0,0 +1,20 @@ +#!/bin/sh + +SED=${SED:-sed} +AWK=${AWK:-awk} + +$SED -n -e 's/#define XK_[a-zA-Z0-9_]\+\s\+\(0x[0-9a-fA-F]\+\)\s*\/\*\s*U+\([0-9a-fA-F]\+\).*\*\//\1\t0x\2/p' \ + | $AWK '{ print strtonum($1) "\t" $2; }' \ + | sort -n -k1 \ + | $AWK ' + BEGIN { print "struct ksmap kstoucs_map[] = {" } + { + if (NR == 1) + separator =" "; + else + separator =" , "; + + if (strtonum($1) < 0x1000000) + print separator "{ " $1 ", " $2 " }" + } + END { print "};" }' diff --git a/console-client/xkb/lex.l b/console-client/xkb/lex.l new file mode 100644 index 00000000..d9198981 --- /dev/null +++ b/console-client/xkb/lex.l @@ -0,0 +1,386 @@ +/* XKB scanner. + + Copyright (C) 2002, 03 Marco Gerards + + Written by Marco Gerards <marco@student.han.nl> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. */ + + + #include "xkb.h" + #include "parser.tab.h" + #include <string.h> + #include <unistd.h> + #include <stdlib.h> + #include <stdio.h> + #include <error.h> + + int close_include (void); + int lineno = 1; + char *filename = "foo"; + +%option nodebug + +%option UNPUT +KEYCODE "<"[A-Z][-+A-Z0-9]*">" +DIGIT [0-9] +NUM {DIGIT}{DIGIT}* +FLOAT {DIGIT}{DIGIT}*\.{DIGIT}{DIGIT}* +HEX 0x[A-Za-z0-9]* +IDENTIFIER [a-zA-Z][a-zA-Z0-9_]* +CPPCOMMENT "//".*"\n" +%% + /* When the end of a file is reached, close the + current one and pop the next file to process + of the stack. */ + + /* Filter out comment. */ +{CPPCOMMENT} { lineno++; } + "/*" { + int c; + + while((c = input()) != 0) + { + if(c == '\n') + lineno++; + + else if(c == '*') + { + if((c = input()) == '/') + break; + else + unput(c); + } + } + } + +\n { lineno++; } + + /* Beginning of sections. */ +xkb_keymap { return XKBKEYMAP; } +xkb_symbols { return XKBSYMBOLS; } +xkb_keycodes { return XKBKEYCODES; } +xkb_types { return XKBTYPES; } +xkb_compatibility { return XKBCOMPAT; } +xkb_geometry { return XKBGEOMETRY; } + + + /* Real modifiers. */ +shift { return SHIFT; } +lock { return LOCK; } +control { return CONTROL; } +ctrl { return CONTROL; } +mod1 { return MOD1; } +mod2 { return MOD2; } +mod3 { return MOD3; } +mod4 { return MOD4; } +mod5 { return MOD5; } + + /* Levels. */ +anylevel { yylval.val = 0; return LEVEL; } +levelone { yylval.val = 1; return LEVEL; } +level1 { yylval.val = 1; return LEVEL; } +level2 { yylval.val = 2; return LEVEL; } +level3 { yylval.val = 3; return LEVEL; } +level4 { yylval.val = 4; return LEVEL; } +level[1-9][0-9]* { yylval.val = atoi(yytext + 5); return LEVEL; } + + /* Groups. */ +group1 { yylval.val = 1; return GROUPNUM; } +group2 { yylval.val = 2; return GROUPNUM; } +group3 { yylval.val = 3; return GROUPNUM; } +group4 { yylval.val = 4; return GROUPNUM; } + + /* Booleans */ +true { yylval.val = 1; return BOOLEAN; } +false { yylval.val = 0; return BOOLEAN; } + + /* Interpretation */ +interpret { return INTERPRET; } +usemodmap { return USEMODMAP; } +usemodmapmods { return USEMODMAP; } +modmapmods { return USEMODMAP; } +repeat { return REPEAT; } +locking { return LOCKING; } +locks { return LOCKING; } +virtualmodifier { return VIRTUALMODIFIER; } +virtualmod { return VIRTUALMODIFIER; } + + /* Interpretation match. */ +noneof { yylval.val = 0; return INTERPMATCH ;} +anyofornone { yylval.val = 1; return INTERPMATCH ;} +anyof { yylval.val = 2; return INTERPMATCH ;} +allof { yylval.val = 3; return INTERPMATCH ;} +exactly { yylval.val = 4; return INTERPMATCH ;} + + /* SetMods action. */ +clearlocks { return CLEARLOCKS; } +mods { return MODS; } + +unlock { return UNLOCK; } +both { return BOTH; } +neither { return NEITHER; } + + /* Actions. */ +setmods { return SETMODS; } +latchmods { return LATCHMODS; } +lockmods { return LOCKMODS; } +setgroup { return SETGROUP; } +latchgroup { return LATCHGROUP; } +lockgroup { return LOCKGROUP; } +setptrdflt { return PTRDFLT; } +setpointerdefault { return PTRDFLT; } +lockptrbtn { return LOCKPTRBTN; } +lockpointerbutton { return LOCKPTRBTN; } +lockptrbutton { return LOCKPTRBTN; } +lockpointerbtn { return LOCKPTRBTN; } +setcontrols { return SETCONTROLS; } +lockcontrols { return LOCKCONTROLS; } +terminateserver { return TERMINATE; } +terminate { return TERMINATE; } +switchscreen { return SWITCHSCREEN; } +consscroll { return CONSSCROLL; } +consolescroll { return CONSSCROLL; } +moveptr { return MOVEPTR; } +private { return PRIVATE; } + /* Action parameters. */ +latchtolock { return LATCHTOLOCK; } +group { return GROUP; } +groups { return GROUPS; } +accel { return ACCEL; } +accelerate { return ACCEL; } +default { return DEFAULT; } +count { return COUNT; } +controls { return CONTROLS; } +same { return SAMESERVER; } +sameserver { return SAMESERVER; } +screen { return SCREEN; } +line { return LINE; } +percentage { return PERCENT; } +ptrbtn { return PTRBTN ; } +pointerbutton { return PTRBTN ; } + +action { return ACTION; } +actions { return ACTIONS; } + +whichmodstate { return WHICHMODSTATE; } +whichmodifierstate { return WHICHMODSTATE; } +whichgroupstate { return WHICHGROUPSTATE; } + + /* Match state for indicator. */ +base { yylval.val = 0; return WHICHSTATE; } +latched { yylval.val = 1; return WHICHSTATE; } +locked { yylval.val = 4; return WHICHSTATE; } +effective { yylval.val = 8; return WHICHSTATE; } + + /* Bits for binary controls. */ +repeatkeys { yylval.val = 0; return CONTROLFLAG; } +autorepeat { yylval.val = 0; return CONTROLFLAG; } +accessxkeys { yylval.val = 0; return CONTROLFLAG; } +slowkeys { yylval.val = 0; return CONTROLFLAG; } +bouncekeys { yylval.val = 0; return CONTROLFLAG; } +stickykeys { yylval.val = 0; return CONTROLFLAG; } +accessxtimeout { yylval.val = 0; return CONTROLFLAG; } +accessxfeedback { yylval.val = 0; return CONTROLFLAG; } +mousekeys { yylval.val = 0; return CONTROLFLAG; } +mousekeysaccel { yylval.val = 0; return CONTROLFLAG; } +audiblebell { yylval.val = 0; return CONTROLFLAG; } +ignoregrouplock { yylval.val = 0; return CONTROLFLAG; } + +index { return INDEX; } +name { return NAME; } +symbols { return SYMBOLS; } +key { return KEY; } + + /* Mouse buttons. */ +button1 { yylval.val = 1; return BUTTONNUM; } +button2 { yylval.val = 2; return BUTTONNUM; } +button3 { yylval.val = 3; return BUTTONNUM; } +button4 { yylval.val = 4; return BUTTONNUM; } +button5 { yylval.val = 5; return BUTTONNUM; } +button { return BUTTON; } + + /* Fuzzyness. */ +any { return ANY; } +all { return ALL; } +none { return NONE; } + +defaultbutton { return DEFAULTBTN; } +affect { return AFFECT; } + +allowexplicit { return ALLOWEXPLICIT; } + + /* Feedback to the keyboard. */ +driveskeyboard { return DRIVESKBD; } +driveskbd { return DRIVESKBD; } +ledsdrivekbd { return DRIVESKBD; } +ledsdrivekeyboard { return DRIVESKBD; } +indicatordriveskbd { return DRIVESKBD; } +indicatordriveskeyboard { return DRIVESKBD; } + + +modifier_map { return MODMAP; } +minimum { return MINIMUM; } +maximum { return MAXIMUM; } +virtual { return VIRTUAL; } +alias { return ALIAS; } +indicator { return INDICATOR; } +virtual_modifiers { return VMODS; } +virtualmods { return VMODS; } +vmods { return VMODS; } +type { return TYPE; } +data { return DATA; } +modifiers { return MODS; } +map { return MAP; } +level_name { return LEVEL_NAME; } +preserve { return PRESERVE; } + + /* Section flags. */ +partial { yylval.val = 1; return FLAGS; } +complete { yylval.val = 2; return FLAGS; } +fc { yylval.val = 4; return FLAGS; } +fd { yylval.val = 8; return FLAGS; } +cp { return XKBCOMPAT; } + + /* Section merge modes. */ +include { yylval.mergemode = defaultmm; return INCLUDE; } +augment { yylval.mergemode = augment; return AUGMENT; } +replace { yylval.mergemode = replace; return REPLACE; } +override { yylval.mergemode = override; return OVERRIDE; } + +isolock { return ISOLOCK; } +pointers { return POINTERS;} +ptr { return POINTERS;} +noaction { return NOACTION;} +groupswrap { return GROUPSWRAP; } +wrapgroups { return GROUPSWRAP; } +clampgroups { return GROUPSCLAMP; } +groupsredirect { return GROUPSREDIRECT; } +overlay1 { yylval.val = 1; return OVERLAY; } +overlay2 { yylval.val = 2; return OVERLAY; } + + /* String. */ +\"([^"]|\\\")*\" { + yytext[strlen (yytext) - 1] = '\0'; + yylval.str = strdup (yytext + 1); + return STR; + } + /* Ignore whitespace. */ +[ \t]* + /* A keycode. */ +{KEYCODE} { yylval.str = strdup (yytext) + 1; yylval.str[strlen (yylval.str) - 1] = 0; return KEYCODE; } + /* A float vlaue. */ +{FLOAT} { yylval.dbl = atof (yytext); return FLOAT; } + /* An integer. */ +{NUM} { yylval.val = atoi (yytext); return NUM; } + /* A hexadecimal value. */ +{HEX} { sscanf (yytext, "0x%X", &yylval.val); return HEX; } + /* An identifier. */ +{IDENTIFIER} { yylval.str = strdup (yytext); return IDENTIFIER; } + /* All unrecognized characters. */ +. { return yytext[0]; } +%% + /* Stupid hack, the current version of flex is fucked. */ + #define yytext_ptr yytext + + void + scanner_unput (int c) + { + unput (c); + } + + int + yywrap (void) + { + if (close_include () == 0) + return 0; + else + return 1; + } + + #define MAX_INCLUDE_DEPTH 50 + + /* An include file on the stack. */ + struct include_stack + { + YY_BUFFER_STATE buffer; + mergemode merge_mode; + int currline; + char *filename; + } include_stack[MAX_INCLUDE_DEPTH]; + int include_stack_ptr = 0; + + /* Add an file to the stack. */ + void + include_file (FILE *file, mergemode new_mm, char *fname) + { + YY_BUFFER_STATE buffer; + + debug_printf ("including file %s\n.", fname); + + if (include_stack_ptr >= MAX_INCLUDE_DEPTH) + { + fprintf (stderr, "Includes nested too deeply\n"); + exit (EXIT_FAILURE); + } + + include_stack[include_stack_ptr].filename = filename; + include_stack[include_stack_ptr].currline = lineno; + include_stack[include_stack_ptr].merge_mode = merge_mode; + include_stack[include_stack_ptr++].buffer = YY_CURRENT_BUFFER; + filename = fname; + lineno = 1; + merge_mode = new_mm; + + buffer = yy_create_buffer (file, YY_BUF_SIZE); + yy_switch_to_buffer (buffer); + } + + /* Close an includefile. returns 0 on success */ + int + close_include (void) + { + if ( --include_stack_ptr < 0 ) + { + fprintf (stderr, "Unexpected end of file at %s:%d.\n", filename, lineno); + return (1); + } + else + { + fclose (yyin); + yy_delete_buffer (YY_CURRENT_BUFFER); + merge_mode = include_stack[include_stack_ptr].merge_mode; + lineno = include_stack[include_stack_ptr].currline; + filename = include_stack[include_stack_ptr].filename; + yy_switch_to_buffer (include_stack[include_stack_ptr].buffer); + debug_printf ("closing file. going back to %s.\n", filename); + return (0); + } + } + + void + yyerror (char *s) + { + fprintf (stderr, "%s:%d: %s\n", filename, lineno, s); + // error_at_line (0, 1, filename, lineno, "foo %d\n", 2); + } + + int + scanner_get_current_location() + { + return lineno; + } + + const char* + scanner_get_current_file() + { + return filename; + } diff --git a/console-client/xkb/parser.y b/console-client/xkb/parser.y new file mode 100644 index 00000000..27b62145 --- /dev/null +++ b/console-client/xkb/parser.y @@ -0,0 +1,1605 @@ +/* parser.y -- XKB parser. + + Copyright (C) 2003, 2004 Marco Gerards + + Written by Marco Gerards <marco@student.han.nl> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. */ + +%{ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include "xkb.h" + +void yyerror(char *); +int yylex (void); +static error_t include_section (char *incl, int sectionsymbol, char *dirname, + mergemode); +static error_t include_sections (char *incl, int sectionsymbol, char *dirname, + mergemode); +void close_include (); +static void skipsection (void); +static error_t set_default_action (struct xkb_action *, struct xkb_action **); +static void key_set_keysym (struct key *key, group_t group, int level, + symbol ks); +static void key_new (char *keyname); +static void key_delete (char *keyname); +void scanner_unput (int c); +static void remove_symbols (struct key *key, group_t group); + +struct xkb_interpret *current_interpretation; +struct xkb_action *current_action; +struct xkb_indicator indi; +struct xkb_indicator *current_indicator = &indi; +struct key defkey; +struct key *default_key = &defkey; + +/* The default settings for actions. */ +struct xkb_action default_setmods = { SA_SetMods }; +struct xkb_action default_lockmods = { SA_LockMods }; +struct xkb_action default_latchmods = { SA_LatchMods }; +struct xkb_action default_setgroup = { SA_SetGroup }; +struct xkb_action default_latchgroup = { SA_LatchGroup }; +struct xkb_action default_lockgroup = { SA_LockGroup }; +struct xkb_action default_moveptr = { SA_MovePtr }; +struct xkb_action default_ptrbtn = { SA_PtrBtn }; +struct xkb_action default_lockptrbtn = { SA_LockPtrBtn }; +struct xkb_action default_ptrdflt = { SA_SetPtrDflt }; +struct xkb_action default_setcontrols = { SA_SetControls }; +struct xkb_action default_lockcontrols = { SA_LockControls }; +struct xkb_action default_isolock = { SA_ISOLock }; +struct xkb_action default_switchscrn = { SA_SwitchScreen }; +struct xkb_action default_consscroll = { SA_ConsScroll }; + +static struct key *current_key; + +/* The dummy gets used when the original may not be overwritten. */ +static struct key dummy_key; + +/* The current parsed group. */ +static int current_group; +static int current_rmod = 0; + +/* The current symbol in the currently parsed key. */ +static int symbolcnt; +static int actioncnt; + +mergemode merge_mode = override; + +//#define YYDEBUG 1 + +#ifndef YY_NULL +#define YY_NULL 0 +#endif + +static struct keytype *current_keytype; +%} + +%union { + int val; + char *str; + modmap_t modmap; + struct xkb_action *action; + double dbl; + mergemode mergemode; +} + +%token XKBKEYMAP "xkb_keymap" +%token XKBKEYCODES "xkb_keycodes" +%token XKBCOMPAT "xkb_compatibility" +%token XKBGEOMETRY "xkb_geometry" +%token XKBTYPES "xkb_types" +%token XKBSYMBOLS "xkb_symbols" +%token STR +%token HEX +%token FLAGS +%token KEYCODE +%token NUM +%token MINIMUM "minimum" +%token MAXIMUM "maximum" +%token VIRTUAL "virtual" +%token INDICATOR "indicator" +%token ALIAS "alias" +%token IDENTIFIER +%token VMODS "virtualmods" +%token TYPE "type" +%token DATA "data" +%token MAP "map" +%token LEVEL_NAME "level_name" +%token PRESERVE "preserve" +%token LEVEL +%token USEMODMAP "usemodmap" +%token REPEAT "repeat" +%token LOCKING "locking" +%token VIRTUALMODIFIER "virtualmod" +%token BOOLEAN +%token INTERPRET "interpret" +%token INTERPMATCH +%token CLEARLOCKS "clearlocks" +%token MODS "mods" +%token SETMODS "setmods" +%token LATCHMODS "latchmods" +%token LOCKMODS "lockmods" +%token ACTION "action" +%token LATCHTOLOCK "latchtolock" +%token GROUP "group" +%token GROUPS "groups" +%token SETGROUP "setgroup" +%token LATCHGROUP "latchgroup" +%token LOCKGROUP "lockgroup" +%token ACCEL "accel" +%token MOVEPTR "moveptr" +%token PRIVATE "private" +%token BUTTON "button" +%token BUTTONNUM +%token DEFAULT "default" +%token COUNT "count" +%token PTRBTN "ptrbtn" +%token DEFAULTBTN "defaultbutton" +%token ALL "all" +%token NONE "none" +%token ANY "any" +%token CONTROLFLAG +%token AFFECT "affect" +%token PTRDFLT "setptrdflt" +%token LOCKPTRBTN "lockptrbtn" +%token SETCONTROLS "setcontrols" +%token LOCKCONTROLS "lockcontrols" +%token CONTROLS "controls" +%token TERMINATE "terminate" +%token WHICHMODSTATE "whichmodstate" +%token WHICHGROUPSTATE "whichgroupstate" +%token WHICHSTATE "whichstate" +%token INDEX "index" +%token ALLOWEXPLICIT "allowexplicit" +%token DRIVESKBD "driveskbd" +//%token AFFECTBTNLOCK +%token SYMBOLS "symbols" +%token NAME "name" +%token GROUPNUM +%token ACTIONS "actions" +%token KEY "key" +%token MODMAP "modifier_map" +%token SHIFT "shift" +%token LOCK "lock" +%token CONTROL "control" +%token MOD1 "mod1" +%token MOD2 "mod2" +%token MOD3 "mod3" +%token MOD4 "mod4" +%token MOD5 "mod5" +%token UNLOCK "unlock" +%token BOTH "both" +%token NEITHER "neither" + +%token INCLUDE "include" +%token AUGMENT "augment" +%token OVERRIDE "override" +%token REPLACE "replace" + + +%token ISOLOCK "isolock" +%token POINTERS "pointers" +%token NOACTION "noaction" +%token GROUPSWRAP "groupswrap" +%token GROUPSCLAMP "groupsclamp" +%token GROUPSREDIRECT "groupsredirect" +%token OVERLAY "overlay" +%token SWITCHSCREEN "switchscreen" +%token SAMESERVER "sameserver" +%token SCREEN "screen" +%token LINE "line" +%token PERCENT "percent" +%token CONSSCROLL "consscroll" +%token FLOAT "float" +%type <str> STR KEYCODE IDENTIFIER +%type <val> FLAGS NUM HEX vmod level LEVEL rmod BOOLEAN symbol INTERPMATCH +%type <val> clearlocks usemodmap latchtolock noaccel button BUTTONNUM +%type <val> ctrlflags allowexplicit driveskbd +%type <val> DRIVESKBD GROUPNUM group symbolname groups whichstate WHICHSTATE +/* Booleans */ +%type <val> locking repeat groupswrap groupsclamp sameserver +%type <modmap> mods +%type <action> action +%type <action> ctrlparams +%type <mergemode> include +%type <dbl> FLOAT +//%debug + +%% +/* A XKB keymap. */ +xkbkeymap: + "xkb_keymap" '{' keymap '}' ';' { YYACCEPT; } +| "xkb_keymap" STR '{' keymap '}' ';' { YYACCEPT; } +| '{' keymap '}' { YYACCEPT; } ';' +; + +/* A XKB configuration has many sections. */ +keymap: + /* empty */ +| keymap types +| keymap keycodes +| keymap compat +| keymap symbols +| keymap geometry +; + +include: + "include" { $$ = defaultmm; } +| "augment" { $$ = augment; } +| "replace" { $$ = replace; } +| "override" { $$= override; } +; + +/* All flags assigned to a section. */ +flags: + /* empty */ +| flags FLAGS +; + +/* The header of a keycode section. */ +keycodes: + flags "xkb_keycodes" '{' keycodesect '}' ';' +| flags "xkb_keycodes" STR '{' keycodesect '}' ';' +; + +/* Process the includes on the stack. */ +keycodesinclude: + '{' keycodesect '}' { close_include (); } +| keycodesinclude '{' keycodesect '}' { close_include (); } +; + +/* The first lines of a keycode section. The amount of keycodes are + declared here. */ +keycodesect: +/* empty */ +| "minimum" '=' NUM ';' keycodesect + { + min_keys = $3; + debug_printf ("working on key: %d\n", $3); + current_key = &keys[$3]; + } +| MAXIMUM '=' NUM ';' keycodesect + { + max_keys = $3; + keys = calloc ($3, sizeof (struct key)); + } +| KEYCODE '=' NUM ';' + { keyname_add ($1, $3); } + keycodesect +| "replace" KEYCODE '=' NUM ';' + { keyname_add ($2, $4); } + keycodesect +| "indicator" NUM '=' STR ';' keycodesect { } +| "virtual" INDICATOR NUM '=' STR ';' keycodesect +| "alias" KEYCODE '=' KEYCODE ';' + { + keycode_t key = keyname_find ($4); + if (key) + keyname_add ($2, key); + else + { + key = keyname_find ($2); + if (key) + keyname_add ($4, key); + } + } + keycodesect +| include STR + { include_sections ($2, XKBKEYCODES, "keycodes", $1); } + keycodesinclude keycodesect +; + +/* The header of a keytypes section. */ +types: + flags "xkb_types" '{' typessect '}' ';' +| flags "xkb_types" STR '{' typessect '}' ';' +; + +/* A list of virtual modifier declarations (see vmods_def), seperated + by commas. */ +vmodslist: + IDENTIFIER { vmod_add ($1); } +| vmodslist ',' IDENTIFIER { vmod_add ($3); } +; + +/* Virtual modifiers must be declared before they can be used. */ +vmods_def: + "virtualmods" vmodslist ';' +; + +/* Return the number of the virtual modifier. */ +vmod: + IDENTIFIER + { if (($$ = vmod_find ($1)) != 0) + $$ = 1 << ($$ - 1); + else + fprintf(stderr, "warning: %s virtual modifier is not defined.", $1); + } +; + +/* A single realmodifier. */ +rmod: + "shift" { $$ = RMOD_SHIFT; } +| "lock" { $$ = RMOD_LOCK; } +| "control" { $$ = RMOD_CTRL; } +| "mod1" { $$ = RMOD_MOD1; } +| "mod2" { $$ = RMOD_MOD2; } +| "mod3" { $$ = RMOD_MOD3; } +| "mod4" { $$ = RMOD_MOD4; } +| "mod5" { $$ = RMOD_MOD5; } +; + +/* One of more modifiers, seperated by '+'. A modmap_t will return all real + and virtual modifiers specified. */ +mods: + mods '+' rmod { $$.rmods = $1.rmods | $3; } +| mods '+' vmod { $$.vmods = $1.vmods | $3; } + /* Use a mid-rule action to start with no modifiers. */ +| { $<modmap>$.rmods = 0; $<modmap>$.vmods = 0; } rmod { $$.rmods = $2; } +| { $<modmap>$.rmods = 0; $<modmap>$.vmods = 0; } vmod { $$.vmods = $2; } +| "all" { $$.rmods = 0xFF; $$.vmods = 0xFFFF; } +| "none" { $$.rmods = 0; $$.vmods = 0; } +; + +/* The numeric level starts with 0. Level1-Level4 returns 0-3, also + numeric values can be used to describe a level. */ +level: + LEVEL { $$ = $1 - 1; } +| "any" { $$ = 0; } +| NUM { $$ = $1 - 1; } +; + +/* A single keytype. */ +type: + /* Empty */ +| type MODS '=' mods ';' + { current_keytype->modmask = $4; } +| type MAP '[' mods ']' '=' level ';' + { keytype_mapadd (current_keytype, $4, $7); } +| type "level_name" '[' level ']' '=' STR ';' +| type "preserve" '[' mods ']' '=' mods ';' + { keytype_preserve_add (current_keytype, $4, $7); } +; + +/* Process the includes on the stack. */ +typesinclude: + '{' typessect '}' { close_include (); } +| typesinclude '{' typessect '}' { close_include (); } +; + +/* A keytype section contains keytypes and virtual modifier declarations. */ +typessect: + /* Empty */ +| typessect vmods_def +| typessect TYPE STR { keytype_new ($3, ¤t_keytype); }'{' type '}' ';' { } +| typessect include STR + { include_sections ($3, XKBTYPES, "types", $2); } + typesinclude +; + +/* The header of a compatibility section. */ +compat: + flags "xkb_compatibility" '{' compatsect '}' ';' +| flags "xkb_compatibility" STR '{' compatsect '}' ';' +; + +/* XXX: A symbol can be more than just an identifier (hex). */ +symbol: + IDENTIFIER { $$ = (int) XStringToKeysym ( $1) ? : -1; } +| ANY { $$ = 0; } +| error { yyerror ("Invalid symbol."); } +; + +/* Which kinds of modifiers (like base, locked, etc.) can affect the + indicator. */ +whichstate: + WHICHSTATE { $$ = $1; } +| "none" { $$ = 0; } +| "any" { $$ = 0xff; } /* XXX */ +; + +/* The groups that can affect a indicator. */ +groups: + groups '+' group +| groups '-' group +| group {} +| "all" { $$ = 0xff; } /* XXX */ +; + +indicators: +/* empty */ +| indicators indicator +; + +/* An indicator desciption. */ +indicator: + "mods" '=' mods ';' { current_indicator->modmap = $3; } +| "groups" '=' groups ';' { current_indicator->groups = $3; } +| "controls" '=' ctrls ';' +| "whichmodstate" '=' whichstate ';' { current_indicator->which_mods = $3; } +| "whichgroupstate" '=' whichstate ';' { current_indicator->which_mods = $3; } +| allowexplicit ';' {} /* Ignored for now. */ +| driveskbd ';' {} +| "index" '=' NUM ';' {} +; + +/* Boolean for allowexplicit. */ +allowexplicit: + '~' "allowexplicit" { $$ = 0; } +| '!' "allowexplicit" { $$ = 0; } +| "allowexplicit" { $$ = 1; } +| "allowexplicit" '=' BOOLEAN { $$ = $3; } +; + +/* Boolean for driveskbd. */ +driveskbd: + '~' "driveskbd" { $$ = 0; } +| '!' "driveskbd" { $$ = 0; } +| "driveskbd" { $$ = 1; } +| "driveskbd" '=' BOOLEAN { $$ = $3; } +; + +interprets: + /* Empty */ +| interprets interpret +; + +/* A single interpretation. */ +interpret: + "usemodmap" '=' level ';' + { current_interpretation->match &= 0x7F | ($3 << 7); } /* XXX */ +| repeat ';' + { + current_interpretation->flags &= ~(KEYREPEAT | KEYNOREPEAT); + current_interpretation->flags |= $1; + } +| locking ';' {} +| "virtualmod" '=' vmod ';' { current_interpretation->vmod = $3; } +| "action" '=' action ';' + { + memcpy (¤t_interpretation->action, $3, sizeof (xkb_action_t)); + free ($3); + } +; + +/* Process the includes on the stack. */ +compatinclude: + '{' compatsect '}' { close_include (); } +| compatinclude '{' compatsect '}' { close_include (); } +; + +/* The body of a compatibility section. */ +compatsect: + /* Empty */ +| compatsect vmods_def +| compatsect "interpret" '.' + { current_interpretation = &default_interpretation; } + interpret +| compatsect "interpret" symbol + { + if ($3 != -1) + { + interpret_new (¤t_interpretation, $3); + current_interpretation->match |= 1; + } + } + '{' interprets '}' ';' +| compatsect "interpret" symbol '+' rmod + { + if ($3 != -1) + { + interpret_new (¤t_interpretation, $3); + current_interpretation->rmods = $5; + current_interpretation->match |= 4; + } + } + '{' interprets '}' ';' +| compatsect "interpret" symbol '+' "any" + { + if ($3 != -1) + { + interpret_new (¤t_interpretation, $3); + current_interpretation->rmods = 255; + current_interpretation->match |= 2; + } + } + '{' interprets '}' ';' +| compatsect "interpret" symbol '+' INTERPMATCH '(' mods ')' + { + if ($3 != -1) + { + interpret_new (¤t_interpretation, $3); + current_interpretation->rmods = $7.rmods; + current_interpretation->match |= $5; + } + } + '{' interprets '}' ';' +| compatsect GROUP NUM '=' mods ';' +| compatsect "indicator" STR '{' indicators '}' ';' +| compatsect include STR + { include_sections ($3, XKBCOMPAT, "compat", $2); } + compatinclude +| compatsect actiondef +| compatsect "indicator" '.' indicator +; + + + /* Booleans */ +/* Boolean for clearlocks. */ +clearlocks: + '~' "clearlocks" { $$ = 0; } +| '!' "clearlocks" { $$ = 0; } +| "clearlocks" { $$ = 1; } +| "clearlocks" '=' BOOLEAN { $$ = $3; } +; + +/* Boolean for latchtolock. */ +latchtolock: + '~' "latchtolock" { $$ = 0; } +| '!' "latchtolock" { $$ = 0; } +| "latchtolock" { $$ = 1; } +| "latchtolock" '=' BOOLEAN { $$ = $3; } +; + +/* Boolean for useModMap. */ +usemodmap: + '~' "usemodmap" { $$ = 0; } +| '!' "usemodmap" { $$ = 0; } +| "usemodmap" { $$ = 1; } +| "usemodmap" '=' BOOLEAN { $$ = $3; } +; + +/* Boolean for locking. */ +locking: + '~' "locking" { $$ = 0; } +| '!' "locking" { $$ = 0; } +| "locking" { $$ = 1; } +| "locking" '=' BOOLEAN { $$ = $3; } +; + +/* Boolean for repeat. */ +repeat: + '~' "repeat" { $$ = KEYNOREPEAT; } +| '!' "repeat" { $$ = KEYNOREPEAT; } +| "repeat" { $$ = KEYREPEAT; } +| "repeat" '=' BOOLEAN + { + if ($3) + $$ = KEYREPEAT; + else + $$ = KEYNOREPEAT; + } +; + +/* Boolean for groupswrap. */ +groupswrap: + '~' "groupswrap" { $$ = 0; } +| '!' "groupswrap" { $$ = 0; } +| "groupswrap" { $$ = 1; } +| "groupswrap" '=' BOOLEAN { $$ = $3; } +; + +/* Boolean for groupsclamp. */ +groupsclamp: + '~' "groupsclamp" { $$ = 0; } +| '!' "groupsclamp" { $$ = 0; } +| "groupsclamp" { $$ = 1; } +| "groupsclamp" '=' BOOLEAN { $$ = $3; } +; + +/* Boolean for noaccel. */ +noaccel: + '~' "accel" { $$ = 0; } +| '!' "accel" { $$ = 0; } +| "accel" { $$ = 1; } +| "accel" '=' BOOLEAN { $$ = $3; } +; + + +sameserver: + '~' "sameserver" { $$ = 0; } +| '!' "sameserver" { $$ = 0; } +| "sameserver" { $$ = 1; } +| "sameserver" '=' BOOLEAN { $$ = $3; } +; + +setmodsparams: + /* empty */ +| setmodsparam +| setmodsparams ',' setmodsparam +; + + +/* Parameter for the (Set|Lock|Latch)Mods action. */ +setmodsparam: + "mods" '=' mods + { + ((action_setmods_t *) current_action)->modmap = $3; + } +| "mods" '=' "usemodmap" + { ((action_setmods_t *) current_action)->flags |= useModMap; + } +| clearlocks + { + ((action_setmods_t *) current_action)->flags &= ~clearLocks; + ((action_setmods_t *) current_action)->flags |= $1; + } +| usemodmap + { + ((action_setmods_t *) current_action)->flags &= ~useModMap; + ((action_setmods_t *) current_action)->flags |= $1; + } +| latchtolock + { + ((action_setmods_t *) current_action)->flags &= ~latchToLock; + ((action_setmods_t *) current_action)->flags |= $1; + } +; + +setgroupparams: +/* empty */ +| setgroupparam +| setgroupparams ',' setgroupparam +; + +/* Parameter for the (Set|Lock|Latch)Group action. */ +setgroupparam: + "group" '=' NUM + { + ((action_setgroup_t *) current_action)->group = $3; + ((action_setgroup_t *) current_action)->flags |= groupAbsolute; + } +| "group" '=' '+' NUM + { + ((action_setgroup_t *) current_action)->group = $4; + } +| "group" '=' '-' NUM + { + ((action_setgroup_t *) current_action)->group = -$4; + } +| clearlocks + { + ((action_setgroup_t *) current_action)->flags |= $1; + } +| latchtolock + { + ((action_setgroup_t *) current_action)->flags |= $1; + } +; + +moveptrparams: +/* empty */ +| moveptrparam +| moveptrparams ',' moveptrparam +; + +/* Parameters for the MovePtr action. */ +moveptrparam: + IDENTIFIER '=' NUM + { + ((action_moveptr_t *) current_action)->x = $3; + ((action_setgroup_t *) current_action)->flags |= MoveAbsoluteX; + } +| IDENTIFIER '=' '+' NUM + { + ((action_moveptr_t *) current_action)->x = $4; + } +| IDENTIFIER '=' '-' NUM + { + ((action_moveptr_t *) current_action)->x = -$4; + } +| noaccel + { + ((action_moveptr_t *) current_action)->flags |= NoAcceleration; + } +; + +/* A mouse button. */ +button: + NUM { $$ = $1; } +| BUTTONNUM { $$ = $1; } +| "default" { $$ = 0; } +; + +affectbtnlock: + "lock" +| "unlock" +| "both" +| "neither" +; + +ptrbtnparams: + /* empty */ +| ptrbtnparam +| ptrbtnparams ',' ptrbtnparam +; + +/* Parameters for the (Set|Lock|Latch)PtrBtn action. */ +ptrbtnparam: + "button" '=' button + { ((action_ptrbtn_t *) current_action)->button = $3; } +| "count" '=' NUM + { ((action_ptrbtn_t *) current_action)->count = $3; } +| "affect" '=' affectbtnlock + { + // ((action_ptrbtn_t *) $$)->a = $3; + } +; + +/* XXX: This should be extended. */ +affectbtns: + "defaultbutton" +| "button" +| "all" +; + +ptrdfltparams: +/* empty */ +| ptrdfltparam +| ptrdfltparams ',' ptrdfltparam +; + +/* Parameters for the SetPtrDflt action. */ +ptrdfltparam: + "button" '=' button { } +| "button" '=' '+' button { } +| "button" '=' '-' button { } +| "affect" '=' affectbtns { } +; + +/* A list of controlflags. */ +ctrls: + ctrls '+' CONTROLFLAG// { $$ |= $3; } +| CONTROLFLAG// { $$ = $1; } +| OVERLAY +; + +/* Modified controlflags. */ +ctrlflags: + ctrls { /*$$ = $1;*/ } +| "all" { $$ = 0xFFFF; } +| "none" { $$ = 0; } +; + +/* The parameters of a (Set|Lock|Latch)Ctrls Action. */ +ctrlparams: + "controls" '=' ctrlflags + { /* ((action_setcontrols_t *) $$)->controls = $3; */ } +; + +isoaffect: + "mods" +| "groups" +| "controls" +| "pointers" +| "all" +| "none" +; + +isolockparams: +/* empty */ +| isolockparam +| isolockparams ',' isolockparam +; + +/* Parameters for the ISOLock action. */ +isolockparam: + "mods" '=' mods +| "mods" '=' USEMODMAP +| "group" '=' group +| "controls" '=' ctrlflags +| "affect" '=' isoaffect +; + +switchscrnparams: + switchscrnparam + | switchscrnparams ',' switchscrnparam +; + +/* Parameters for the SwitchScreen action. */ +switchscrnparam: + "screen" '=' NUM + { + ((action_switchscrn_t *) current_action)->screen = $3; + ((action_switchscrn_t *) current_action)->flags |= screenAbs; + } +| "screen" '+' '=' NUM + { + ((action_switchscrn_t *) current_action)->screen = $4; + } +| "screen" '-' '=' NUM + { + ((action_switchscrn_t *) current_action)->screen = -$4; + } +| sameserver + { + /* XXX: Implement this. */ +/* ((action_switchscrn_t *) current_action)->flags &= ~0; */ +/* ((action_switchscrn_t *) current_action)->flags |= $1; */ + } +; + +consscrollparams: + consscrollparam + | consscrollparams ',' consscrollparam +; + +/* Parameters for the ConsScroll action. */ +consscrollparam: + "screen" '+' '=' FLOAT + { + ((action_consscroll_t *) current_action)->screen = $4; + } +| "screen" '-' '=' FLOAT + { + ((action_consscroll_t *) current_action)->screen = -$4; + } +| "line" '=' NUM + { + ((action_consscroll_t *) current_action)->line = $3; + ((action_consscroll_t *) current_action)->flags |= lineAbs; + } +| "line" '+' '=' NUM + { + ((action_consscroll_t *) current_action)->line = $4; + } +| "line" '-' '=' NUM + { + ((action_consscroll_t *) current_action)->line = -$4; + } +| "percent" '=' NUM + { + ((action_consscroll_t *) current_action)->percent = $3; + ((action_consscroll_t *) current_action)->flags |= usePercentage; + } +; + +privateparams: + /* empty */ + | privateparam + | privateparams ',' privateparam +; + +privateparam: + "type" '=' HEX + { + } +| "data" '=' STR + { + } +; + +/* An action definition. */ +action: + "setmods" + { + if (set_default_action (&default_setmods, ¤t_action)) + YYABORT; + } + '(' setmodsparams ')' { $$ = current_action; } +| "latchmods" + { + if (set_default_action (&default_latchmods, ¤t_action)) + YYABORT; + } + '(' setmodsparams ')' { $$ = current_action; } +| "lockmods" + { + if (set_default_action (&default_lockmods, ¤t_action)) + YYABORT; + } + '(' setmodsparams ')' { $$ = current_action; } +| "setgroup" + { + if (set_default_action (&default_setgroup, ¤t_action)) + YYABORT; + } + '(' setgroupparams ')' { $$ = current_action; } +| "latchgroup" + { + if (set_default_action (&default_latchgroup, ¤t_action)) + YYABORT; + } + '(' setgroupparams ')' { $$ = current_action; } +| "lockgroup" + { + if (set_default_action (&default_lockgroup, ¤t_action)) + YYABORT; + } + '(' setgroupparams ')' { $$ = current_action; } +| "moveptr" + { + if (set_default_action (&default_moveptr, ¤t_action)) + YYABORT; + } + '(' moveptrparams ')' { $$ = current_action; } +| "ptrbtn" + { + if (set_default_action (&default_ptrbtn, ¤t_action)) + YYABORT; + } + '(' ptrbtnparams ')' { $$ = current_action; } +| "lockptrbtn" + { + if (set_default_action (&default_lockptrbtn, ¤t_action)) + YYABORT; + } + '(' ptrbtnparams ')' { $$ = current_action; } +| "setptrdflt" + { + if (set_default_action (&default_ptrdflt, ¤t_action)) + YYABORT; + } + '(' ptrdfltparams ')' { $$ = current_action; } +| "setcontrols" + { + if (set_default_action (&default_setcontrols, ¤t_action)) + YYABORT; + } + '(' ctrlparams ')' { $$ = current_action; } +| "lockcontrols" + { + if (set_default_action (&default_lockcontrols, ¤t_action)) + YYABORT; + } + '(' ctrlparams ')' { $$ = current_action; } +| "terminate" '(' ')' + { $$ = calloc (1, sizeof (xkb_action_t)); $$->type = SA_TerminateServer; } +| "switchscreen" + { + if (set_default_action (&default_switchscrn, ¤t_action)) + YYABORT; + } +'(' switchscrnparams ')' { $$ = current_action; } +| "consscroll" + { + if (set_default_action (&default_consscroll, ¤t_action)) + YYABORT; + } + '(' consscrollparams ')' { $$ = current_action; } +| "isolock" + { + if (set_default_action (&default_isolock, ¤t_action)) + YYABORT; + } + '(' isolockparams ')' { $$ = current_action; } +| "private" '(' privateparams ')' + { $$ = calloc (1, sizeof (xkb_action_t)); $$->type = SA_NoAction; } +| "noaction" '(' ')' + { $$ = calloc (1, sizeof (xkb_action_t)); $$->type = SA_NoAction; } +| error ')' { yyerror ("Invalid action\n"); } +; + +/* Define values for default actions. */ +actiondef: + "setmods" '.' { current_action = &default_setmods; } setmodsparam ';' +| "latchmods" '.' { current_action = &default_latchmods; } setmodsparam ';' +| "lockmods" '.' { current_action = &default_lockmods; } setmodsparam ';' +| "setgroup" '.' { current_action = &default_setgroup; } setgroupparam ';' +| "latchgroup" '.' { current_action = &default_latchgroup; } setgroupparam ';' +| "lockgroup" '.' { current_action = &default_lockgroup; } setgroupparam ';' +| "moveptr" '.' { current_action = &default_moveptr; } moveptrparam ';' +| "ptrbtn" '.' { current_action = &default_ptrbtn; } ptrbtnparam ';' +| "lockptrbtn" '.' { current_action = &default_lockptrbtn; } ptrbtnparam ';' +| "setptrdflt" '.' { current_action = &default_ptrdflt; } ptrdfltparam ';' +| "setcontrols" '.' { current_action = &default_setcontrols; } ctrlparams ';' +| "lockcontrols" '.' { current_action = &default_lockcontrols; } ctrlparams ';' +| "isolock" '.' { current_action = &default_isolock; } isolockparam ';' +| "switchscreen" '.' { current_action = &default_switchscrn; } switchscrnparam ';' +; + +/* The header of a symbols section. */ +symbols: + flags "xkb_symbols" '{' symbolssect '}' ';' +| flags "xkb_symbols" STR '{' symbolssect '}' ';' +; + +/* A group. */ +group: + GROUPNUM { $$ = $1 - 1; } +| NUM { $$ = $1 - 1; } +| HEX { $$ = $1 - 1; } +; + +/* A list of keysyms and keycodes bound to a realmodifier. */ +key_list: + key_list ',' KEYCODE { set_rmod_keycode ($3, current_rmod); } +| key_list ',' symbolname { ksrm_add ($3, current_rmod); } +| KEYCODE { set_rmod_keycode ($1, current_rmod); } +| symbolname { ksrm_add ($1, current_rmod); } +; + +/* Process the includes on the stack. */ +symbolinclude: + '{' symbolssect '}' { close_include (); } +| symbolinclude '{' symbolssect '}' { close_include (); } +; + +/* A XKB symbol section. It is used to bind keysymbols, actions and + realmodifiers to keycodes. */ +symbolssect: + /* Empty */ +| symbolssect vmods_def +| symbolssect NAME '[' group ']' '=' STR ';' +| symbolssect "key" KEYCODE + { + key_new ($3); + current_group = 0; + } '{' keydescs '}' ';' +| symbolssect "replace" "key" KEYCODE + { + key_delete ($4); + key_new ($4); + current_group = 0; + } '{' keydescs '}' ';' +| symbolssect "override" "key" KEYCODE + { + key_delete ($4); + key_new ($4); + current_group = 0; + } '{' keydescs '}' ';' +| symbolssect "modifier_map" rmod { current_rmod = $3; } '{' key_list '}' ';' +| symbolssect include STR + { include_sections ($3, XKBSYMBOLS, "symbols", $2); } + symbolinclude +| symbolssect actiondef +| symbolssect "key" '.' {debug_printf ("working on default key.\n"); current_key = default_key; } keydesc ';' +| symbolssect error ';' { yyerror ("Error in symbol section\n"); } +; + +/* Returns a keysymbols, the numberic representation. */ +symbolname: + IDENTIFIER { $$ = XStringToKeysym ($1); } +| NUM + { + if (($1 >= 0) && ($1 < 10)) + $$ = $1 + '0'; + else + $$ = $1; + } +| HEX { $$ = $1; } +; + +/* None or more keysyms, assigned to a single group of the current + key. */ +groupsyms: + /* empty */ +| groupsyms ',' symbolname + { key_set_keysym (current_key, current_group, symbolcnt++, $3); } +| { symbolcnt = 0; } symbolname + { + symbolcnt = 0; + key_set_keysym (current_key, current_group, symbolcnt++, $2); + } +; + +/* A list of actions. */ +actions: + actions ',' action + { key_set_action (current_key, current_group, actioncnt++, $3); } +| { actioncnt = 0; } action + { key_set_action ( current_key, current_group, actioncnt++, $2); } +; + +keydescs: + keydesc +| keydescs ',' keydesc +; + +/* A single key and everything assigned to it. */ +keydesc: + "type" '[' group ']' '=' STR + { + current_key->groups[$3].keytype = keytype_find ($6); + } +| "type" '[' error ']' { yyerror ("Invalid group.\n"); } +| "type" '=' STR + { current_key->groups[current_group].keytype = keytype_find ($3); } +| { symbolcnt = 0; } "symbols" '[' group ']' { current_group = $4; } '=' '[' groupsyms ']' + { + current_key->numgroups = ($4 + 1) > current_key->numgroups ? + ($4 + 1) : current_key->numgroups; + } +| {actioncnt = 0; } "actions" '[' group ']' { current_group = $4; } '=' '[' actions ']' + { + current_key->numgroups = ($4 + 1) > current_key->numgroups ? + ($4 + 1) : current_key->numgroups; + } +| "virtualmods" '=' mods + { current_key->mods.vmods = $3.vmods; } +| '[' groupsyms ']' + { + current_group++; + current_key->numgroups = current_group > current_key->numgroups ? + current_group : current_key->numgroups; + } +| '[' actions ']' + { + current_group++; + current_key->numgroups = current_group > current_key->numgroups ? + current_group : current_key->numgroups; + } +| locking {}/* This is not implemented - YET. */ +/* XXX: There 3 features are described in Ivan Pascals docs about XKB, + but aren't used in the standard keymaps and cannot be used because it + cannot be stored in the XKM dataformat. */ +| groupswrap {} +| groupsclamp {} +| "groupsredirect" '=' NUM +| "overlay" '=' KEYCODE /* If you _REALLY_ need overlays, mail me!!!! */ +| repeat + { + current_key->flags &= ~(KEYREPEAT | KEYNOREPEAT); + current_key->flags |= $1; + } +; + +/* The geometry map is ignored. */ + +/* The header of a geometry section. */ +geometry: + flags "xkb_geometry" '{' { skipsection (); } '}' ';' +| flags "xkb_geometry" STR '{' { skipsection (); } '}' ';' +; + +%% +/* Skip all tokens until a section of the type SECTIONSYMBOL with the + name SECTIONNAME is found. */ +static int +skip_to_sectionname (char *sectionname, int sectionsymbol) +{ + int symbol; + + do + { + do + { + symbol = yylex (); + } while ((symbol != YY_NULL) && (symbol != sectionsymbol)); + + if (symbol != YY_NULL) + symbol = yylex (); + + if (symbol == YY_NULL) { + return 1; + } else if (symbol != STR) + continue; + + } while (strcmp (yylval.str, sectionname)); + return 0; +} + +/* Skip all tokens until the first section of the type SECTIONSYMBOL + is found. */ +static int +skip_to_firstsection (int sectionsymbol) +{ + int symbol; + + do + { + do + { + symbol = yylex (); + } while ((symbol != YY_NULL) && (symbol != sectionsymbol)); + + if (symbol != YY_NULL) + symbol = yylex (); + + if (symbol == YY_NULL) + return 1; + } while (symbol != STR); + return 0; +} + +/* Skip all tokens until the default section is found. */ +static int +skip_to_defaultsection (void) +{ + int symbol; + + /* Search the default section. */ + do + { + if ((symbol = yylex ()) == YY_NULL) + return 1; + } while (symbol != DEFAULT); + + do + { + if ((symbol = yylex ()) == YY_NULL) + return 1; + } while (symbol != '{'); + scanner_unput ('{'); + return 0; +} + +/* Include a single file. INCL is the filename. SECTIONSYMBOL is the + token that marks the beginning of the section. DIRNAME is the name + of the directory from where the includefiles must be loaded. NEW_MM + is the mergemode that should be used. */ +static error_t +include_section (char *incl, int sectionsymbol, char *dirname, + mergemode new_mm) +{ + void include_file (FILE *, mergemode, char *); + int scanner_get_current_location (); + const char* scanner_get_current_file (); + + char *filename; + char *sectionname = NULL; + FILE *includefile; + + int current_location = scanner_get_current_location (); + char* current_file = strdup (scanner_get_current_file ()); + + sectionname = strchr (incl, '('); + if (sectionname) + { + int snlen; + + snlen = strlen (sectionname); + if (sectionname[snlen-1] != ')') + { + free(current_file); + return 0; + } + sectionname[snlen-1] = '\0'; + sectionname[0] = '\0'; + sectionname++; + + if (asprintf (&filename, "%s/%s", dirname, incl) < 0) + { + free (current_file); + return ENOMEM; + } + } + else + { + if (asprintf (&filename, "%s/%s", dirname, incl) < 0) + { + free (current_file); + return ENOMEM; + } + } + + includefile = fopen (filename, "r"); + + if (includefile == NULL) + { + fprintf (stderr, "Couldn't open include file \"%s\"\n", filename); + free (current_file); + free (filename); + exit (EXIT_FAILURE); + } + + include_file (includefile, new_mm, strdup (filename)); + debug_printf ("skipping to section %s\n", (sectionname ? sectionname : "default")); + /* If there is a sectionname not the entire file should be included, + the scanner should be positioned at the required section. */ + int err; + if (sectionname) + err = skip_to_sectionname (sectionname, sectionsymbol); + else + if ((err = skip_to_defaultsection ()) != 0) + { + /* XXX: after skip_to_defaultsection failed the whole file was + consumed and it is required to include it here, too. */ + include_file (includefile, new_mm, strdup (filename)); + err = skip_to_firstsection (sectionsymbol); + } + if (err != 0) { + char* tmpbuf = malloc (sizeof(char)*1024); + if (tmpbuf) { + snprintf (tmpbuf, 1023, "cannot find section %s in file %s included from %s:%d.\n" + , (sectionname ? sectionname : "DEFAULT") + , filename, current_file, current_location); + yyerror (tmpbuf); + free (tmpbuf); + } + free (current_file); + free (filename); + exit (err); + } + free (current_file); + free (filename); + return 0; +} + +/* Include multiple file sections, seperated by '+'. INCL is the + include string. SECTIONSYMBOL is the token that marks the beginning + of the section. DIRNAME is the name of the directory from where the + includefiles must be loaded. NEW_MM is the mergemode that should be + used. */ +static error_t +include_sections (char *incl, int sectionsymbol, char *dirname, + mergemode new_mm) +{ + char *curstr; + char *s; + + if (new_mm == defaultmm) + new_mm = merge_mode; + +/* printf ("dir: %s - include: %s: %d\n", dirname, incl, new_mm); */ + /* Cut of all includes, starting with the first. The includes are + pushed on the stack in reversed order. */ + do { + curstr = strrchr (incl, '+'); + if (curstr) + { + curstr[0] = '\0'; + curstr++; + + s = strdup (curstr); + if (s == NULL) + return ENOMEM; + + include_section (s, sectionsymbol, dirname, new_mm); + free (s); + } + } while (curstr); + + s = strdup (incl); + if (s == NULL) + return ENOMEM; + + include_section (s, sectionsymbol, dirname, new_mm); + free (s); + + return 0; +} + +/* Skip all tokens until the end of the section is reached. */ +static void +skipsection (void) +{ + /* Pathesensis counter. */ + int p = 0; + while (p >= 0) + { + int symbol = yylex (); + if (symbol == '{') + p++; + if (symbol == '}') + p--; + } + scanner_unput ('}'); +} + +/* Initialize the default action with the default DEF. */ +static error_t +set_default_action (struct xkb_action *def, + struct xkb_action **newact) +{ + struct xkb_action *newaction; + newaction = malloc (sizeof (struct xkb_action)); + if (newaction == NULL) + return ENOMEM; + memcpy (newaction, def, sizeof (struct xkb_action)); + + *newact = newaction; + + return 0; +} + +/* Remove all keysyms bound to the group GROUP or the key KEY. */ +static void +remove_symbols (struct key *key, group_t group) +{ + // printf ("rem: group: %d\n", group); + if (key->groups[group].symbols) + { + free (key->groups[group].symbols); + key->groups[group].symbols = NULL; + key->groups[group].width = 0; + } +} + +/* Set the keysym KS for key KEY on group GROUP and level LEVEL. */ +static void +key_set_keysym (struct key *key, group_t group, int level, symbol ks) +{ + symbol *keysyms = key->groups[group].symbols; + + if ((level + 1) > key->groups[group].width) + { + keysyms = realloc (keysyms, (level + 1)*sizeof(symbol)); + + if (!keysyms) + { + fprintf (stderr, "No mem\n"); + exit (EXIT_FAILURE); + } + + key->groups[group].symbols = keysyms; + key->groups[group].width++; + } + else + /* For NoSymbol leave the old symbol intact. */ + if (!ks) { + debug_printf ("symbol %d was not added to key.\n", ks); + return; + } + + debug_printf ("symbol '%c'(%d) added to key for group %d and level %d.\n", ks, ks, group, level); + keysyms[level++] = ks; +} + +/* Set the action ACTION for key KEY on group GROUP and level LEVEL. */ +void +key_set_action (struct key *key, group_t group, int level, xkb_action_t *action) +{ + xkb_action_t **actions = key->groups[group].actions; + size_t width = key->groups[group].actionwidth; + + if ((size_t) (level + 1) > width) + { + actions = realloc (actions, (level + 1)*sizeof(xkb_action_t *)); + /* Levels between 'width' and 'level' have no actions defined. */ + memset (&actions[width], 0, (level - width)*sizeof(xkb_action_t *)); + + if (!actions) + { + fprintf (stderr, "No mem\n"); + exit (EXIT_FAILURE); + } + + key->groups[group].actions = actions; + key->groups[group].actionwidth += level - width + 1; + } + + actions[level++] = action; +} + +/* Delete keycode to keysym mapping. */ +void +key_delete (char *keyname) +{ + group_t group; + keycode_t kc = keyname_find (keyname); + + current_key = &keys[kc]; + for (group = 0; group < current_key->numgroups; group++) + remove_symbols (current_key, group); + memset (current_key, 0, sizeof (struct key)); + +} + +/* Create a new keycode to keysym mapping, check if the old one should + be removed or preserved. */ +static void +key_new (char *keyname) +{ + group_t group; + + int isempty (char *mem, int size) + { + int i; + for (i = 0; i < size; i++) + if (mem[i]) + return 0; + return 1; + } + + keycode_t kc = keyname_find (keyname); + + if (merge_mode == augment) + { + if (!isempty ((char *) &keys[kc], sizeof (struct key))) + { + current_key = &dummy_key; + debug_printf ("working on dummy key due to merge mode.\n"); + return; + } + else + current_key = &keys[kc]; + } + + if (merge_mode == override) + current_key = &keys[kc]; + + if (merge_mode == replace) + { + key_delete (keyname); + current_key = &keys[kc]; + } + + debug_printf ("working on key %s(%d)", keyname, kc); + + if (current_key->numgroups == 0 || merge_mode == replace) + { + debug_printf (" cloned default key"); + /* Clone the default key. */ + memcpy (current_key, default_key, sizeof (struct key)); + for (group = 0; group < 3; group++) + { + current_key->groups[group].symbols = NULL; + current_key->groups[group].actions = NULL; + current_key->groups[group].actionwidth = 0; + current_key->groups[group].width = 0; + } + } + debug_printf ("\n"); +} + +/* Load the XKB configuration from the section XKBKEYMAP in the + keymapfile XKBKEYMAPFILE. Use XKBDIR as root directory for relative + pathnames. */ +error_t +parse_xkbconfig (char *xkbdir, char *xkbkeymapfile, char *xkbkeymap) +{ + error_t err; + char *cwd = getcwd (NULL, 0); + extern FILE *yyin; + extern char *filename; + + debug_printf ("Dir: %s, file: %s sect: %s\n", xkbdir, xkbkeymapfile, xkbkeymap); + + if (xkbkeymapfile) + { + filename = xkbkeymapfile; + + if (chdir (xkbdir) == -1) + { + fprintf (stderr, "Could not set \"%s\" as the active directory\n", + xkbdir); + free (cwd); + return errno; + } + + yyin = fopen (xkbkeymapfile, "r"); + if (yyin == NULL) + { + fprintf (stderr, "Couldn't open keymap file\n"); + free (cwd); + return errno; + } + + if (xkbkeymap) + skip_to_sectionname (xkbkeymap, XKBKEYMAP); + else + skip_to_defaultsection(); + } + else + { + free (cwd); + return EINVAL; + } + err = yyparse (); + fclose (yyin); + + if (err || yynerrs > 0) + { + free (cwd); + return EINVAL; + } + + if (xkbkeymapfile) + { + if (chdir (cwd) == -1) + { + fprintf (stderr, + "Could not set \"%s\" as the active directory\n", cwd); + free (cwd); + return errno; + } + } + + /* Apply keysym to realmodifier mappings. */ + ksrm_apply (); + + free (cwd); + return 0; +} diff --git a/console-client/xkb/xkb-data/keymap/hurd b/console-client/xkb/xkb-data/keymap/hurd new file mode 100644 index 00000000..dbb874c5 --- /dev/null +++ b/console-client/xkb/xkb-data/keymap/hurd @@ -0,0 +1,392 @@ +// $XFree86: xc/programs/xkbcomp/keymap/xfree86,v 3.30 2003/04/03 16:34:49 dawes Exp $ + + +xkb_keymap "Hurd" { + xkb_keycodes { include "xfree86" }; + xkb_types { include "default+hurd" }; + xkb_compatibility { include "default" }; + xkb_symbols { include "pc(pc105)+us+hurd+group(win_switch)+compose(menu)" }; + xkb_geometry { include "pc" }; +}; + +default xkb_keymap "us" { + xkb_keycodes { include "xfree86" }; + xkb_types { include "default+hurd" }; + xkb_compatibility { include "default" }; + xkb_symbols { include "pc(pc105)+us+hurd" }; + xkb_geometry { include "pc" }; +}; + +// "ar" addition by Arabeyes Team, <support@arabeyes.org> +xkb_keymap "ar" { + xkb_keycodes { include "xfree86" }; + xkb_types { include "default+hurd" }; + xkb_compatibility { include "default" }; + xkb_symbols { include "pc(pc105)+ara+hurd" }; + xkb_geometry { include "pc(pc102)" }; +}; +xkb_keymap "be" { + xkb_keycodes { include "xfree86" }; + xkb_types { include "default+hurd" }; + xkb_compatibility { include "default" }; + xkb_symbols { include "pc(pc105)+be+hurd" }; + xkb_geometry { include "pc(pc102)" }; +}; +xkb_keymap "bg" { + xkb_keycodes { include "xfree86" }; + xkb_types { include "default+hurd" }; + xkb_compatibility { include "default" }; + xkb_symbols { include "pc(pc105)+bg+hurd" }; + xkb_geometry { include "pc(pc102)" }; +}; +// us_intl and br by Ricardo Y. Igarashi (iga@that.com.br) +xkb_keymap "br" { + xkb_keycodes { include "xfree86" }; + xkb_types { include "default+hurd" }; + xkb_compatibility { include "default" }; + xkb_symbols { include "pc(pc105)+br+hurd" }; + xkb_geometry { include "pc(abnt2)" }; +}; +// cz and sk keymaps by Kamil Toman (ktoman@email.cz) +// are designed to replace old czechoslovakian and czsk keyboards +// and their prog variants. Those are now obsolete and should not be used anymore. +xkb_keymap "cz" { + xkb_keycodes { include "xfree86" }; + xkb_types { include "default+hurd" }; + xkb_compatibility { include "default" }; + xkb_symbols { include "pc(pc105)+cz+hurd" }; + xkb_geometry { include "pc" }; +}; +xkb_keymap "de" { + xkb_keycodes { include "xfree86" }; + xkb_types { include "default+hurd" }; + xkb_compatibility { include "default" }; + xkb_symbols { include "pc(pc105)+de+hurd" }; + xkb_geometry { include "pc(pc102)" }; +}; +xkb_keymap "ch_de" { + xkb_keycodes { include "xfree86" }; + xkb_types { include "default+hurd" }; + xkb_compatibility { include "default" }; + xkb_symbols { include "pc(pc105)+ch(de)+hurd" }; + xkb_geometry { include "pc(pc102)" }; +}; +xkb_keymap "ch_fr" { + xkb_keycodes { include "xfree86" }; + xkb_types { include "default+hurd" }; + xkb_compatibility { include "default" }; + xkb_symbols { include "pc(pc105)+ch(fr)+hurd" }; + xkb_geometry { include "pc(pc102)" }; +}; +xkb_keymap "dk" { + xkb_keycodes { include "xfree86" }; + xkb_types { include "default+hurd" }; + xkb_compatibility { include "default" }; + xkb_symbols { include "pc(pc105)+dk+hurd" }; + xkb_geometry { include "pc(pc102)" }; +}; +xkb_keymap "dvorak" { + xkb_keycodes { include "xfree86" }; + xkb_types { include "default+hurd" }; + xkb_compatibility { include "default" }; + xkb_symbols { include "pc(pc105)+us(dvorak)+hurd"}; + xkb_geometry { include "pc(pc102)" }; +}; +xkb_keymap "en_US" { + xkb_keycodes { include "xfree86" }; + xkb_types { include "default+hurd" }; + xkb_compatibility { include "default" }; + xkb_symbols { include "pc(pc105)+us+hurd" }; + xkb_geometry { include "pc" }; +}; +xkb_keymap "es" { + xkb_keycodes { include "xfree86" }; + xkb_types { include "default+hurd" }; + xkb_compatibility { include "default" }; + xkb_symbols { include "pc(pc105)+es+hurd" }; + xkb_geometry { include "pc(pc102)" }; +}; +xkb_keymap "fr" { + xkb_keycodes { include "xfree86" }; + xkb_types { include "default+hurd" }; + xkb_compatibility { include "default" }; + xkb_symbols { include "pc(pc105)+fr+hurd" }; + xkb_geometry { include "pc(pc102)" }; +}; +xkb_keymap "fr-latin9" { + xkb_keycodes { include "xfree86" }; + xkb_types { include "default+hurd" }; + xkb_compatibility { include "default" }; + xkb_symbols { include "pc(pc105)+fr-latin9+hurd" }; + xkb_geometry { include "pc" }; +}; +xkb_keymap "fr_CA" { + xkb_keycodes { include "xfree86" }; + xkb_types { include "default+hurd" }; + xkb_compatibility { include "default" }; + xkb_symbols { include "pc(pc105)+ca+hurd" }; + xkb_geometry { include "pc(pc102)" }; +}; +xkb_keymap "gb" { + xkb_keycodes { include "xfree86" }; + xkb_types { include "default+hurd" }; + xkb_compatibility { include "default" }; + xkb_symbols { include "pc(pc105)+gb+hurd" }; + xkb_geometry { include "pc(pc102)" }; +}; +xkb_keymap "hr" { + xkb_keycodes { include "xfree86" }; + xkb_types { include "default+hurd" }; + xkb_compatibility { include "default" }; + xkb_symbols { include "pc(pc105)+hr+hurd" }; + xkb_geometry { include "pc(pc102)" }; +}; +xkb_keymap "it" { + xkb_keycodes { include "xfree86" }; + xkb_types { include "default+hurd" }; + xkb_compatibility { include "default" }; + xkb_symbols { include "pc(pc105)+it+hurd" }; + xkb_geometry { include "pc(pc102)" }; +}; +xkb_keymap "jp106" { + xkb_keycodes { include "xfree86" }; + xkb_types { include "default+hurd" }; + xkb_compatibility { include "default" }; + xkb_symbols { include "jp(jp106)+hurd" }; + xkb_geometry { include "pc(jp106)" }; +}; +xkb_keymap "lt" { + xkb_keycodes { include "xfree86" }; + xkb_types { include "default+hurd" }; + xkb_compatibility { include "default" }; + xkb_symbols { include "pc(pc105)+lt+hurd" }; + xkb_geometry { include "pc(pc102)" }; +}; +xkb_keymap "lt_std" { + xkb_keycodes { include "xfree86" }; + xkb_types { include "default+hurd" }; + xkb_compatibility { include "default" }; + xkb_symbols { include "pc(pc105)+lt(std)+hurd" }; + xkb_geometry { include "pc(pc102)" }; +}; +xkb_keymap "lv" { + xkb_keycodes { include "xfree86" }; + xkb_types { include "default+hurd" }; + xkb_compatibility { include "default" }; + xkb_symbols { include "pc(pc105)+lv+hurd" }; + xkb_geometry { include "pc(pc102)" }; +}; +xkb_keymap "mk" { + xkb_keycodes { include "xfree86" }; + xkb_types { include "default+hurd" }; + xkb_compatibility { include "default" }; + xkb_symbols { include "pc(pc105)+mk+hurd" }; + xkb_geometry { include "pc(pc102)" }; +}; +xkb_keymap "mt" { + xkb_keycodes { include "xfree86" }; + xkb_types { include "default+hurd" }; + xkb_compatibility { include "default" }; + xkb_symbols { include "pc(pc105)+mt+hurd" }; + xkb_geometry { include "pc(pc102)" }; +}; +xkb_keymap "neo" { + xkb_keycodes { include "xfree86" }; + xkb_types { include "default+hurd" }; + xkb_compatibility { include "default+caps(caps_lock)+misc(assign_shift_left_action)+level5(level5_lock)" }; + xkb_symbols { include "pc(pc105)+de(neo)+hurd" }; + xkb_geometry { include "pc(pc102)" }; +}; +xkb_keymap "no" { + xkb_keycodes { include "xfree86" }; + xkb_types { include "default+hurd" }; + xkb_compatibility { include "default" }; + xkb_symbols { include "pc(pc105)+no+hurd" }; + xkb_geometry { include "pc(pc102)" }; +}; +xkb_keymap "pl" { + xkb_keycodes { include "xfree86" }; + xkb_types { include "default+hurd" }; + xkb_compatibility { include "default" }; + xkb_symbols { include "pc(pc105)+pl+hurd" }; + xkb_geometry { include "pc(pc102)" }; +}; +xkb_keymap "pt" { + xkb_keycodes { include "xfree86" }; + xkb_types { include "default+hurd" }; + xkb_compatibility { include "default" }; + xkb_symbols { include "pc(pc105)+pt+hurd" }; + xkb_geometry { include "pc(pc102)" }; +}; +// ro: additions by Cristian Gafton, <gafton@redhat.com> +xkb_keymap "ro" { + xkb_keycodes { include "xfree86" }; + xkb_types { include "default+hurd" }; + xkb_compatibility { include "default" }; + xkb_symbols { include "pc(pc105)+ro(pc101)+hurd" }; + xkb_geometry { include "pc(pc101)" }; +}; +xkb_keymap "ro_microsoft" { + xkb_keycodes { include "xfree86" }; + xkb_types { include "default+hurd" }; + xkb_compatibility { include "default" }; + xkb_symbols { include "pc(pc105)+ro(pc105)+hurd" }; + xkb_geometry { include "pc(pc105)" }; +}; +xkb_keymap "ru" { + xkb_keycodes { include "xfree86" }; + xkb_types { include "default+hurd" }; + xkb_compatibility { include "default" }; + xkb_symbols { include "pc(pc105)+ru+hurd" }; + xkb_geometry { include "pc(pc102)" }; +}; +xkb_keymap "se_FI" { + xkb_keycodes { include "xfree86" }; + xkb_types { include "default+hurd" }; + xkb_compatibility { include "default" }; + xkb_symbols { include "pc(pc105)+fi+hurd" }; + xkb_geometry { include "pc(pc102)" }; +}; +xkb_keymap "se_SE" { + xkb_keycodes { include "xfree86" }; + xkb_types { include "default+hurd" }; + xkb_compatibility { include "default" }; + xkb_symbols { include "pc(pc105)+se+hurd" }; + xkb_geometry { include "pc(pc102)" }; +}; +xkb_keymap "sl" { + xkb_keycodes { include "xfree86" }; + xkb_types { include "default+hurd" }; + xkb_compatibility { include "default" }; + xkb_symbols { include "pc(pc105)+si+hurd" }; + xkb_geometry { include "pc(pc102)" }; +}; +xkb_keymap "sl_SI" { + xkb_keycodes { include "xfree86" }; + xkb_types { include "default+hurd" }; + xkb_compatibility { include "default" }; + xkb_symbols { include "pc(pc105)+si+hurd" }; + xkb_geometry { include "pc(pc102)" }; +}; +// cz and sk keymaps by Kamil Toman (ktoman@email.cz) +// are designed to replace old czechoslovakian and czsk keyboards +// and their prog variants. Those are now obsolete and should not be used anymore. +xkb_keymap "sk" { + xkb_keycodes { include "xfree86" }; + xkb_types { include "default+hurd" }; + xkb_compatibility { include "default" }; + xkb_symbols { include "pc(pc105)+sk+hurd" }; + xkb_geometry { include "pc" }; +}; +// Additions by Emil Soleyman-Zomalan, <emil@nishra.com> +xkb_keymap "syr" { + xkb_keycodes { include "xfree86" }; + xkb_types { include "default+hurd" }; + xkb_compatibility { include "default" }; + xkb_symbols { include "pc(pc105)+syr+hurd" }; + xkb_geometry { include "pc(pc102)" }; +}; +xkb_keymap "th" { + xkb_keycodes { include "xfree86" }; + xkb_types { include "default+hurd" }; + xkb_compatibility { include "default" }; + xkb_symbols { include "pc(pc105)+th+hurd" }; + xkb_geometry { include "pc(pc102)" }; +}; +xkb_keymap "th_tis" { + xkb_keycodes { include "xfree86" }; + xkb_types { include "default+hurd" }; + xkb_compatibility { include "default" }; + xkb_symbols { include "pc(pc105)+th(tis)+hurd" }; + xkb_geometry { include "pc(pc102)" }; +}; +xkb_keymap "th_pat" { + xkb_keycodes { include "xfree86" }; + xkb_types { include "default+hurd" }; + xkb_compatibility { include "default" }; + xkb_symbols { include "pc(pc105)+th(pat)+hurd" }; + xkb_geometry { include "pc(pc102)" }; +}; +xkb_keymap "tr" { + xkb_keycodes { include "xfree86" }; + xkb_types { include "default+hurd" }; + xkb_compatibility { include "default" }; + xkb_symbols { include "pc(pc105)+tr+hurd" }; + xkb_geometry { include "pc(pc102)" }; +}; +xkb_keymap "uk" { + xkb_keycodes { include "xfree86" }; + xkb_types { include "default+hurd" }; + xkb_compatibility { include "default" }; + xkb_symbols { include "pc(pc105)+uk)+hurd" }; + xkb_geometry { include "pc(pc102)" }; +}; +xkb_keymap "us_flexpro" { + xkb_keycodes { include "xfree86" }; + xkb_types { include "default+hurd" }; + xkb_compatibility { include "default" }; + xkb_symbols { include "us(pc105)+hurd" }; + xkb_geometry { include "keytronic(FlexPro)" }; +}; +// us_intl and br by Ricardo Y. Igarashi (iga@that.com.br) +// us_intl means standard us keyboard plus dead_keys symbols +// these keyboards are very popular in Brazil +xkb_keymap "us_intl" { + xkb_keycodes { include "xfree86" }; + xkb_types { include "default+hurd" }; + xkb_compatibility { include "default" }; + xkb_symbols { include "us(pc101)+us(intl)+hurd" }; + xkb_geometry { include "pc" }; +}; +xkb_keymap "us_microsoft" { + xkb_keycodes { include "xfree86" }; + xkb_types { include "default+hurd" }; + xkb_compatibility { include "default" }; + xkb_symbols { include "us(pc105)+hurd" }; + xkb_geometry { include "microsoft" }; +}; + +xkb_keymap "uz" { + xkb_keycodes { include "xfree86" }; + xkb_types { include "default+hurd" }; + xkb_compatibility { include "default" }; + xkb_symbols { include "pc(pc105)+uz+hurd" }; + xkb_geometry { include "pc(pc102)" }; +}; + +# svu: compatibility keymaps, based on variants +xkb_keymap "cz_qwerty" { + xkb_keycodes { include "xfree86" }; + xkb_types { include "default+hurd" }; + xkb_compatibility { include "default" }; + xkb_symbols { include "pc(pc105)+cz(qwerty)+hurd" }; + xkb_geometry { include "pc" }; +}; +xkb_keymap "de_CH" { + xkb_keycodes { include "xfree86" }; + xkb_types { include "default+hurd" }; + xkb_compatibility { include "default" }; + xkb_symbols { include "pc(pc105)+ch(de)+hurd" }; + xkb_geometry { include "pc(pc102)" }; +}; +xkb_keymap "fr_CH" { + xkb_keycodes { include "xfree86" }; + xkb_types { include "default+hurd" }; + xkb_compatibility { include "default" }; + xkb_symbols { include "pc(pc105)+ch(fr)+hurd" }; + xkb_geometry { include "pc(pc102)" }; +}; +xkb_keymap "mt_us" { + xkb_keycodes { include "xfree86" }; + xkb_types { include "default+hurd" }; + xkb_compatibility { include "default" }; + xkb_symbols { include "pc(pc105)+mt(us)+hurd" }; + xkb_geometry { include "pc(pc102)" }; +}; +xkb_keymap "sk_qwerty" { + xkb_keycodes { include "xfree86" }; + xkb_types { include "default+hurd" }; + xkb_compatibility { include "default" }; + xkb_symbols { include "pc(pc105)+sk(qwerty)+hurd" }; + xkb_geometry { include "pc" }; +}; diff --git a/console-client/xkb/xkb-data/symbols/hurd b/console-client/xkb/xkb-data/symbols/hurd new file mode 100644 index 00000000..39edf2ae --- /dev/null +++ b/console-client/xkb/xkb-data/symbols/hurd @@ -0,0 +1,125 @@ +// -*- Mode: C -*- +default +xkb_symbols "hurd" { + /* Switch to local consoles by default. */ + // SwitchScreen.SameServer; + + /* Make F1 - F10 switch virtual consoles when Alt is held down. */ + key <FK01> + { + type[Group1] = "HURD", + actions[Group1] = [ NoAction () , SwitchScreen (screen = 1), + NoAction () ] + }; + key <FK02> + { + type[Group1] = "HURD", + actions[Group1] = [ NoAction () , SwitchScreen (screen = 2), + NoAction () ] + }; + key <FK03> + { + type[Group1] = "HURD", + actions[Group1] = [ NoAction () , SwitchScreen (screen = 3), + NoAction () ] + }; + key <FK04> + { + type[Group1] = "HURD", + actions[Group1] = [ NoAction () , SwitchScreen (screen = 4), + NoAction () ] + }; + key <FK05> + { + type[Group1] = "HURD", + actions[Group1] = [ NoAction () , SwitchScreen (screen = 5), + NoAction () ] + }; + key <FK06> + { + type[Group1] = "HURD", + actions[Group1] = [ NoAction () , SwitchScreen (screen = 6), + NoAction () ] + }; + key <FK07> + { + type[Group1] = "HURD", + actions[Group1] = [ NoAction () , SwitchScreen (screen = 7), + NoAction () ] + }; + key <FK08> + { + type[Group1] = "HURD", + actions[Group1] = [ NoAction () , SwitchScreen (screen = 8), + NoAction () ] + }; + key <FK09> + { + type[Group1] = "HURD", + actions[Group1] = [ NoAction () , SwitchScreen (screen = 9), + NoAction () ] + }; + key <FK10> + { + type[Group1] = "HURD", + actions[Group1] = [ NoAction () , SwitchScreen (screen = 10), + NoAction () ] + }; + + // Make the left and right cursor keys switch virtual consoles when + // Alt is held down. + key <LEFT> + { + type[Group1] = "HURD", + actions[Group1] = [ NoAction (), SwitchScreen (screen -= 1), + NoAction () ] + }; + key <RGHT> + { + type[Group1] = "HURD", + actions[Group1] = [ NoAction (), SwitchScreen (screen += 1), + NoAction () ] + }; + + // Scroll the console up or down (one line). + key <UP> + { + type[Group1] = "HURD", + actions[Group1] = [ NoAction (), ConsScroll (line -= 1) ] + }; + key <DOWN> + { + type[Group1] = "HURD", + actions[Group1] = [ NoAction (), ConsScroll (line += 1) ] + }; + + // Scroll the console up or down (1/2 screen). + key <PGUP> + { + type[Group1] = "HURD", + actions[Group1] = [ NoAction (), NoAction (), + ConsScroll (screen -= 0.5) ] + }; + key <PGDN> + { + type[Group1] = "HURD", + actions[Group1] = [ NoAction (), NoAction (), + ConsScroll (screen += 0.5) ] + }; + + // Scroll the console to 0%, 25%, 75% or 100%. + key <HOME> + { + type[Group1] = "HURD", + actions[Group1] = [ NoAction (), + ConsScroll (percentage = 0), + ConsScroll (percentage = 25) ] + }; + key <END> + { + type[Group1] = "HURD", + actions[Group1] = [ NoAction (), + ConsScroll (percentage = 100), + ConsScroll (percentage = 75) ] + }; +}; diff --git a/console-client/xkb/xkb-data/types/hurd b/console-client/xkb/xkb-data/types/hurd new file mode 100644 index 00000000..e6c7aa0c --- /dev/null +++ b/console-client/xkb/xkb-data/types/hurd @@ -0,0 +1,18 @@ +// -*- Mode: C -*- + +default xkb_types "hurd" { + virtual_modifiers Alt; + + type "HURD" + { + modifiers = Shift + Alt + Control; + map[Alt] = Level2; + map[Shift] = Level3; + map[Control] = Level4; + level_name[Level1] = "Base"; + level_name[Level2] = "Hurd console"; + level_name[Level3] = "Hurd console2"; + level_name[Level4] = "Hurd console3"; + }; +}; + diff --git a/console-client/xkb/xkb.c b/console-client/xkb/xkb.c new file mode 100644 index 00000000..0b439134 --- /dev/null +++ b/console-client/xkb/xkb.c @@ -0,0 +1,1380 @@ +/* xkb.c -- Main XKB routines. + + Copyright (C) 2002, 2003, 2004 Marco Gerards + + Written by Marco Gerards <metgerards@student.han.nl> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include <fcntl.h> +#include <string.h> +#include <stdio.h> +#include <errno.h> +#include <ctype.h> +#include <stdlib.h> +#include <iconv.h> +#include <locale.h> +#include <error.h> +#include <device/device.h> +#include <mach/mach_port.h> + +#include "xkb.h" +#include <hurd/console.h> +#define XK_XKB_KEYS +#define XK_MISCELLANY +#include <X11/keysymdef.h> +#include <driver.h> +#include <mach-inputdev.h> +#include <wctype.h> + + + +/* The converter. */ +extern iconv_t cd; + +/* All interpretations for compatibility. (Translation from keysymbol + to actions). */ +xkb_interpret_t *interpretations; + +/* All keysymbols and how they are handled by XKB. */ +struct key *keys = NULL; +int min_keys; +int max_keys; + +/* The current set of modifiers. */ +static modmap_t bmods; +/* Temporary set of modifiers. This is a copy of mods, so mods won't + be consumed (Effective modifiers). */ +static modmap_t emods; + +/* Effective group. */ +static group_t egroup; +/* Base group. */ +static group_t bgroup; +/* Locked group. */ +static group_t lgroup; +/* Latched group. */ +static group_t latchedgroup; + +static boolctrls bboolctrls; + +/* A counter to count how often the modifier was set. This is used + when two seperate actions set the same modifier. (example: Left + Shift and Right Shift.). */ +modcount_t modsc; + +keystate_t keystate[255]; + +/* The locked modifiers. Lock simply works an an invertion. */ +static modmap_t lmods = {0, 0}; + +/* When the modifier is used the modifier will be consumed. */ +static modmap_t latchedmods = {0, 0}; + +/* Not setting GroupsWrap uses modulus to keep the value into the + range. */ +static int GroupsWrap = 0; + +/* MouseKeys, default: off. */ +static int MouseKeys = 0; +/* Default mousebutton. */ +static int default_button = 0; + +static xkb_indicator_t *indicators; +static int indicator_count; +static int indicator_map = 0; + +/* unused +static int stickykeys_active = 1; +*/ + +int +debug_printf (const char *f, ...) +{ + va_list ap; + int ret = 0; + + va_start (ap, f); +#ifdef XKB_DEBUG + ret = vfprintf (stderr, f, ap); +#endif + va_end (ap); + + return ret; +} + + +static void +interpret_kc (keycode_t kc) +{ + int cursym; + int rmods = keys[kc].mods.rmods; + struct xkb_interpret *interp; + + for (interp = interpretations; interp; interp = interp->next) + { + group_t group; + + for (group = 0; group < keys[kc].numgroups; group++) + { + int width = keys[kc].groups[group].width; + + for (cursym = 0; cursym < width; cursym++) + { + int symbol = keys[kc].groups[group].symbols[cursym]; + + /* Check if a keysymbol requirement exists or if it + matches. */ + if (interp->symbol == 0 || + (symbol && (interp->symbol == symbol))) + { + int flags = interp->match & 0x7f; + + /* XXX: use enum. */ + if ((flags == 0 && (!(interp->rmods & rmods))) || + (flags == 1) || + (flags == 2 && (interp->rmods & rmods)) || + (flags == 3 && ((interp->rmods & rmods) == + interp->rmods)) || + (flags == 4 && interp->rmods == rmods)) + { + xkb_action_t *action; + + if (keys[kc].groups[group].actionwidth > cursym && + keys[kc].groups[group].actions[cursym] && + keys[kc].groups[group].actions[cursym]->type != + SA_NoAction) + continue; + + action = malloc (sizeof (xkb_action_t)); + memcpy (action, &interp->action, sizeof (xkb_action_t)); + + key_set_action (&keys[kc], group, cursym, action); + + keys[kc].flags = interp->flags | KEYHASACTION; + if (!keys[kc].mods.vmods) + keys[kc].mods.vmods = interp->vmod; + } + } + } + } + } + +} + + +/* Test if c is an uppercase letter. */ +static int islatin_upper (int c) +{ + return (c >= 'A' && c <= 'Z'); +} + +/* Test if c is an lowercase letter. */ +static int islatin_lower (int c) +{ + return (c >= 'a' && c <= 'z'); +} + +/* A key is of the keytype KEYPAD when one of the symbols that can be produced + by this key is in the KEYPAD symbol range. */ +static int +iskeypad (int width, int *sym) +{ + int i; + + for (i = 0; i < width; i++, sym++) + { + /* Numlock is in the keypad range but shouldn't be of the type + keypad because it will depend on itself in that case. */ + if (*sym == XK_Num_Lock) + return 0; + if (*sym >= KEYPAD_FIRST_KEY && *sym <= KEYPAD_LAST_KEY) + return 1; + } + return 0; +} + +/* Get the keytype (the keytype determines which modifiers are used + for shifting. + + For reference, see FindAutomaticType@xkbcomp/symbols.c. + + These rules are used: + + * If the width is 1 the keytype is ONE_LEVEL. + * If the first symbol is lowercase and the second is uppercase + (latin alphabeth) the keytype is ALPHABETHIC. + * If one of the symbols is in the keypad range the keytype is KEYPAD. + * If width is 4 the type is either FOUR_LEVEL, FOUR_LEVEL_ALPHABETIC, + FOUR_LEVEL_SEMI_ALPHABETIC (first sym pair is alphabetic) or + FOUR_LEVEL_KEYPAD. + * Else the keytype is TWO_LEVEL. */ +static struct keytype * +get_keytype (int width, symbol *sym) +{ + struct keytype *ktfound = NULL; + + if (!sym) + ktfound = keytype_find ("TWO_LEVEL"); + else if ((width == 1) || (width == 0)) + ktfound = keytype_find ("ONE_LEVEL"); + else if (width == 2) { + if (islatin_lower (sym[0]) && islatin_upper (sym[1])) + ktfound = keytype_find ("ALPHABETIC"); + else if (iskeypad (width, sym)) + ktfound = keytype_find ("KEYPAD"); + else + ktfound = keytype_find ("TWO_LEVEL"); + } + else if (width <= 4) { + if (islatin_lower (sym[0]) && islatin_upper (sym[1])) + if (islatin_lower(sym[2]) && islatin_upper(sym[3])) + ktfound = keytype_find ("FOUR_LEVEL_ALPHABETIC"); + else + ktfound = keytype_find ("FOUR_LEVEL_SEMIALPHABETIC"); + else if (iskeypad (2, sym)) + ktfound = keytype_find ("FOUR_LEVEL_KEYPAD"); + else + ktfound = keytype_find ("FOUR_LEVEL"); + } + + if (!ktfound) + ktfound = keytype_find ("TWO_LEVEL"); + if (!ktfound) + { + console_error (L"Default keytypes have not been defined!\n"); + exit (1); + } + + return ktfound; +} + +/* Create XKB style actions for every action described by keysymbols. */ +static void +interpret_all (void) +{ + keycode_t curkc; + + /* Check every key. */ + for (curkc = 0; curkc < max_keys; curkc++) + interpret_kc (curkc); +} + +static void +determine_keytypes (void) +{ + keycode_t curkc; + + /* Check every key. */ + for (curkc = 0; curkc < max_keys; curkc++) + { + group_t group; + for (group = 0; group < 4; group++) + { + struct keygroup *kg = &keys[curkc].groups[group]; + + if (!kg->keytype) + kg->keytype = get_keytype (kg->width, kg->symbols); + } + } +} + +/* Wrap the group GROUP into a valid group range. The method to use is + defined by the GroupsWrap control. */ +static int +wrapgroup (group_t group, int maxgroup) +{ + /* If the group is in an invalid range, fix it. */ + if (group < 0 || group >= maxgroup) + { + /* If RedirectIntoRange should be used use the 4 LSbs of + the GroupsWrap control instead of the current group. */ + if (GroupsWrap & RedirectIntoRange) + group = GroupsWrap & 0x0F; + /* Select the closest valid group. */ + else if (GroupsWrap & ClampIntoRange) + { + if (group < 0) + group = 0; + else + group = maxgroup - 1; + } + else /* Default: use modulus to wrap the group. */ + group %= maxgroup; + } + + return group; +} + + + +/* This function must be called after a modifier, group or control has + been changed. The indicator map will be regenerated and the hardwre + representation of this map will be updated. */ +static void +set_indicator_mods (void) +{ + int i; + + /* Calculate the effective modmap. */ + emods = bmods; + emods.rmods |= lmods.rmods; + emods.vmods |= lmods.vmods; + emods.rmods |= latchedmods.rmods; + emods.vmods |= latchedmods.vmods; + + for (i = 0; i < indicator_count; i++) + { + if (!(indicators[i].flags & IM_NoAutomatic)) + { + if (indicators[i].which_mods & IM_UseBase) + { + if (((indicators[i].modmap.rmods & bmods.rmods) == + indicators[i].modmap.rmods) && + ((indicators[i].modmap.vmods & bmods.vmods) == + indicators[i].modmap.vmods)) + { + indicator_map |= (1 << i); + continue; + } + } + if (indicators[i].which_mods & IM_UseLatched) + { + if (((indicators[i].modmap.rmods & latchedmods.rmods) == + indicators[i].modmap.rmods) && + ((indicators[i].modmap.vmods & latchedmods.vmods) == + indicators[i].modmap.vmods)) + { + indicator_map |= (1 << i); + continue; + } + } + if (indicators[i].which_mods & IM_UseLocked) + { + if (((indicators[i].modmap.rmods & lmods.rmods) == + indicators[i].modmap.rmods) && + ((indicators[i].modmap.vmods & lmods.vmods) == + indicators[i].modmap.vmods)) + { + indicator_map |= (1 << i); + continue; + } + } + if (indicators[i].which_mods & IM_UseEffective) + { + if (((indicators[i].modmap.rmods & emods.rmods) == + indicators[i].modmap.rmods) && + ((indicators[i].modmap.vmods & emods.vmods) == + indicators[i].modmap.vmods)) + { + indicator_map |= (1 << i); + continue; + } + } + /* The indicator shouldn't be on anymore so turn it off. */ + indicator_map &= ~(1 << i); + } + } + debug_printf ("INDICATOR: %d\n", indicator_map); +} + +/* Set base modifiers. A counter exists for every modifier. When a + modifier is set this counter will be incremented with one. */ +static void +setmods (modmap_t smods, keypress_t key) +{ + /* Higher the modcount for every set modifier. */ + void set_xmods (int xmods, int modsc[]) + { + int i = 0; + + while (xmods) + { + if (xmods & 1) + modsc[i]++; + + xmods >>= 1; + i++; + } + } + + bmods.rmods |= smods.rmods; + bmods.vmods |= smods.vmods; + + set_xmods (smods.rmods, modsc.rmods); + set_xmods (smods.vmods, modsc.vmods); + + set_indicator_mods (); +} + +/* Clear base modifiers. A counter exists for every modifier. When a + key release wants to clear a modifier this counter will be + decreased with one. When the counter becomes 0 the modifier will be + cleared and unlocked if the clearLocks flag is set. */ +static void +clearmods (modmap_t cmods, keypress_t key, int flags) +{ +#define CLEAR_XMOD(MODTYPE) \ + { \ + int i = 0; \ + int mmods = cmods.MODTYPE; \ + \ + while (mmods) \ + { \ + if (mmods & 1) \ + if (!(--modsc.MODTYPE[i])) \ + { \ + bmods.MODTYPE &= ~(1 << i); \ + if (flags & clearLocks) \ + lmods.MODTYPE &= ~(1 << i); \ + } \ + mmods >>= 1; \ + i++; \ + } \ + } + + CLEAR_XMOD(rmods); + CLEAR_XMOD(vmods); + + set_indicator_mods (); +} + +/* Set modifiers in smods and also lock them if the flag noLock is + not set. */ +static void +setlocks (modmap_t smods, keypress_t key, int flags) +{ + if (!key.rel) + { + modmap_t cmods; + cmods.rmods = lmods.rmods & smods.rmods; + cmods.vmods = lmods.vmods & smods.vmods; + + /* Locking also sets the base modifiers. */ + setmods (smods, key); + + if (!(flags & noUnlock)) + { + lmods.rmods &= ~cmods.rmods; + lmods.vmods &= ~cmods.vmods; + } + + if (!(flags & noLock)) + { + lmods.rmods |= (~cmods.rmods & smods.rmods); + lmods.vmods |= (~cmods.vmods & smods.vmods); + } + } + else + clearmods (smods, key, flags); +} + +/* Latch the modifiers smods for key KEY. When another key is operated while + KEY is pressed the release of KEY will just clear the base + modifiers, otherwise latch the modifiers like is described in the + protocol specification. */ +static void +latchmods (modmap_t smods, keypress_t key, int flags) +{ + if (!key.rel) + setmods (smods, key); + else + { + modmap_t oldlmods; + oldlmods = lmods; + clearmods (smods, key, flags); + /* Modifier that have been unlocked don't have effect anymore. */ + smods.rmods &= ~(lmods.rmods & oldlmods.rmods); + smods.vmods &= ~(lmods.vmods & oldlmods.vmods); + + + /* If another key has been pressed while this modifier key was + pressed don't latch, just behave as SetMods. */ + if (key.keycode == key.prevkc) + { + if (flags & latchToLock) + { + /* When a modifier exists as a locked modifier, consume + and unlock. */ + lmods.rmods |= latchedmods.rmods & smods.rmods; + lmods.vmods |= latchedmods.vmods & smods.vmods; + + smods.rmods &= ~(latchedmods.rmods & smods.rmods); + smods.vmods &= ~(latchedmods.vmods & smods.vmods); + } + + /* Use the remaining modifiers for latching. */ + latchedmods.rmods |= smods.rmods; + latchedmods.vmods |= smods.vmods; + } + } +} + +static void +setgroup (keypress_t key, group_t group, int flags) +{ + debug_printf ("Setgroup ()\n"); + if (!key.rel) + { + if (flags & groupAbsolute) + { + bgroup = group; + keystate[key.keycode].oldgroup = bgroup; + } + else + bgroup += group; + } + else + { + if ((key.keycode == key.prevkc) && (flags & clearLocks)) + lgroup = 0; + if (flags & groupAbsolute) + bgroup = keystate[key.keycode].oldgroup; + else + /* XXX: Maybe oldgroup should be restored for !groupAbsolute + too, because wrapgroup might have affected the calculation + and substracting will not undo the set operation. However + this way of handeling relative groups is better because the + order of releasing keys when multiple relative setgroup keys + are pressed doesn't matter. */ + bgroup -= group; + } +} + +static void +latchgroup (keypress_t key, group_t sgroup, int flags) +{ + group_t oldlgroup = sgroup; + setgroup (key, sgroup, flags); + debug_printf ("Latchgroup ()\n"); + + if (key.keycode == key.prevkc && oldlgroup == lgroup) + { + if ((flags & latchToLock) && latchedgroup) + { + lgroup += sgroup; + latchedgroup -= sgroup; + } + else + latchedgroup += sgroup; + } +} + +static void +lockgroup (keypress_t key, group_t group, int flags) +{ + debug_printf (">L: %d, g: %d\n", lgroup, group); + + keystate[key.keycode].oldgroup = lgroup; + if (flags & groupAbsolute) + lgroup = group; + else + lgroup += group; + + lgroup = wrapgroup (lgroup, 4); + + debug_printf ("<L: %d, g: %d\n", lgroup, group); + +} + +static void +setcontrols (keypress_t key, boolctrls ctrls, int flags) +{ + keystate[key.keycode].bool = ctrls & ~bboolctrls; + bboolctrls |= ctrls; +} + +static void +clearcontrols (keypress_t key, boolctrls ctrls, int flags) +{ + bboolctrls &= ~keystate[key.keycode].bool; +} + +static void +lockcontrols (keypress_t key, boolctrls ctrls, int flags) +{ + if (!key.rel) + { + //setcontrols (key, boolctrls, flags); + if (!(flags & noLock)); + // lboolctrls |= boolctrls; + } + else + { + // clearcontrols (key, boolctrls, flags); + /* This unlock behaviour doesnt work and sucks, just like the protocol + specification where it was documented. */ + // if (!(flags & noUnlock)) + // lboolctrls &= ~keystate[key.keycode].bool; + } + +} + +/* Not properly implemented, not very high priority for me. */ +/* static void */ +/* isolock (keypress_t key, group group, modmap_t mods, int flags) */ +/* { */ +/* if (!key.rel) */ +/* { */ +/* struct isolock *newiso; */ + +/* if (flags & dfltIsGroup) */ +/* setgroup (key, group, flags & groupAbsolute); */ +/* else */ +/* setmods (key, mods, 0); */ + +/* newiso = malloc (sizeof struct isolock); */ +/* if (!newiso) */ +/* ;// return error */ +/* active_isolocks.anchor */ +/* } */ +/* else */ +/* { */ +/* if (flags & dfltIsGroup) */ +/* { */ +/* cleargroup (key, group, flags & groupAbsolute); */ +/* if (bla) */ +/* lockgroup (key, group, flags & groupAbsolute); */ +/* } */ +/* else */ +/* { */ +/* clearmods (key, mods, 0); */ +/* if (bla) */ +/* { */ +/* lmods.rmods |= mods.rmods; */ +/* lmods.vmods |= mods.vmods; */ +/* } */ +/* } */ +/* } */ +/* } */ + +/* Move the mousepointer relational to its current position. */ +static void +mouse_x_move (int xdelta) +{ + /* XXX: Ofcouse this function has to do *something* :). */ +} + +/* Change the horizontal position of the mousepointer. */ +static void +mouse_x_move_to (int x) +{ + /* XXX: Ofcouse this function has to do *something* :). */ +} + +/* Move the mousepointer relational to its current position. */ +static void +mouse_y_move (int ydelta) +{ + /* XXX: Ofcouse this function has to do *something* :). */ +} + +/* Change the vertical position of the mousepointer. */ +static void +mouse_y_move_to (int y) +{ + /* XXX: Ofcouse this function has to do *something* :). */ +} + +/* Simulate a mouse button press for button BUTTON. */ +static void +mouse_button_press (int button) +{ + /* XXX: Ofcouse this function has to do *something* :). */ +} + +/* Simulate a mouse button press for button BUTTON. */ +static void +mouse_button_release (int button) +{ + /* XXX: Ofcouse this function has to do *something* :). */ +} + + + +/* Forward declaration for redirected keys. */ +static symbol handle_key (keypress_t); + + +/* Execute an action bound to a key. When the action isn't supported + or when the action doesn't consume the key return true, otherwise + return false. */ +static int +action_exec (xkb_action_t *action, keypress_t key) +{ + if (!action) + return KEYNOTCONSUMED; + + debug_printf ("EXEC: %d\n", action->type); + + switch (action->type) + { + /* LockMods: Lock/Unlock modifiers when the key is pressed. */ + case SA_LockMods: + { + action_setmods_t *setmodmap = (action_setmods_t *) action; + modmap_t modm = setmodmap->modmap; + + /* UseModMap */ + if (setmodmap->flags & useModMap) + { + modm.rmods |= keys[key.keycode].mods.rmods; + modm.vmods |= keys[key.keycode].mods.vmods; + } + + setlocks (modm, key, setmodmap->flags); + } + break; + /* SetMods: Set/Unset modifiers. Those modifier will be set as + long the key is pressed. Keys like shift, alt and control are + used here often. */ + case SA_SetMods: + { + action_setmods_t *setmodmap = (action_setmods_t *) action; + modmap_t modm = setmodmap->modmap; + + /* UseModMapMods means: also use the real modifiers specified + in the key's modmap. */ + if (setmodmap->flags & useModMap) + { + debug_printf ("Apply modmaps\n"); + modm.rmods |= keys[key.keycode].mods.rmods; + modm.vmods |= keys[key.keycode].mods.vmods; + } + + /* When the key is pressed set the modifiers. */ + if (!key.rel) + setmods (modm, key); + else /* When the key is released clear the modifiers. */ + clearmods (modm, key, setmodmap->flags); + + break; + } + /* Set the basegroup. When groupAbsolute isn't used add it + to the basegroup. */ + case SA_LatchMods: + { + action_setmods_t *setmodmap = (action_setmods_t *) action; + + modmap_t modm = setmodmap->modmap; + + /* UseModMapMods means: also use the real modifiers specified + in the key's modmap. */ + if (setmodmap->flags & useModMap) + { + modm.rmods |= keys[key.keycode].mods.rmods; + modm.vmods |= keys[key.keycode].mods.vmods; + } + + latchmods (modm, key, setmodmap->flags); + + break; + } + case SA_SetGroup: + { + action_setgroup_t *setgroupac = (action_setgroup_t *) action; + + setgroup (key, setgroupac->group, setgroupac->flags); + break; + } + case SA_LockGroup: + { + action_setgroup_t *setgroupac = (action_setgroup_t *) action; + + if (!key.rel) + lockgroup (key, setgroupac->group, setgroupac->flags); + break; + } + case SA_LatchGroup: + { + action_setgroup_t *setgroupac = (action_setgroup_t *) action; + + latchgroup (key, setgroupac->group, setgroupac->flags); + break; + } + + case SA_PtrBtn: + { + action_ptrbtn_t *ptrbtnac = (action_ptrbtn_t *) action; + int i; + int button; + + if (!MouseKeys) + return KEYNOTCONSUMED; + + if (ptrbtnac->flags & useDfltBtn) + button = default_button; + else + button = ptrbtnac->button; + + if (ptrbtnac->count) + for (i = 0; i < ptrbtnac->count; i++) + { + /* XXX: Should there be a delay? */ + mouse_button_press (button); + mouse_button_release (button); + } + else if (!key.rel) + mouse_button_press (button); + else + mouse_button_release (button); + break; + } + case SA_LockPtrBtn: + { + action_ptrbtn_t *ptrbtnac = (action_ptrbtn_t *) action; + + int button; + + if (!MouseKeys) + return KEYNOTCONSUMED; + + if (ptrbtnac->flags & useDfltBtn) + button = default_button; + else + button = ptrbtnac->button; + + /* XXX: Do stuff. */ + + break; + } + case SA_SetPtrDflt: + { + action_ptr_dflt_t *ptrdfltac = (action_ptr_dflt_t *) action; + + if (!MouseKeys) + return KEYNOTCONSUMED; + + if (!key.rel) + { + if (ptrdfltac->flags & DfltBtnAbsolute) + default_button = ptrdfltac->value; + else + default_button += ptrdfltac->value; + } + + if (default_button < 0) + default_button = 0; + + if (default_button > 5) + default_button = 5; + + break; + } + case SA_TerminateServer: + /* Zap! */ + console_exit (); + break; + case SA_SwitchScreen: + { + action_switchscrn_t *switchscrnac = (action_switchscrn_t *) action; + + if (key.rel) + break; + + if (switchscrnac->flags & screenAbs) + /* Switch to screen. */ + console_switch ((char) switchscrnac->screen, 0); + else + /* Move to next/prev. screen. */ + console_switch (0, (char) switchscrnac->screen); + break; + } + case SA_RedirectKey: + { + action_redirkey_t *redirkeyac = (action_redirkey_t *) action; + + key.keycode = redirkeyac->newkey & (key.rel ? 0x80:0); + + /* For the redirected key other modifiers should be used. */ + emods.rmods = bmods.rmods | lmods.rmods | latchedmods.rmods; + emods.vmods = bmods.vmods | lmods.vmods | latchedmods.vmods; + + emods.rmods &= ~redirkeyac->rmodsmask; + emods.rmods |= redirkeyac->rmods; + emods.vmods &= ~redirkeyac->vmods; + emods.vmods |= redirkeyac->vmodsmask; + + /* XXX: calc group etc. */ + + handle_key (key); + break; + } + case SA_ConsScroll: + { + action_consscroll_t *scrollac = (action_consscroll_t *) action; + + if (key.rel) + break; + + if (scrollac->flags & usePercentage) + console_scrollback (CONS_SCROLL_ABSOLUTE_PERCENTAGE, + 100 - scrollac->percent); + + if (scrollac->screen) + console_scrollback (CONS_SCROLL_DELTA_SCREENS, -scrollac->screen); + + if (scrollac->line) + { + int type = (scrollac->flags & lineAbs) ? + CONS_SCROLL_ABSOLUTE_LINE : CONS_SCROLL_DELTA_LINES; + console_scrollback (type, -scrollac->line); + } + break; + } + case SA_ActionMessage: + case SA_DeviceBtn: + case SA_LockDeviceBtn: + case SA_DeviceValuator: + return KEYNOTCONSUMED; + case SA_MovePtr: + { + action_moveptr_t *moveptrac = (action_moveptr_t *) action; + + if (!MouseKeys) + return KEYNOTCONSUMED; + + if (moveptrac->flags & MoveAbsoluteX) + mouse_x_move_to (moveptrac->x); + else + mouse_x_move (moveptrac->x); + + if (moveptrac->flags & MoveAbsoluteY) + mouse_y_move_to (moveptrac->y); + else + mouse_y_move (moveptrac->y); + break; + } + case SA_SetControls: + { + action_setcontrols_t *controlsac = (action_setcontrols_t *) action; + if (key.rel) + clearcontrols (key, controlsac->controls, 0); + else + setcontrols (key, controlsac->controls, 0); + break; + } + case SA_LockControls: + { + action_setcontrols_t *controlsac = (action_setcontrols_t *) action; + lockcontrols (key, controlsac->controls, 0); + break; + } + default: + /* Preserve the keycode. */ + return KEYNOTCONSUMED; + break; + } + + /* Don't preserve the keycode because it was consumed. */ + return KEYCONSUMED; +} + + + +/* Calculate the shift level for a specific key. */ +static int +calc_shift (keycode_t key) +{ + /* The keytype for this key. */ + struct keytype *keytype = keys[key].groups[egroup].keytype; + struct typemap *map; + + /* XXX: Shouldn't happen, another way to fix this? */ + if (!keytype) + return 0; + + /* Scan though all modifier to level maps of this keytype to search + the level. */ + for (map = keytype->maps; map; map = map->next) + /* Does this map meet our requirements? */ + if (map->mods.rmods == (emods.rmods & keytype->modmask.rmods) && + map->mods.vmods == (emods.vmods & keytype->modmask.vmods)) + { + /* Preserve all modifiers specified in preserve for this map. */ + emods.rmods &= ~(map->mods.rmods & (~map->preserve.rmods)); + emods.vmods &= ~(map->mods.vmods & (~map->preserve.vmods)); + return map->level; + } + + /* When no map is found use the default shift level and consume all + modifiers. */ + emods.vmods &= ~keytype->modmask.vmods; + emods.rmods &= ~keytype->modmask.rmods; + + return 0; +} + +static symbol +symtoctrlsym (symbol c) +{ + c = toupper (c); + + switch (c) + { + case 'A' ... 'Z': + c = c - 'A' + 1; + break; + case '[': case '3': + c = '\e'; + break; + case '\\': case '4': + c = ''; + break; + case ']': case '5': + c = ''; + break; + case '^': case '6': + c = ''; + break; + case '/': + c = '/'; + break; + case ' ': + c = '\0'; + break; + case '_': case '7': + c= ''; + break; + case '8': + c = '\x7f'; + break; + } + + return c; +} + + +/* Handle all actions, etc. bound to the key KEYCODE and return a XKB + symbol if one is generated by this key. If redirected_key contains + 1 this is keypress generated by the action SA_RedirectKey, don't + change the effective modifiers because they exist and have been + changed by SA_RedirectKey. */ +static symbol +handle_key (keypress_t key) +{ + int actioncompl = 0; + + modmap_t oldmods; + group_t oldgroup = 0; + + /* The level for this key. */ + int level; + + /* The symbol this keypress generated. */ + symbol sym = 0; + + debug_printf ("groups\n"); + /* If the key does not have a group there is nothing to do. */ + if (keys[key.keycode].numgroups == 0) + return -1; + + /* The effective group is the current group, but it can't be + out of range. */ + egroup = wrapgroup (bgroup + lgroup, + keys[key.keycode].numgroups); + + if (keys[key.keycode].groups[egroup].actions) + { + if (key.rel) + { + debug_printf ("action\n"); + if (!keystate[key.keycode].prevstate) + /* Executing the inverse action of a never executed + action... Stop! */ + return -1; + + keystate[key.keycode].prevstate = 0; + emods = keystate[key.keycode].prevmods; + egroup = wrapgroup (keystate[key.keycode].prevgroup, + keys[key.keycode].numgroups); + } + else /* This is a keypress event. */ + { + /* Calculate the effective modmap. */ + emods = bmods; + emods.rmods |= lmods.rmods; + emods.vmods |= lmods.vmods; + emods.rmods |= latchedmods.rmods; + emods.vmods |= latchedmods.vmods; + } + + oldmods = emods; + oldgroup = egroup; + + level = calc_shift (key.keycode);// % + + if (keys[key.keycode].groups[egroup].actionwidth >= level + 1 + && keys[key.keycode].groups[egroup].actions[level]) + { + actioncompl = action_exec + (keys[key.keycode].groups[egroup].actions[level], key); + } + } + + if (actioncompl == KEYCONSUMED && !key.rel) + { + /* An action was executed. Store the effective modifier this key + so the reverse action can be called on key release. */ + keystate[key.keycode].prevstate = 1; + keystate[key.keycode].prevmods = oldmods; + keystate[key.keycode].prevgroup = oldgroup; + } + + debug_printf ("consumed: %d - %d -%d\n", actioncompl, key.rel, + !keys[key.keycode].groups[egroup].width); + /* If the action comsumed the keycode, this is a key release event + or if the key doesn't have any symbols bound to it there is no + symbol returned. */ + if (actioncompl == KEYCONSUMED || key.rel || + !keys[key.keycode].groups[egroup].width) + return -1; + + /* Calculate the effective modmap. */ + emods = bmods; + emods.rmods |= lmods.rmods; + emods.vmods |= lmods.vmods; + emods.rmods |= latchedmods.rmods; + emods.vmods |= latchedmods.vmods; + + level = calc_shift (key.keycode) % keys[key.keycode].groups[egroup].width; + + /* The latched modifier is used for a symbol, clear it. */ + latchedmods.rmods = latchedmods.vmods = 0; + + /* Search the symbol for this key in the keytable. Make sure the + group and shift level exists. */ + sym = keys[key.keycode].groups[egroup].symbols[level]; + + /* Convert keypad symbols to symbols. XXX: Is this the right place + to do this? */ + if ((sym >= XK_KP_Multiply && sym <= XK_KP_Equal) || sym == XK_KP_Enter) + sym &= ~0xFF80; + + /* Check if this keypress was a part of a compose sequence. */ + sym = compose_symbols (sym); + + return sym; +} + +void +xkb_input (keypress_t key) +{ + char buf[100]; + size_t size = 0; + wchar_t input; + + debug_printf ("input: %d, rel: %d, rep: %d\n", key.keycode, key.rel, key.repeat); + + if (key.rel) + keystate[key.keycode].lmods = lmods; + input = handle_key (key); + + debug_printf ("handle: %d\n", input); + if (input == -1) + return; + + /* If the realmodifier MOD1 (AKA Alt) is set generate an ESC + symbol. */ + if (emods.rmods & RMOD_MOD1) + buf[size++] = '\e'; + + buf[size] = '\0'; + + if (!input) + return; + + /* Special key, generate escape sequence. */ + char *escseq = NULL; + + switch (input) + { + case XK_Up: case XK_KP_Up: + escseq = CONS_KEY_UP; + break; + case XK_Down: case XK_KP_Down: + escseq = CONS_KEY_DOWN; + break; + case XK_Left: case XK_KP_Left: + escseq = CONS_KEY_LEFT; + break; + case XK_Right: case XK_KP_Right: + escseq = CONS_KEY_RIGHT; + break; + case XK_BackSpace: + escseq = CONS_KEY_BACKSPACE; + break; + case XK_F1: case XK_KP_F1: + escseq = CONS_KEY_F1; + break; + case XK_F2: case XK_KP_F2: + escseq = CONS_KEY_F2; + break; + case XK_F3: case XK_KP_F3: + escseq = CONS_KEY_F3; + break; + case XK_F4: case XK_KP_F4: + escseq = CONS_KEY_F4; + break; + case XK_F5: + escseq = CONS_KEY_F5; + break; + case XK_F6: + escseq = CONS_KEY_F6; + break; + case XK_F7: + escseq = CONS_KEY_F7; + break; + case XK_F8: + escseq = CONS_KEY_F8; + break; + case XK_F9: + escseq = CONS_KEY_F9; + break; + case XK_F10: + escseq = CONS_KEY_F10; + break; + case XK_F11: + escseq = CONS_KEY_F11; + break; + case XK_F12: + escseq = CONS_KEY_F12; + break; + case XK_F13: + escseq = CONS_KEY_F13; + break; + case XK_F14: + escseq = CONS_KEY_F14; + break; + case XK_F15: + escseq = CONS_KEY_F15; + break; + case XK_F16: + escseq = CONS_KEY_F16; + break; + case XK_F17: + escseq = CONS_KEY_F17; + break; + case XK_F18: + escseq = CONS_KEY_F18; + break; + case XK_F19: + escseq = CONS_KEY_F19; + break; + case XK_F20: + escseq = CONS_KEY_F20; + break; + case XK_Home: case XK_KP_Home: + escseq = CONS_KEY_HOME; + break; + case XK_Insert: case XK_KP_Insert: + escseq = CONS_KEY_IC; + break; + case XK_Delete: case XK_KP_Delete: + escseq = CONS_KEY_DC; + break; + case XK_End: case XK_KP_End: + escseq = CONS_KEY_END; + break; + case XK_Page_Up: case XK_KP_Page_Up: + escseq = CONS_KEY_PPAGE; + break; + case XK_Page_Down: case XK_KP_Page_Down: + escseq = CONS_KEY_NPAGE; + break; + case XK_KP_Begin: + escseq = CONS_KEY_B2; + break; + case XK_ISO_Left_Tab: + escseq = CONS_KEY_BTAB; + break; + case XK_Return: case XK_KP_Enter: + escseq = "\x0d"; + break; + case XK_Tab: case XK_KP_Tab: + escseq = "\t"; + break; + case XK_Escape: + escseq = "\e"; + break; + } + + if (escseq != NULL) + { + strcat (buf + size, escseq); + size += strlen (escseq); + } + else + { + char *buffer = &buf[size]; + size_t left = sizeof (buf) - size; + char *inbuf = (char *) &input; + size_t inbufsize = sizeof (wchar_t); + size_t nr; + + /* Control key behaviour. */ + if (bmods.rmods & RMOD_CTRL) + input = symtoctrlsym (input); + + /* Convert the Keysym to a UCS4 characted. */ + input = KeySymToUcs4 (input); + /* if (!input) */ + /* continue; */ + + debug_printf ("UCS4: %d -- %c\n", (int) input, input); + + /* If CAPSLOCK is active capitalize the symbol. */ + if (emods.rmods & 2) + input = towupper (input); + + 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; + } + + if (size) + console_input (buf, size); + size = 0; +} + +error_t parse_xkbconfig (char *xkbdir, char *xkbkeymapfile, char *xkbkeymap); + +error_t +xkb_load_layout (char *xkbdir, char *xkbkeymapfile, char *xkbkeymap) +{ + error_t err; + + err = parse_xkbconfig (xkbdir, xkbkeymapfile, xkbkeymap); + if (err) + return err; + + determine_keytypes (); + interpret_all (); + return 0; +} diff --git a/console-client/xkb/xkb.h b/console-client/xkb/xkb.h new file mode 100644 index 00000000..07694930 --- /dev/null +++ b/console-client/xkb/xkb.h @@ -0,0 +1,431 @@ +/* Keyboard plugin for the Hurd console using XKB keymaps. + + Copyright (C) 2002,03 Marco Gerards + + Written by Marco Gerards <marco@student.han.nl> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. */ + +#include <errno.h> +#include <argp.h> +#include <X11/Xlib.h> + +typedef int keycode_t; +typedef unsigned int scancode_t; +typedef int symbol; +typedef int group_t; +typedef unsigned int boolctrls; + +#define KEYCONSUMED 1 +#define KEYNOTCONSUMED 0 + +#define RedirectIntoRange 1 +#define ClampIntoRange 2 +#define WrapIntoRange 0 + +typedef enum mergemode + { + augment, + override, + replace, + alternate, + defaultmm + } mergemode; + +extern mergemode merge_mode; + +typedef unsigned long KeySym; + +/* Real modifiers. */ +#define RMOD_SHIFT 1 << 0 +#define RMOD_LOCK 1 << 1 +#define RMOD_CTRL 1 << 2 +#define RMOD_MOD1 1 << 3 +#define RMOD_MOD2 1 << 4 +#define RMOD_MOD3 1 << 5 +#define RMOD_MOD4 1 << 6 +#define RMOD_MOD5 1 << 7 + +/* If set the key has action(s). */ +#define KEYHASACTION (1<<4) +/* Normally the keytype will be calculated, but some keys like SYSRQ + can't be calculated. For these keys the name for the keytypes will + be given for every group fro whch the bit is set. */ +#define KEYHASTYPES 0xf +#define KEYHASBEHAVIOUR (1<<5) +/* Will the key be repeated when held down, or not. */ +#define KEYREPEAT (1<<6) +#define KEYNOREPEAT (1<<7) + +/* The complete set of modifiers. */ +typedef struct modmap +{ + /* Real modifiers. */ + int rmods; + /* Virtual modifiers. */ + int vmods; +} modmap_t; + +/* Modifier counter. */ +typedef struct modcount +{ + int rmods[8]; + int vmods[16]; +} modcount_t; + +/* Map modifiers to a shift level. */ +typedef struct typemap +{ + /* Shift level used when required modifiers match the active modifiers. */ + int level; + modmap_t mods; + modmap_t preserve; + struct typemap *next; +} typemap_t; + +/* The keypad symbol range. */ +#define KEYPAD_FIRST_KEY 0xFF80 +#define KEYPAD_LAST_KEY 0xFFB9 +#define KEYPAD_MASK 0xFF80 + +/* Convert a keypad symbol to a ASCII symbol. */ +#define keypad_to_ascii(c) c = (c & (~KEYPAD_MASK)) + +/* The default keytypes. These can be calculated. */ +#define KT_ONE_LEVEL 0 +#define KT_TWO_LEVEL 1 +#define KT_ALPHA 2 +#define KT_KEYPAD 3 + +typedef struct keytype +{ + /* Mask that determines which modifiers should be checked. */ + modmap_t modmask; + /* Amount of levels. */ + int levels; + /* The required set of modifiers for one specific level. */ + struct typemap *maps; + + char *name; + struct keytype *hnext; + struct keytype **prevp; +} keytype_t; + +extern struct keytype *keytypes; +extern int keytype_count; + +/* All Actions as described in the protocol specification. */ +typedef enum actiontype + { + SA_NoAction, + SA_SetMods, + SA_LatchMods, + SA_LockMods, + SA_SetGroup, + SA_LatchGroup, + SA_LockGroup, + SA_MovePtr, + SA_PtrBtn, + SA_LockPtrBtn, + SA_SetPtrDflt, + SA_ISOLock, + SA_TerminateServer, + SA_SwitchScreen, + SA_SetControls, + SA_LockControls, + SA_ActionMessage, + SA_RedirectKey, + SA_DeviceBtn, + SA_LockDeviceBtn, + SA_DeviceValuator, + SA_ConsScroll + } actiontype_t; + +typedef struct xkb_action +{ + actiontype_t type; + int data[15]; +} xkb_action_t; + +#define useModMap 4 +#define clearLocks 1 +#define latchToLock 2 +#define noLock 1 +#define noUnlock 2 +#define groupAbsolute 4 +#define NoAcceleration 1 +#define MoveAbsoluteX 2 +#define MoveAbsoluteY 4 +/* XXX: This flag is documentated and I've implemented it. Weird + stuff. */ +#define useDfltBtn 0 +#define DfltBtnAbsolute 2 +#define AffectDfltBtn 1 +#define switchApp 1 +#define screenAbs 4 +#define lineAbs 2 +#define usePercentage 8 + +/* Defines how symbols and rmods are interpreted. This is used to + bound an action to a key that doesn't have an action bound to it, + only a modifier or action describing symbol. */ +typedef struct xkb_interpret +{ + symbol symbol; + int rmods; + int match; + int vmod; /* XXX: Why does this field have a size of only 8 bits? */ + int flags; + struct xkb_action action; + struct xkb_interpret *next; +} xkb_interpret_t; + +extern xkb_interpret_t *interpretations; +extern int interpret_count; + +/* These are the parameter names that are used by the actions that + control modifiers. (this is stored in the data field of + xkb_action)*/ +typedef struct action_setmods +{ + actiontype_t type; + /* The flags configure the behaviour of the action. */ + int flags; + /* XXX: The real modifiers that can be controlled by this action. */ + int modmask; + /* The modifiers that are will be set/unset by this action. */ + modmap_t modmap; +} action_setmods_t; + +typedef struct action_setgroup +{ + actiontype_t type; + int flags; + int group; +} action_setgroup_t; + +typedef struct action_moveptr +{ + actiontype_t type; + int flags; + int x; + int y; +} action_moveptr_t; + +typedef struct action_ptrbtn +{ + actiontype_t type; + int flags; + int count; /* Isn't used for LockPtrBtn. */ + int button; +} action_ptrbtn_t; + +typedef struct action_ptr_dflt +{ + actiontype_t type; + int flags; + int affect; + int value; +} action_ptr_dflt_t; + +typedef struct action_switchscrn +{ + actiontype_t type; + int flags; + int screen; +} action_switchscrn_t; + +typedef struct action_consscroll +{ + actiontype_t type; + int flags; + double screen; + int line; + int percent; +} action_consscroll_t; + +typedef struct action_redirkey +{ + actiontype_t type; + int newkey; + int rmodsmask; + int rmods; + int vmodsmask; + int vmods; +} action_redirkey_t; + +typedef struct action_setcontrols +{ + actiontype_t type; + int controls; +} action_setcontrols_t; + +/* Every key can have 4 groups, this is the information stored per + group. */ +struct keygroup +{ + /* All symbols for every available shift level and group. */ + symbol *symbols; + /* All actions for every available shift level and group. */ + struct xkb_action **actions; + /* The keytype of this key. The keytype determines the available + shift levels and which modiers are used to set the shift level. + */ + struct keytype *keytype; + /* Amount of symbols held in this group of this key. */ + int width; + int actionwidth; +}; + +/* A single key scancode stored in memory. */ +typedef struct key +{ + /* The flags that can be set for this key (To change the behaviour + of this key). */ + int flags; + /* Every key has a maximum of 4 groups. (XXX: According to Ivan + Pascal's documentation... I'm not really sure if that is true.) */ + struct keygroup groups[4]; + int numgroups; + struct modmap mods; +} keyinf_t; + +extern struct key *keys; +extern int min_keys; +extern int max_keys; + +/* The current state of every key. */ +typedef struct keystate +{ + /* Key is pressed. */ + unsigned short keypressed:1; + unsigned short prevstate:1; + /* The key was disabled for bouncekeys. */ + unsigned short disabled:1; + /* Information about locked modifiers at the time of the keypress, + this information is required for unlocking when the key is released. */ + modmap_t lmods; + /* The modifiers and group that were active at keypress, make them + active again on keyrelease so the action will be undone. */ + modmap_t prevmods; + boolctrls bool; + group_t prevgroup; + group_t oldgroup; +} keystate_t; + +extern struct keystate keystate[255]; + +typedef struct keypress +{ + keycode_t keycode; + keycode_t prevkc; + unsigned short repeat:1; /* It this a real keypress?. */ + unsigned short redir:1; /* This is not a real keypress. */ + unsigned short rel; /* Key release. */ +} keypress_t; + +/* Flags for indicators. */ +#define IM_NoExplicit 0x80 +#define IM_NoAutomatic 0x40 +#define IM_LEDDrivesKB 0x20 + +#define IM_UseCompat 0x10 +#define IM_UseEffective 0x08 +#define IM_UseLocked 0x04 +#define IM_UseLatched 0x02 +#define IM_UseBase 0x01 + + +typedef struct xkb_indicator +{ + int flags; + int which_mods; + modmap_t modmap; + int which_groups; + int groups; + unsigned int ctrls; +} xkb_indicator_t; + +unsigned int KeySymToUcs4(int keysym); +symbol compose_symbols (symbol symbol); +error_t read_composefile (char *); +struct keytype *keytype_find (char *name); + +void key_set_action (struct key *key, group_t group, int level, + xkb_action_t *action); + + +/* Interfaces for xkbdata.c: */ +extern struct xkb_interpret default_interpretation; + + +/* Assign the name KEYNAME to the keycode KEYCODE. */ +error_t keyname_add (char *keyname, int keycode); + +/* Find the numberic representation of the keycode with the name + KEYNAME. */ +int keyname_find (char *keyname); + +/* Search the keytype with the name NAME. */ +struct keytype *keytype_find (char *name); + +/* Remove the keytype KT. */ +void keytype_delete (struct keytype *kt); + +/* Create a new keytype with the name NAME. */ +error_t keytype_new (char *name, struct keytype **new_kt); + +/* Add a level (LEVEL) to modifiers (MODS) mapping to the current + keytype. */ +error_t keytype_mapadd (struct keytype *kt, modmap_t mods, int level); + +/* For the current keytype the modifiers PRESERVE should be preserved + when the modifiers MODS are pressed. */ +error_t keytype_preserve_add (struct keytype *kt, modmap_t mods, + modmap_t preserve); + +/* Add a new interpretation. */ +error_t interpret_new (xkb_interpret_t **new_interpret, symbol ks); + +/* Get the number assigned to the virtualmodifier with the name + VMODNAME. */ +int vmod_find (char *vmodname); + +/* Give the virtualmodifier VMODNAME a number and add it to the + hashtable. */ +error_t vmod_add (char *vmodname); + +/* Initialize the list for keysyms to realmodifiers mappings. */ +void ksrm_init (); + +/* Add keysym to realmodifier mapping. */ +error_t ksrm_add (symbol ks, int rmod); + +/* Apply the rkms (realmods to keysyms) table to all keysyms. */ +void ksrm_apply (void); + +/* Set the current rmod for the key with keyname KEYNAME. */ +/* XXX: It shouldn't be applied immediatly because the key can be + replaced. */ +void set_rmod_keycode (char *keyname, int rmod); + +/* Initialize XKB data structures. */ +error_t xkb_data_init (void); + +error_t xkb_input_key (int key); + +error_t xkb_init_repeat (int delay, int repeat); + +void xkb_input (keypress_t key); + +int debug_printf (const char *f, ...); + +error_t xkb_load_layout (char *xkbdir, char *xkbkeymapfile, char *xkbkeymap); diff --git a/console-client/xkb/xkbdata.c b/console-client/xkb/xkbdata.c new file mode 100644 index 00000000..767bf38a --- /dev/null +++ b/console-client/xkb/xkbdata.c @@ -0,0 +1,464 @@ +/* xkbdata.c -- Manage XKB datastructures. + + Copyright (C) 2003 Marco Gerards + + Written by Marco Gerards <marco@student.han.nl> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. */ + +/* Generate a key for the string S. XXX: The are many more effecient + algoritms, this one should be replaced by one of those. */ + +#include <stdlib.h> +#include <string.h> +#include <hurd/ihash.h> +#include "xkb.h" + +static int +name_hash (char *s) +{ + int i = 0; + while (*s) + i += *(s++); + return i; +} + + +/* A keyname with a keycode and realmodifier bound to it. */ +struct keyname +{ + int keycode; + int rmods; +}; + +static struct hurd_ihash kn_mapping; + +/* Initialize the keyname hashtable. */ +static void +keyname_init () +{ + hurd_ihash_init (&kn_mapping, HURD_IHASH_NO_LOCP); + debug_printf ("Kn_mapping init"); + /* XXX: error. */ +} + +static inline int +keyname_hash(char *keyname) +{ + char tmp[4] = { 0 }; + strncpy(tmp, keyname, sizeof tmp); + return tmp[0] + (tmp[1] << 8) + (tmp[2] << 16) + (tmp[3] << 24); +} + +/* Assign the name KEYNAME to the keycode KEYCODE. */ +error_t +keyname_add (char *keyname, int keycode) +{ + struct keyname *kn; + int kn_int; + + kn = malloc (sizeof (struct keyname)); + if (!kn) + return ENOMEM; + + /* XXX: 4 characters can be mapped into a int, it is safe to assume + this will not be changed. */ + if (strlen (keyname) > 4) + { + debug_printf ("The keyname `%s' consist of more than 4 characters;" + " 4 characters is the maximum.\n", keyname); + /* XXX: Abort? */ + return 0; + } + + kn->keycode = keycode; + kn->rmods = 0; + + kn_int = keyname_hash(keyname); + debug_printf ("add key %s(%d) hash: %d\n", keyname, keycode, kn_int); + hurd_ihash_add (&kn_mapping, kn_int, kn); + + return 0; +} + +/* Find the numberic representation of the keycode with the name + KEYNAME. */ +int +keyname_find (char *keyname) +{ + struct keyname *kn; + int kn_int; + + /* XXX: 4 characters can be mapped into a int, it is safe to assume + this will not be changed. */ + if (strlen (keyname) > 4) + { + debug_printf ("The keyname `%s' consist of more than 4 characters;" + " 4 characters is the maximum.\n", keyname); + /* XXX: Abort? */ + return 0; + } + kn_int = keyname_hash(keyname); + + kn = hurd_ihash_find (&kn_mapping, kn_int); + if (kn) + return kn->keycode; +/* int h = name_hash (keyname); */ +/* struct keyname *kn; */ +/* for (kn = knhash[KNHASH(h)]; kn; kn = kn->hnext) */ +/* { */ +/* if (strcmp (kn->keyname, keyname)) */ +/* continue; */ + +/* return kn->keycode; */ +/* } */ + + /* XXX: Is 0 an invalid keycode? */ + return 0; +} + + +/* Keytypes and keytype maps. */ + +/* The dummy gets used when the original may not be overwritten. */ +static struct keytype dummy_keytype; + +#define KTHSZ 16 +#if ((KTHSZ&(KTHSZ-1)) == 0) +#define KTHASH(ktttl) ((ktttl)&(KTHSZ-1)) +#else +#define KTHASH(ktttl) (((unsigned)(kt))%KTHSZ) +#endif + +/* All keytypes. */ +struct keytype *kthash[KTHSZ]; + +/* Initialize the keytypes hashtable. */ +static void +keytype_init () +{ + int n; + for (n = 0; n < KTHSZ; n++) + kthash[n] = 0; +} + +/* Search the keytype with the name NAME. */ +struct keytype * +keytype_find (char *name) +{ + int nhash = name_hash (name); + struct keytype *kt; + + for (kt = kthash[KTHASH(nhash)]; kt; kt = kt->hnext) + if (!strcmp (name, kt->name)) + return kt; + return NULL; +} + +/* Remove the keytype KT. */ +void +keytype_delete (struct keytype *kt) +{ + struct typemap *map; + + + *kt->prevp = kt->hnext; + if (kt->hnext) + kt->hnext->prevp = kt->prevp; + + map = kt->maps; + while (map) + { + struct typemap *nextmap = map->next; + free (map); + map = nextmap; + } + +} + +/* Create a new keytype with the name NAME. */ +error_t +keytype_new (char *name, struct keytype **new_kt) +{ + struct keytype *kt; + struct keytype *ktlist; + int nhash; + + nhash = name_hash (name); + debug_printf ("New: %s\n", name); + + kt = keytype_find (name); + + if (kt) + { + /* If the merge mode is augement don't replace it. */ + if (merge_mode == augment) + { + *new_kt = &dummy_keytype; + return 0; + } + else /* This keytype should replace the old one, remove the old one. */ + keytype_delete (kt); + } + + ktlist = kthash[KTHASH(nhash)]; + kt = calloc (1, sizeof (struct keytype)); + if (kt == NULL) + return ENOMEM; + + kt->hnext = ktlist; + kt->name = strdup (name); + kt->prevp = &kthash[KTHASH(nhash)]; + kt->maps = NULL; + if (kthash[KTHASH(nhash)]) + kthash[KTHASH(nhash)]->prevp = &(kt->hnext); + kthash[KTHASH(nhash)] = kt; + + *new_kt = kt; + return 0; +} + +/* Add a level (LEVEL) to modifiers (MODS) mapping to the current + keytype. */ +error_t +keytype_mapadd (struct keytype *kt, modmap_t mods, int level) +{ + struct typemap *map; + modmap_t nulmap = {0, 0}; + + map = malloc (sizeof (struct typemap)); + if (!map) + return ENOMEM; + + map->level = level; + map->mods = mods; + map->preserve = nulmap; + /* By default modifiers shouldn't be preserved. */ + map->next = kt->maps; + + kt->maps = map; + + return 0; +} + +/* For the current keytype the modifiers PRESERVE should be preserved + when the modifiers MODS are pressed. */ +error_t +keytype_preserve_add (struct keytype *kt, modmap_t mods, modmap_t preserve) +{ + error_t err; + struct typemap *map; + + map = kt->maps; + while (map) + { + if (mods.rmods == map->mods.rmods && mods.vmods == map->mods.vmods) + { + map->preserve = preserve; + return 0; + } + map = map->next; + } + + /* No map has been found, add the default map. */ + err = keytype_mapadd (kt, mods, 0); + if (err) + return err; + + keytype_preserve_add (kt, mods, preserve); + + return 0; +} + + +/* Interpretations. */ + +struct xkb_interpret *last_interp; +struct xkb_interpret default_interpretation; + + +/* Add a new interpretation. */ +error_t +interpret_new (xkb_interpret_t **new_interpret, symbol ks) +{ + struct xkb_interpret *new_interp; + + new_interp = malloc (sizeof (struct xkb_interpret)); + if (!new_interp) + return ENOMEM; + + memcpy (new_interp, &default_interpretation, sizeof (struct xkb_interpret)); + new_interp->symbol = ks; + + if (ks) + { + new_interp->next = interpretations; + interpretations = new_interp; + + if (!last_interp) + last_interp = new_interp; + } + else + { + if (last_interp) + last_interp->next = new_interp; + + last_interp = new_interp; + + if (!interpretations) + interpretations = new_interp; + } + + *new_interpret = new_interp; + + return 0; +} + + +/* XXX: Dead code!? */ +/* Virtual modifiers name to number mapping. */ +/* Last number assigned to a virtual modifier. */ +static int lastvmod = 0; + +/* One virtual modifiername -> vmod number mapping. */ +struct vmodname +{ + char *name; + struct vmodname *next; +}; + +/* A list of virtualmodifier names and its numberic representation. */ +static struct vmodname *vmodnamel; + +/* Get the number assigned to the virtualmodifier with the name + VMODNAME. */ +int +vmod_find (char *vmodname) +{ + int i = 0; + struct vmodname *vmn = vmodnamel; + + while (vmn) + { + if (!strcmp (vmn->name, vmodname)) + return (lastvmod - i); + vmn = vmn->next; + i++; + } + + return 0; +} + +/* Give the virtualmodifier VMODNAME a number and add it to the + hashtable. */ +error_t +vmod_add (char *vmodname) +{ + struct vmodname *vmn; + + if (vmod_find (vmodname)) + return 0; + + vmn = malloc (sizeof (struct vmodname)); + if (vmn == NULL) + return ENOMEM; + + vmn->name = vmodname; + vmn->next = vmodnamel; + vmodnamel = vmn; + + lastvmod++; + if (lastvmod > 16) + debug_printf("warning: only sixteen virtual modifiers are supported, %s will not be functional.\n", vmodname); + + return 0; +} + + +/* XXX: Use this, no pointers. */ +struct ksrm +{ + symbol ks; + + int rmods; +}; +static struct hurd_ihash ksrm_mapping; + +/* Initialize the list for keysyms to realmodifiers mappings. */ +void +ksrm_init () +{ + hurd_ihash_init (&ksrm_mapping, HURD_IHASH_NO_LOCP); + debug_printf ("KSRM MAP IHASH CREATED \n"); +} + +/* Add keysym to realmodifier mapping. */ +error_t +ksrm_add (symbol ks, int rmod) +{ + hurd_ihash_add (&ksrm_mapping, ks, (void *) rmod); + + return 0; +} + +/* Apply the rkms (realmods to keysyms) table to all keysyms. */ +void +ksrm_apply (void) +{ + keycode_t kc; + for (kc = 0; kc < max_keys; kc++) + { + int group; + for (group = 0; group < 4; group++) + { + int cursym; + for (cursym = 0; cursym < keys[kc].groups[group].width; cursym++) + { + symbol ks = keys[kc].groups[group].symbols[cursym]; + int rmods = (int) hurd_ihash_find (&ksrm_mapping, ks); + + if (rmods) + { + keys[kc].mods.rmods = rmods; + } + } + } + } +} + + +/* void */ +/* indicator_new (xkb_indicator_t **, */ + + +/* Keycode to realmodifier mapping. */ + +/* Set the current rmod for the key with keyname KEYNAME. */ +/* XXX: It shouldn't be applied immediatly because the key can be + replaced. */ +void +set_rmod_keycode (char *keyname, int rmod) +{ + keycode_t kc = keyname_find (keyname); + keys[kc].mods.rmods = rmod; + debug_printf ("%s (kc %d) rmod: %d\n", keyname, kc, rmod); +} + +/* Initialize XKB data structures. */ +error_t +xkb_data_init (void) +{ + keyname_init (); + keytype_init (); + ksrm_init (); + + return 0; +} diff --git a/console-client/xkb/xkbtimer.c b/console-client/xkb/xkbtimer.c new file mode 100644 index 00000000..7621af72 --- /dev/null +++ b/console-client/xkb/xkbtimer.c @@ -0,0 +1,231 @@ +/* xkbtimer.c -- Manage XKB timers. + + Copyright (C) 2003, 2004 Marco Gerards + + Written by Marco Gerards <marco@student.han.nl> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. */ + +/* XKB requires a timer for key repeat and accessibility. Carefully + stack all compatibility features. */ + +#include <mach.h> +#include <errno.h> +#include <assert.h> + +#include "xkb.h" +#include <timer.h> + +/* For key repeat. */ +static int key_delay = 0; +static int key_repeat = 0; + +/* SlowKeys. */ +static int slowkeys_delay = 50; +static int slowkeys_active = 0; + +/* BounceKeys. */ +static int bouncekeys_delay = 100; +static int bouncekeys_active = 0; + +/* The current status of the timer. */ +enum timer_status + { + timer_stopped, + timer_slowkeys, + timer_repeat_delay, + timer_repeating + }; + +/* Timer used to time key controls. */ +static struct per_key_timer +{ + /* Used for slowkeys and repeat. */ + struct timer_list enable_timer; + + /* Status of the enable timer. */ + enum timer_status enable_status; + + /* Used for bouncekeys. */ + struct timer_list disable_timer; +} per_key_timers[255]; + +/* The last pressed key. Only this key may generate keyrepeat events. */ +static int lastkey = 0; + +error_t +xkb_handle_key (keycode_t kc) +{ + static keycode_t prevkc = 0; + keypress_t key; + + key.keycode = kc & 127; + key.prevkc = prevkc; + key.repeat = (prevkc == kc); + key.redir = 0; + key.rel = kc & 128; + keystate[key.keycode & 127].keypressed = key.rel ? 0 : 1; + debug_printf ("PRESSED: %d\n", !(key.rel)); + xkb_input (key); + prevkc = key.keycode; + return 0; +} + +error_t +xkb_init_repeat (int delay, int repeat) +{ + error_t err; + err = timer_init (); + if (err) + return err; + + key_delay = delay; + key_repeat = repeat; + + return 0; +} + +static int +key_enable (void *handle) +{ + int key = (int) handle; + + /* Enable the key. */ + keystate[key].disabled = 0; + + return 0; +} + +/* Called by key timer. The global variable timer_status determines + the current control. */ +static int +key_timing (void *handle) +{ + int current_key = (int) handle; + + xkb_handle_key (current_key); + + /* Another key was pressed after this key, stop repeating. */ + if (lastkey != current_key) + { + per_key_timers[current_key].enable_status = timer_stopped; + return 0; + } + + switch (per_key_timers[current_key].enable_status) + { + case timer_stopped: + assert ("Stopped timer triggered timer event\n"); + break; + case timer_slowkeys: + per_key_timers[current_key].enable_timer.expires + = fetch_jiffies () + key_delay; + lastkey = current_key; + + if (keys[current_key].flags & KEYNOREPEAT) + { + per_key_timers[current_key].enable_status = timer_stopped; + /* Stop the timer. */ + return 0; + } + else + { + per_key_timers[current_key].enable_status = timer_repeat_delay; + } + break; + case timer_repeat_delay: + per_key_timers[current_key].enable_status = timer_repeating; + /* Fall through. */ + case timer_repeating: + per_key_timers[current_key].enable_timer.expires + = fetch_jiffies () + key_repeat; + break; + } + return 1; +} + +error_t +xkb_input_key (int key) +{ + int pressed = !(key & 128); + int keyc = key & 127; + + debug_printf ("KEYIN: %d\n", key); + + /* Filter out any double or disabled keys. */ + if (key == lastkey || keystate[keyc].disabled) + return 0; + + /* Always handle keyrelease events. */ + if (!pressed) + { + /* Stop the timer for this released key. */ + if (per_key_timers[keyc].enable_status != timer_stopped) + { + timer_remove (&per_key_timers[keyc].enable_timer); + per_key_timers[keyc].enable_status = timer_stopped; + } + + /* No more last key; it was released. */ + if (keyc == lastkey) + lastkey = 0; + + /* Make sure the key was pressed before releasing it, it might + not have been accepted. */ + if (keystate[key & 127].keypressed) + xkb_handle_key (key); + + /* If bouncekeys is active, disable the key. */ + if (bouncekeys_active) + { + keystate[keyc].disabled = 1; + + /* Setup a timer to enable the key. */ + timer_clear (&per_key_timers[keyc].disable_timer); + per_key_timers[keyc].disable_timer.fnc = key_enable; + per_key_timers[keyc].disable_timer.fnc_data = (void *) keyc; + per_key_timers[keyc].disable_timer.expires + = fetch_jiffies () + bouncekeys_delay; + timer_add (&per_key_timers[keyc].disable_timer); + } + + return 0; + } + + /* Setup the timer for slowkeys. */ + timer_clear (&per_key_timers[keyc].enable_timer); + lastkey = keyc; + per_key_timers[keyc].enable_timer.fnc = key_timing; + per_key_timers[keyc].enable_timer.fnc_data = (void *) keyc; + + if (slowkeys_active) + { + per_key_timers[keyc].enable_status = timer_slowkeys; + per_key_timers[keyc].enable_timer.expires + = fetch_jiffies () + slowkeys_delay; + } + else + { + /* Immediatly report the keypress. */ + xkb_handle_key (keyc); + + /* Check if this repeat is allowed for this keycode. */ + if (keys[keyc].flags & KEYNOREPEAT) + return 0; /* Nope. */ + + per_key_timers[keyc].enable_status = timer_repeat_delay; + per_key_timers[keyc].enable_timer.expires + = fetch_jiffies () + key_delay; + } + timer_add (&per_key_timers[keyc].enable_timer); + + return 0; +} |