Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better support for multiple keyboard layouts. #275

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ VERSION := $(shell $(VERCMD) || cat VERSION)
CPPFLAGS += -D_POSIX_C_SOURCE=200112L -DVERSION=\"$(VERSION)\"
CFLAGS += -std=c99 -pedantic -Wall -Wextra
LDFLAGS ?=
LDLIBS = $(LDFLAGS) -lxcb -lxcb-keysyms
LDLIBS = $(LDFLAGS) -lxcb -lxcb-keysyms -lxcb-xkb -lxkbcommon -lxkbcommon-x11

PREFIX ?= /usr/local
BINPREFIX ?= $(PREFIX)/bin
Expand Down
8 changes: 5 additions & 3 deletions Sourcedeps
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
grab.o: grab.c grab.h helpers.h parse.h sxhkd.h types.h
keys.o: keys.c keys.h
OBJ += keys.o
grab.o: grab.c grab.h helpers.h parse.h sxhkd.h types.h keys.c keys.h
OBJ += grab.o
helpers.o: helpers.c helpers.h sxhkd.h types.h
OBJ += helpers.o
parse.o: parse.c helpers.h locales.h parse.h sxhkd.h types.h
parse.o: parse.c helpers.h locales.h parse.h sxhkd.h types.h keys.c keys.h
OBJ += parse.o
sxhkd.o: sxhkd.c grab.h helpers.h parse.h sxhkd.h types.h
OBJ += sxhkd.o
types.o: types.c grab.h helpers.h parse.h sxhkd.h types.h
types.o: types.c grab.h helpers.h parse.h sxhkd.h types.h keys.c keys.h
OBJ += types.o
15 changes: 10 additions & 5 deletions src/grab.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include <stdbool.h>
#include "parse.h"
#include "grab.h"
#include "keys.h"

