]> git.sur5r.net Git - i3/i3/blob - i3-config-wizard/cfgparse.y
Merge branch 'fix-key-release'
[i3/i3] / i3-config-wizard / cfgparse.y
1 %{
2 /*
3  * vim:ts=4:sw=4:expandtab
4  *
5  */
6 #include <sys/types.h>
7 #include <sys/stat.h>
8 #include <unistd.h>
9 #include <fcntl.h>
10 #include <stdint.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14
15 #include <X11/Xlib.h>
16 #include <X11/XKBlib.h>
17
18 #include "libi3.h"
19
20 extern Display *dpy;
21
22 struct context {
23         int line_number;
24         char *line_copy;
25
26         char *compact_error;
27
28         /* These are the same as in YYLTYPE */
29         int first_column;
30         int last_column;
31
32         char *result;
33 };
34
35 typedef struct yy_buffer_state *YY_BUFFER_STATE;
36 extern int yylex(struct context *context);
37 extern int yyparse(void);
38 extern FILE *yyin;
39 YY_BUFFER_STATE yy_scan_string(const char *);
40
41 static struct context *context;
42 static xcb_connection_t *conn;
43 static xcb_key_symbols_t *keysyms;
44
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. */
48 //int yydebug = 1;
49
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)
59             fprintf(stderr, "^");
60         else fprintf(stderr, " ");
61     fprintf(stderr, "\n");
62     fprintf(stderr, "\n");
63 }
64
65 int yywrap() {
66     return 1;
67 }
68
69 char *rewrite_binding(const char *bindingline) {
70     char *result = NULL;
71
72     conn = xcb_connect(NULL, NULL);
73     if (conn == NULL || xcb_connection_has_error(conn)) {
74         fprintf(stderr, "Cannot open display\n");
75         exit(1);
76     }
77     keysyms = xcb_key_symbols_alloc(conn);
78
79     context = calloc(sizeof(struct context), 1);
80
81     yy_scan_string(bindingline);
82
83     if (yyparse() != 0) {
84         fprintf(stderr, "Could not parse configfile\n");
85         exit(1);
86     }
87
88     result = context->result;
89
90     if (context->line_copy)
91         free(context->line_copy);
92     free(context);
93     xcb_key_symbols_free(keysyms);
94     xcb_disconnect(conn);
95
96     return result;
97 }
98
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("");
113 }
114
115 /*
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).
118  *
119  */
120 static bool keysym_used_on_other_key(KeySym sym, xcb_keycode_t except_keycode) {
121     xcb_keycode_t i,
122                   min_keycode = xcb_get_setup(conn)->min_keycode,
123                   max_keycode = xcb_get_setup(conn)->max_keycode;
124
125     for (i = min_keycode; i && i <= max_keycode; i++) {
126         if (i == except_keycode)
127             continue;
128         for (int level = 0; level < 4; level++) {
129             if (xcb_key_symbols_get_keysym(keysyms, i, level) != sym)
130                 continue;
131             return true;
132         }
133     }
134     return false;
135 }
136
137 %}
138
139 %error-verbose
140 %lex-param { struct context *context }
141
142 %union {
143     int number;
144     char *string;
145 }
146
147 %token <number>NUMBER "<number>"
148 %token <string>STR "<string>"
149 %token TOKBINDCODE
150 %token TOKMODVAR "$mod"
151 %token MODIFIER "<modifier>"
152 %token TOKCONTROL "control"
153 %token TOKSHIFT "shift"
154 %token WHITESPACE "<whitespace>"
155
156 %%
157
158 lines: /* empty */
159     | lines WHITESPACE bindcode
160     | lines error
161     | lines bindcode
162     ;
163
164 bindcode:
165     TOKBINDCODE WHITESPACE binding_modifiers NUMBER WHITESPACE STR
166     {
167         //printf("\tFound keycode binding mod%d with key %d and command %s\n", $<number>3, $4, $<string>6);
168         int level = 0;
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). */
174             level = 1;
175
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.
180              *
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))
186                 level = 0;
187         }
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);
192         free(modifiers);
193     }
194     ;
195
196 binding_modifiers:
197     /* NULL */                               { $<number>$ = 0; }
198     | binding_modifier
199     | binding_modifiers '+' binding_modifier { $<number>$ = $<number>1 | $<number>3; }
200     | binding_modifiers '+'                  { $<number>$ = $<number>1; }
201     ;
202
203 binding_modifier:
204     MODIFIER        { $<number>$ = $<number>1; }
205     | TOKMODVAR     { $<number>$ = $<number>1; }
206     | TOKCONTROL    { $<number>$ = (1 << 2); }
207     | TOKSHIFT      { $<number>$ = (1 << 0); }
208     ;