summaryrefslogtreecommitdiff
path: root/components/keymap.c
blob: aed408e4a10366bd2e0fcf5653a39d47d32c5363 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
/* See LICENSE file for copyright and license details. */
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <X11/XKBlib.h>
#include <X11/Xlib.h>

#include "../util.h"

#define LAYOUT_MAX 256

/* Given a token (sym) from the xkb_symbols string
 * check whether it is a valid layout/variant. The
 * EXCLUDES array contains invalid layouts/variants
 * that are part of the xkb rules config.
 */
static int
IsLayoutOrVariant(char *sym)
{
	static const char* EXCLUDES[] = { "evdev", "inet", "pc", "base" };

	size_t i;
	for (i = 0; i < sizeof(EXCLUDES)/sizeof(EXCLUDES[0]); ++i) {
		if (strstr(sym, EXCLUDES[i])) {
			return 0;
		}
	}

	return 1;
}

static void
GetKeyLayout(char *syms, char layout[], int groupNum)
{
	char *token, *copy, *delims;
	int group;

	delims = "+:";
	group = 0;
	copy = strdup(syms);
	token = strtok(copy, delims);
	while (token != NULL && group <= groupNum) {
		/* Ignore :2,:3,:4 which represent additional layout
 		 * groups
 		 */
		if (IsLayoutOrVariant(token)
		    && !(strlen(token) == 1 && isdigit(token[0]))) {
			strncpy (layout, token, LAYOUT_MAX);
			group++;
		}

		token = strtok(NULL,delims);
	}

	free(copy);
}

const char *
keymap(void)
{
	static char layout[LAYOUT_MAX];

	Display *dpy;
	char *symbols = NULL;
	XkbDescRec* desc = NULL;

	memset(layout, '\0', LAYOUT_MAX);

	if (!(dpy = XOpenDisplay(NULL))) {
		warn("XOpenDisplay: Failed to open display");
		return NULL;
	}

	;
	if (!(desc = XkbAllocKeyboard())) {
		warn("XkbAllocKeyboard: failed to allocate keyboard");
		XCloseDisplay(dpy);
		return NULL;
	}

	XkbGetNames(dpy, XkbSymbolsNameMask, desc);
	if (desc->names) {
		XkbStateRec state;
		XkbGetState(dpy, XkbUseCoreKbd, &state);

		symbols = XGetAtomName(dpy, desc->names->symbols);
		GetKeyLayout(symbols, layout, state.group);
		XFree(symbols);
	} else {
		warn("XkbGetNames: failed to retrieve symbols for keys");
		return NULL;
	}

	XkbFreeKeyboard(desc, XkbSymbolsNameMask, 1);
	XCloseDisplay(dpy);

	return layout;
}