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;
}