void grab(void)
{
Expand All @@ -40,12 +41,16 @@ void grab_chord(chord_t *chord)
{
for (chord_t *c = chord; c != NULL; c = c->more) {
if (c->button == XCB_NONE) {
xcb_keycode_t *keycodes = keycodes_from_keysym(c->keysym);
if (keycodes != NULL)
for (xcb_keycode_t *kc = keycodes; *kc != XCB_NO_SYMBOL; kc++)
if (c->keysym == xcb_key_symbols_get_keysym(symbols, *kc, 0))
// TODO:
// - this code seems to compute from c->keysym all keysyms that
// correspond to the same keycode as itself
// - why do this?
struct keycode_array keycodes = keycodes_from_keysym(kb_keymap, c->keysym);
if (keycodes.data != NULL)
for (xkb_keycode_t const* kc = keycodes.data; kc != keycodes.data + keycodes.count; ++kc)
if (c->keysym == keycode_to_keysym(kb_keymap, *kc))
grab_key_button(*kc, c->button, c->modfield);
free(keycodes);
keycode_array_free(&keycodes);
} else {
grab_key_button(XCB_NONE, c->button, c->modfield);
}
Expand Down
95 changes: 95 additions & 0 deletions src/keys.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
#include "keys.h"
#include <stdlib.h>
#include <stddef.h>

struct keysym_array keycode_to_keysyms(struct xkb_keymap* keymap, xkb_keycode_t keycode)
{
xkb_keysym_t const* data = NULL;
int count = 0;
count = xkb_keymap_key_get_syms_by_level(keymap, keycode, 0, 0, &data);

if(count <= 0) {
return (struct keysym_array){ .data = NULL, .count = 0 };
}

return (struct keysym_array){ .data = data, .count = count };
}

xkb_keysym_t keycode_to_keysym (struct xkb_keymap* keymap, xkb_keycode_t keycode)
{
struct keysym_array keysyms = keycode_to_keysyms(keymap, keycode);

if(keysyms.data)
return *keysyms.data;
else
return XKB_KEY_NoSymbol;
}

void keycode_array_free(struct keycode_array* keycodes)
{
if(keycodes->data) {
free(keycodes->data);
keycodes->data = NULL;
keycodes->count = 0;
}
}

struct keycodes_from_keysym_iterator
{
xkb_keysym_t keysym;
xkb_keycode_t * data;
size_t count;
};

static void keycodes_from_keysym_counter(struct xkb_keymap* keymap, xkb_keycode_t keycode, void* data)
{
struct keycodes_from_keysym_iterator * state = data;
struct keysym_array keysyms = keycode_to_keysyms(keymap, keycode);

xkb_keysym_t const* first = keysyms.data;
xkb_keysym_t const* last = keysyms.data + keysyms.count;

for(; first != last; ++first) {
if(state->keysym == *first) {
state->count++;
break;
}
}
}

static void keycodes_from_keysym_collector(struct xkb_keymap* keymap, xkb_keycode_t keycode, void* data)
{
struct keycodes_from_keysym_iterator * state = data;
struct keysym_array keysyms = keycode_to_keysyms(keymap, keycode);

xkb_keysym_t const* first = keysyms.data;
xkb_keysym_t const* last = keysyms.data + keysyms.count;

for(; first != last; ++first) {
if(state->keysym == *first) {
state->data[state->count++] = keycode;
break;
}
}
}

struct keycode_array keycodes_from_keysym(struct xkb_keymap* keymap, xkb_keysym_t keysym)
{
struct keycodes_from_keysym_iterator counter = {
.keysym = keysym,
.data = NULL,
.count = 0
};

xkb_keymap_key_for_each(keymap, keycodes_from_keysym_counter, &counter);

struct keycodes_from_keysym_iterator collector = {
.keysym = keysym,
.data = malloc(counter.count * sizeof(xkb_keycode_t)),
.count = 0
};

xkb_keymap_key_for_each(keymap, keycodes_from_keysym_collector, &collector);

return (struct keycode_array){ .data = collector.data, .count = collector.count };
}
24 changes: 24 additions & 0 deletions src/keys.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#ifndef SXHKD_KEYS_H
#define SXHKD_KEYS_H

#include <xkbcommon/xkbcommon.h>

struct keysym_array {
xkb_keysym_t const* data;
size_t count;
};

struct keycode_array {
xkb_keycode_t * data;
size_t count;
};

void keycode_array_free(struct keycode_array*);

struct keysym_array keycode_to_keysyms(struct xkb_keymap*, xkb_keycode_t);
xkb_keysym_t keycode_to_keysym (struct xkb_keymap*, xkb_keycode_t);

struct keycode_array keycodes_from_keysym(struct xkb_keymap*, xkb_keysym_t);

#endif

48 changes: 8 additions & 40 deletions src/parse.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include <ctype.h>
#include "locales.h"
#include "parse.h"
#include "keys.h"

xcb_keysym_t Alt_L, Alt_R, Super_L, Super_R, Hyper_L, Hyper_R,
Meta_L, Meta_R, Mode_switch, Num_Lock, Scroll_Lock;
Expand Down Expand Up @@ -2421,13 +2422,13 @@ void parse_event(xcb_generic_event_t *evt, uint8_t event_type, xcb_keysym_t *key
xcb_key_press_event_t *e = (xcb_key_press_event_t *) evt;
xcb_keycode_t keycode = e->detail;
*modfield = e->state;
*keysym = xcb_key_symbols_get_keysym(symbols, keycode, 0);
*keysym = keycode_to_keysym(kb_keymap, keycode);
PRINTF("key press %u %u\n", keycode, *modfield);
} else if (event_type == XCB_KEY_RELEASE) {
xcb_key_release_event_t *e = (xcb_key_release_event_t *) evt;
xcb_keycode_t keycode = e->detail;
*modfield = e->state & ~modfield_from_keycode(keycode);
*keysym = xcb_key_symbols_get_keysym(symbols, keycode, 0);
*keysym = keycode_to_keysym(kb_keymap, keycode);
PRINTF("key release %u %u\n", keycode, *modfield);
} else if (event_type == XCB_BUTTON_PRESS) {
xcb_button_press_event_t *e = (xcb_button_press_event_t *) evt;
Expand Down Expand Up @@ -2814,12 +2815,12 @@ void get_lock_fields(void)
int16_t modfield_from_keysym(xcb_keysym_t keysym)
{
uint16_t modfield = 0;
xcb_keycode_t *keycodes = NULL;
if ((keycodes = keycodes_from_keysym(keysym)) != NULL) {
for (xcb_keycode_t *k = keycodes; *k != XCB_NO_SYMBOL; k++)
modfield |= modfield_from_keycode(*k);
struct keycode_array keycodes = keycodes_from_keysym(kb_keymap, keysym);
if (keycodes.data != NULL) {
for (xkb_keycode_t const* kc = keycodes.data; kc != keycodes.data + keycodes.count; ++kc)
modfield |= modfield_from_keycode(*kc);
}
free(keycodes);
keycode_array_free(&keycodes);
return modfield;
}

Expand All @@ -2846,36 +2847,3 @@ int16_t modfield_from_keycode(xcb_keycode_t keycode)
free(reply);
return modfield;
}

xcb_keycode_t *keycodes_from_keysym(xcb_keysym_t keysym)
{
xcb_setup_t const *setup;
unsigned int num = 0;
xcb_keycode_t *result = NULL, *result_np = NULL;

if ((setup = xcb_get_setup(dpy)) != NULL) {
xcb_keycode_t min_kc = setup->min_keycode;
xcb_keycode_t max_kc = setup->max_keycode;

/* We must choose a type for kc other than xcb_keycode_t whose size
* is 1, otherwise, since max_kc will most likely be 255, if kc == 255,
* kc++ would be 0 and the outer loop would start over ad infinitum */
for(unsigned int kc = min_kc; kc <= max_kc; kc++)
for(unsigned int col = 0; col < KEYSYMS_PER_KEYCODE; col++) {
xcb_keysym_t ks = xcb_key_symbols_get_keysym(symbols, kc, col);
if (ks == keysym) {
num++;
result_np = realloc(result, sizeof(xcb_keycode_t) * (num + 1));
if (result_np == NULL) {
free(result);
return NULL;
}
result = result_np;
result[num - 1] = kc;
result[num] = XCB_NO_SYMBOL;
break;
}
}
}
return result;
}
1 change: 0 additions & 1 deletion src/parse.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,5 @@ void get_standard_keysyms(void);
void get_lock_fields(void);
int16_t modfield_from_keysym(xcb_keysym_t keysym);
int16_t modfield_from_keycode(xcb_keycode_t keycode);
xcb_keycode_t *keycodes_from_keysym(xcb_keysym_t keysym);

#endif
39 changes: 35 additions & 4 deletions src/sxhkd.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@

xcb_connection_t *dpy;
xcb_window_t root;
xcb_key_symbols_t *symbols;
int32_t kb_device = -1;
struct xkb_context* kb_context = NULL;
struct xkb_keymap* kb_keymap = NULL;

char *shell;
char config_file[MAXLEN];
Expand Down Expand Up @@ -217,7 +219,8 @@ int main(int argc, char *argv[])
ungrab();
cleanup();
destroy_chord(abort_chord);
xcb_key_symbols_free(symbols);
xkb_keymap_unref(kb_keymap);
xkb_context_unref(kb_context);
xcb_disconnect(dpy);
return EXIT_SUCCESS;
}
Expand Down Expand Up @@ -257,6 +260,23 @@ void key_button_event(xcb_generic_event_t *evt, uint8_t event_type)
xcb_flush(dpy);
}

static bool refresh_keymap(void)
{
struct xkb_keymap* keymap
= xkb_x11_keymap_new_from_device( kb_context
, dpy
, kb_device
, XKB_KEYMAP_COMPILE_NO_FLAGS);

if(keymap == NULL)
return false;

xkb_keymap_unref(kb_keymap);
kb_keymap = keymap;

return true;
}

void mapping_notify(xcb_generic_event_t *evt)
{
if (!mapping_count)
Expand All @@ -265,7 +285,8 @@ void mapping_notify(xcb_generic_event_t *evt)
PRINTF("mapping notify %u %u\n", e->request, e->count);
if (e->request == XCB_MAPPING_POINTER)
return;
if (xcb_refresh_keyboard_mapping(symbols, e) == 1) {

if (refresh_keymap()) {
destroy_chord(abort_chord);
get_lock_fields();
reload_cmd();
Expand Down Expand Up @@ -294,7 +315,17 @@ void setup(void)
root = screen->root;
if ((shell = getenv(SXHKD_SHELL_ENV)) == NULL && (shell = getenv(SHELL_ENV)) == NULL)
err("The '%s' environment variable is not defined.\n", SHELL_ENV);
symbols = xcb_key_symbols_alloc(dpy);

// TODO:
// - Allow specifying keymap on the command line.
xcb_xkb_use_extension(dpy, XCB_XKB_MAJOR_VERSION, XCB_XKB_MINOR_VERSION);
if(NULL == (kb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS)))
err("Failed to allocate xkb context.\n");
if(-1 == (kb_device = xkb_x11_get_core_keyboard_device_id(dpy)))
err("Failed to retrieve keyboard device.\n");
if(! refresh_keymap())
err("Failed to retrieve keymap.\n");

hotkeys_head = hotkeys_tail = NULL;
progress[0] = '\0';
}
Expand Down
7 changes: 6 additions & 1 deletion src/sxhkd.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
#define SXHKD_SXHKD_H

#include <xcb/xcb_keysyms.h>
#include <xcb/xkb.h>
#include <xkbcommon/xkbcommon.h>
#include <xkbcommon/xkbcommon-x11.h>
#include <stdio.h>
#include <stdbool.h>
#include "types.h"
Expand All @@ -44,7 +47,9 @@

extern xcb_connection_t *dpy;
extern xcb_window_t root;
extern xcb_key_symbols_t *symbols;
extern int32_t kb_device;
extern struct xkb_context* kb_context;
extern struct xkb_keymap* kb_keymap;

extern char *shell;
extern char config_file[MAXLEN];
Expand Down