diff --git a/src/cfgfile.c b/src/cfgfile.c index 04abb9e..00038e8 100644 --- a/src/cfgfile.c +++ b/src/cfgfile.c @@ -31,6 +31,10 @@ along with this program. If not, see . struct cfg cfg, prev_cfg; +#ifdef USE_X11 +unsigned long kbemu_keysym(const char *str); +#endif + /* all parsable config options... some of them might map to the same cfg field */ enum { CFG_REPEAT, @@ -57,6 +61,7 @@ enum { RMCFG_ALL, RMCFG_OWN }; static int parse_bnact(const char *s); static const char *bnact_name(int bnact); +static int parse_kbmap(const char *str, unsigned long *kbmap, int max_keys); static int add_cfgopt(int opt, int idx, const char *fmt, ...); static int add_cfgopt_devid(int vid, int pid); static int rm_cfgopt(const char *name, int mode); @@ -76,7 +81,7 @@ static int num_lines; void default_cfg(struct cfg *cfg) { - int i; + int i, j; memset(cfg, 0, sizeof *cfg); @@ -99,7 +104,10 @@ void default_cfg(struct cfg *cfg) for(i=0; imap_button[i] = i; cfg->kbmap_str[i] = 0; - cfg->kbmap[i] = 0; + cfg->kbmap_count[i] = 0; + for(j=0; jkbmap[i][j] = 0; + } } cfg->repeat_msec = -1; @@ -399,6 +407,7 @@ int read_cfg(const char *fname, struct cfg *cfg) free(cfg->kbmap_str[bnidx]); } cfg->kbmap_str[bnidx] = strdup(val_str); + cfg->kbmap_count[bnidx] = parse_kbmap(val_str, cfg->kbmap[bnidx], MAX_KEYS_PER_BUTTON); } else if(strcmp(key_str, "led") == 0) { lptr->opt = CFG_LED; @@ -726,6 +735,51 @@ static const char *bnact_name(int bnact) return "none"; } +static int parse_kbmap(const char *str, unsigned long *kbmap, int max_keys) +{ +#ifdef USE_X11 + char buf[256], *ptr, *start; + int count = 0; + + if(!str || !*str) return 0; + + strncpy(buf, str, sizeof buf - 1); + buf[sizeof buf - 1] = 0; + + start = buf; + while(*start && count < max_keys) { + ptr = strchr(start, '+'); + if(ptr) *ptr = 0; + + while(*start && isspace(*start)) start++; + if(*start) { + char *end = start + strlen(start) - 1; + while(end > start && isspace(*end)) *end-- = 0; + } + + if(*start) { + unsigned long ksym = (unsigned long)kbemu_keysym(start); + if(ksym == 0) { + logmsg(LOG_WARNING, "invalid key name in keyboard mapping: \"%s\"\n", start); + } else { + kbmap[count++] = ksym; + } + } + + if(!ptr) break; + start = ptr + 1; + } + + if(count >= max_keys && *start) { + logmsg(LOG_WARNING, "keyboard mapping truncated, max %d keys supported\n", max_keys); + } + + return count; +#else + return 0; +#endif +} + static struct cfgline *find_cfgopt(int opt, int idx) { int i; diff --git a/src/cfgfile.h b/src/cfgfile.h index 049e6a9..ead825e 100644 --- a/src/cfgfile.h +++ b/src/cfgfile.h @@ -24,6 +24,7 @@ along with this program. If not, see . #define MAX_AXES 64 #define MAX_BUTTONS 64 #define MAX_CUSTOM 64 +#define MAX_KEYS_PER_BUTTON 8 enum { LED_OFF = 0, @@ -51,7 +52,8 @@ struct cfg { int map_axis[MAX_AXES]; int map_button[MAX_BUTTONS]; int bnact[MAX_BUTTONS]; - int kbmap[MAX_BUTTONS]; + unsigned long kbmap[MAX_BUTTONS][MAX_KEYS_PER_BUTTON]; + int kbmap_count[MAX_BUTTONS]; char *kbmap_str[MAX_BUTTONS]; int swapyz; int led, grab_device; diff --git a/src/event.c b/src/event.c index a129420..bd6f0e5 100644 --- a/src/event.c +++ b/src/event.c @@ -215,14 +215,9 @@ void process_input(struct device *dev, struct dev_input *inp) /* check to see if we must emulate a keyboard event instead of a * retular button event for this button */ - if(cfg.kbmap_str[inp->idx]) { - if(!cfg.kbmap[inp->idx]) { - cfg.kbmap[inp->idx] = kbemu_keysym(cfg.kbmap_str[inp->idx]); - if(verbose) { - logmsg(LOG_DEBUG, "mapping ``%s'' to keysym %d\n", cfg.kbmap_str[inp->idx], (int)cfg.kbmap[inp->idx]); - } - } - send_kbevent(cfg.kbmap[inp->idx], inp->val); + if(cfg.kbmap_count[inp->idx] > 0) { + KeySym *keys = (KeySym*)cfg.kbmap[inp->idx]; + send_kbevent_combo(keys, cfg.kbmap_count[inp->idx], inp->val); break; } #endif diff --git a/src/kbemu.c b/src/kbemu.c index 6b67c03..115f78c 100644 --- a/src/kbemu.c +++ b/src/kbemu.c @@ -63,12 +63,18 @@ void send_kbevent(KeySym key, int press) XEvent xevent; Window win; int rev_state; + KeyCode kc; if(!dpy) return; + if(!(kc = XKeysymToKeycode(dpy, key))) { + logmsg(LOG_WARNING, "failed to convert keysym %lu to keycode\n", key); + return; + } + #ifdef HAVE_XTEST_H if(use_xtest) { - XTestFakeKeyEvent(dpy, XKeysymToKeycode(dpy, key), press, 0); + XTestFakeKeyEvent(dpy, kc, press, 0); XFlush(dpy); return; } @@ -81,7 +87,7 @@ void send_kbevent(KeySym key, int press) xevent.xkey.root = DefaultRootWindow(dpy); xevent.xkey.window = win; xevent.xkey.subwindow = None; - xevent.xkey.keycode = XKeysymToKeycode(dpy, key); + xevent.xkey.keycode = kc; xevent.xkey.state = 0; xevent.xkey.time = CurrentTime; xevent.xkey.x = xevent.xkey.y = 1; @@ -90,6 +96,27 @@ void send_kbevent(KeySym key, int press) XSendEvent(dpy, win, True, press ? KeyPressMask : KeyReleaseMask, &xevent); XFlush(dpy); } + +void send_kbevent_combo(KeySym *keys, int count, int press) +{ + int i; + + if(!dpy || count <= 0) return; + + if(press) { + return; + } + + /* send press events for all keys */ + for(i=0; idata[1]; + /* TODO handle multi-key sequences in a backwards compatible way */ + cfg.kbmap[idx][0] = req->data[1]; + cfg.kbmap_count[idx] = req->data[1] > 0 ? 1 : 0; free(cfg.kbmap_str[idx]); cfg.kbmap_str[idx] = req->data[1] > 0 ? strdup(str) : 0; sendresp(c, req, 0); @@ -550,11 +552,9 @@ static int handle_request(struct client *c, struct reqresp *req) sendresp(c, req, -1); return 0; } - if(cfg.kbmap_str[idx]) { - if(!cfg.kbmap[idx]) { - cfg.kbmap[idx] = kbemu_keysym(cfg.kbmap_str[idx]); - } - req->data[1] = cfg.kbmap[idx]; + /* TODO handle multi-key sequences in a backwards compatible way */ + if(cfg.kbmap_count[idx] > 0) { + req->data[1] = cfg.kbmap[idx][0]; } else { req->data[1] = 0; }