3 * vim:ts=4:sw=4:expandtab
16 #include <X11/XKBlib.h>
28 /* These are the same as in YYLTYPE */
35 typedef struct yy_buffer_state *YY_BUFFER_STATE;
36 extern int yylex(struct context *context);
37 extern int yyparse(void);
39 YY_BUFFER_STATE yy_scan_string(const char *);
41 static struct context *context;
42 static xcb_connection_t *conn;
43 static xcb_key_symbols_t *keysyms;
45 /* We don’t need yydebug for now, as we got decent error messages using
46 * yyerror(). Should you ever want to extend the parser, it might be handy
47 * to just comment it in again, so it stays here. */
50 void yyerror(const char *error_message) {
51 fprintf(stderr, "\n");
52 fprintf(stderr, "CONFIG: %s\n", error_message);
53 fprintf(stderr, "CONFIG: line %d:\n",
54 context->line_number);
55 fprintf(stderr, "CONFIG: %s\n", context->line_copy);
56 fprintf(stderr, "CONFIG: ");
57 for (int c = 1; c <= context->last_column; c++)
58 if (c >= context->first_column)
60 else fprintf(stderr, " ");
61 fprintf(stderr, "\n");
62 fprintf(stderr, "\n");
69 char *rewrite_binding(const char *bindingline) {
72 conn = xcb_connect(NULL, NULL);
73 if (conn == NULL || xcb_connection_has_error(conn)) {
74 fprintf(stderr, "Cannot open display\n");
77 keysyms = xcb_key_symbols_alloc(conn);
79 context = calloc(sizeof(struct context), 1);
81 yy_scan_string(bindingline);
84 fprintf(stderr, "Could not parse configfile\n");
88 result = context->result;
90 if (context->line_copy)
91 free(context->line_copy);
93 xcb_key_symbols_free(keysyms);
99 /* XXX: does not work for combinations of modifiers yet */
100 static char *modifier_to_string(int modifiers) {
101 //printf("should convert %d to string\n", modifiers);
102 if (modifiers == (1 << 3))
103 return strdup("$mod+");
104 else if (modifiers == ((1 << 3) | (1 << 0)))
105 return strdup("$mod+Shift+");
106 else if (modifiers == (1 << 9))
107 return strdup("$mod+");
108 else if (modifiers == ((1 << 9) | (1 << 0)))
109 return strdup("$mod+Shift+");
110 else if (modifiers == (1 << 0))
111 return strdup("Shift+");
112 else return strdup("");
116 * Returns true if sym is bound to any key except for 'except_keycode' on the
117 * first four layers (normal, shift, mode_switch, mode_switch + shift).
120 static bool keysym_used_on_other_key(KeySym sym, xcb_keycode_t except_keycode) {
122 min_keycode = xcb_get_setup(conn)->min_keycode,
123 max_keycode = xcb_get_setup(conn)->max_keycode;
125 for (i = min_keycode; i && i <= max_keycode; i++) {
126 if (i == except_keycode)
128 for (int level = 0; level < 4; level++) {
129 if (xcb_key_symbols_get_keysym(keysyms, i, level) != sym)
140 %lex-param { struct context *context }
147 %token <number>NUMBER "<number>"
148 %token <string>STR "<string>"
150 %token TOKMODVAR "$mod"
151 %token MODIFIER "<modifier>"
152 %token TOKCONTROL "control"
153 %token TOKSHIFT "shift"
154 %token WHITESPACE "<whitespace>"
159 | lines WHITESPACE bindcode
165 TOKBINDCODE WHITESPACE binding_modifiers NUMBER WHITESPACE STR
167 //printf("\tFound keycode binding mod%d with key %d and command %s\n", $<number>3, $4, $<string>6);
169 if (($<number>3 & (1 << 0))) {
170 /* When shift is included, we really need to use the second-level
171 * symbol (upper-case). The lower-case symbol could be on a
172 * different key than the upper-case one (unlikely for letters, but
173 * more likely for special characters). */
176 /* Try to use the keysym on the first level (lower-case). In case
177 * this doesn’t make it ambiguous (think of a keyboard layout
178 * having '1' on two different keys, but '!' only on keycode 10),
179 * we’ll stick with the keysym of the first level.
181 * This reduces a lot of confusion for users who switch keyboard
182 * layouts from qwerty to qwertz or other slight variations of
183 * qwerty (yes, that happens quite often). */
184 KeySym sym = XkbKeycodeToKeysym(dpy, $4, 0, 0);
185 if (!keysym_used_on_other_key(sym, $4))
188 KeySym sym = XkbKeycodeToKeysym(dpy, $4, 0, level);
189 char *str = XKeysymToString(sym);
190 char *modifiers = modifier_to_string($<number>3);
191 sasprintf(&(context->result), "bindsym %s%s %s\n", modifiers, str, $<string>6);
197 /* NULL */ { $<number>$ = 0; }
199 | binding_modifiers '+' binding_modifier { $<number>$ = $<number>1 | $<number>3; }
200 | binding_modifiers '+' { $<number>$ = $<number>1; }
204 MODIFIER { $<number>$ = $<number>1; }
205 | TOKMODVAR { $<number>$ = $<number>1; }
206 | TOKCONTROL { $<number>$ = (1 << 2); }
207 | TOKSHIFT { $<number>$ = (1 << 0); }