awful: fix empty command adding
[awesome.git] / keybinding.c
blob332ea91d21fa6f8cf57dfcab05d4a411776adc79
1 /*
2 * keybinding.c - Key bindings configuration management
4 * Copyright © 2008 Julien Danjou <julien@danjou.info>
5 * Copyright © 2008 Pierre Habouzit <madcoder@debian.org>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 /* XStringToKeysym() */
24 #include <X11/Xlib.h>
26 #include "structs.h"
27 #include "common/refcount.h"
28 #include "common/array.h"
29 #include "keybinding.h"
31 ARRAY_TYPE(keybinding_t *, keybinding)
33 extern awesome_t globalconf;
35 static struct {
36 keybinding_array_t by_code;
37 keybinding_array_t by_sym;
38 } keys_g;
40 static void keybinding_delete(keybinding_t **kbp)
42 luaL_unref(globalconf.L, LUA_REGISTRYINDEX, (*kbp)->fct);
43 p_delete(kbp);
46 DO_RCNT(keybinding_t, keybinding, keybinding_delete)
47 ARRAY_FUNCS(keybinding_t *, keybinding, keybinding_unref)
48 DO_LUA_NEW(static, keybinding_t, keybinding, "keybinding", keybinding_ref)
49 DO_LUA_GC(keybinding_t, keybinding, "keybinding", keybinding_unref)
51 static int
52 keybinding_ev_cmp(xcb_keysym_t keysym, xcb_keycode_t keycode,
53 unsigned long mod, const keybinding_t *k)
55 if (k->keysym) {
56 if (k->keysym != keysym)
57 return k->keysym > keysym ? 1 : -1;
59 if (k->keycode) {
60 if (k->keycode != keycode)
61 return k->keycode > keycode ? 1 : -1;
63 return k->mod == mod ? 0 : (k->mod > mod ? 1 : -1);
66 static int
67 keybinding_cmp(const keybinding_t *k1, const keybinding_t *k2)
69 assert ((k1->keysym && k2->keysym) || (k1->keycode && k2->keycode));
70 assert ((!k1->keysym && !k2->keysym) || (!k1->keycode && !k2->keycode));
72 if (k1->keysym != k2->keysym)
73 return k2->keysym > k1->keysym ? 1 : -1;
74 if (k1->keycode != k2->keycode)
75 return k2->keycode > k1->keycode ? 1 : -1;
76 return k1->mod == k2->mod ? 0 : (k2->mod > k1->mod ? 1 : -1);
79 /** Grab key on the root windows.
80 * \param k The keybinding.
82 static void
83 window_root_grabkey(keybinding_t *k)
85 int phys_screen = globalconf.default_screen;
86 xcb_screen_t *s;
87 xcb_keycode_t kc;
89 if((kc = k->keycode)
90 || (k->keysym && (kc = xcb_key_symbols_get_keycode(globalconf.keysyms, k->keysym))))
93 s = xutil_screen_get(globalconf.connection, phys_screen);
94 xcb_grab_key(globalconf.connection, true, s->root,
95 k->mod, kc, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC);
96 xcb_grab_key(globalconf.connection, true, s->root,
97 k->mod | XCB_MOD_MASK_LOCK, kc, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC);
98 xcb_grab_key(globalconf.connection, true, s->root,
99 k->mod | globalconf.numlockmask, kc, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC);
100 xcb_grab_key(globalconf.connection, true, s->root,
101 k->mod | globalconf.numlockmask | XCB_MOD_MASK_LOCK, kc, XCB_GRAB_MODE_ASYNC,
102 XCB_GRAB_MODE_ASYNC);
103 phys_screen++;
104 } while(!globalconf.screens_info->xinerama_is_active
105 && phys_screen < globalconf.screens_info->nscreen);
108 /** Ungrab key on the root windows.
109 * \param k The keybinding.
111 static void
112 window_root_ungrabkey(keybinding_t *k)
114 int phys_screen = globalconf.default_screen;
115 xcb_screen_t *s;
116 xcb_keycode_t kc;
118 if((kc = k->keycode)
119 || (k->keysym && (kc = xcb_key_symbols_get_keycode(globalconf.keysyms, k->keysym))))
122 s = xutil_screen_get(globalconf.connection, phys_screen);
123 xcb_ungrab_key(globalconf.connection, kc, s->root,
124 k->mod);
125 xcb_ungrab_key(globalconf.connection, kc, s->root,
126 k->mod | XCB_MOD_MASK_LOCK);
127 xcb_ungrab_key(globalconf.connection, kc, s->root,
128 k->mod | globalconf.numlockmask);
129 xcb_ungrab_key(globalconf.connection, kc, s->root,
130 k->mod | globalconf.numlockmask | XCB_MOD_MASK_LOCK);
131 phys_screen++;
132 } while(!globalconf.screens_info->xinerama_is_active
133 && phys_screen < globalconf.screens_info->nscreen);
136 static void
137 keybinding_register_root(keybinding_t *k)
139 keybinding_array_t *arr = k->keysym ? &keys_g.by_sym : &keys_g.by_code;
140 int l = 0, r = arr->len;
142 keybinding_ref(&k);
144 while (l < r) {
145 int i = (r + l) / 2;
146 switch (keybinding_cmp(k, arr->tab[i])) {
147 case -1: /* k < arr->tab[i] */
148 r = i;
149 break;
150 case 0: /* k == arr->tab[i] */
151 keybinding_unref(&arr->tab[i]);
152 arr->tab[i] = k;
153 return;
154 case 1: /* k > arr->tab[i] */
155 l = i + 1;
156 break;
160 keybinding_array_splice(arr, r, 0, &k, 1);
161 window_root_grabkey(k);
164 static void
165 keybinding_unregister_root(keybinding_t **k)
167 keybinding_array_t *arr = (*k)->keysym ? &keys_g.by_sym : &keys_g.by_code;
168 int l = 0, r = arr->len;
170 while (l < r) {
171 int i = (r + l) / 2;
172 switch (keybinding_cmp(*k, arr->tab[i])) {
173 case -1: /* k < arr->tab[i] */
174 r = i;
175 break;
176 case 0: /* k == arr->tab[i] */
177 keybinding_array_take(arr, i);
178 window_root_ungrabkey(*k);
179 keybinding_unref(k);
180 return;
181 case 1: /* k > arr->tab[i] */
182 l = i + 1;
183 break;
188 keybinding_t *
189 keybinding_find(const xcb_key_press_event_t *ev)
191 const keybinding_array_t *arr = &keys_g.by_sym;
192 int l, r, mod = XUTIL_MASK_CLEAN(ev->state);
193 xcb_keysym_t keysym;
195 keysym = xcb_key_symbols_get_keysym(globalconf.keysyms, ev->detail, 0);
197 again:
198 l = 0;
199 r = arr->len;
200 while (l < r) {
201 int i = (r + l) / 2;
202 switch (keybinding_ev_cmp(keysym, ev->detail, mod, arr->tab[i])) {
203 case -1: /* ev < arr->tab[i] */
204 r = i;
205 break;
206 case 0: /* ev == arr->tab[i] */
207 return arr->tab[i];
208 case 1: /* ev > arr->tab[i] */
209 l = i + 1;
210 break;
213 if (arr != &keys_g.by_code) {
214 arr = &keys_g.by_code;
215 goto again;
217 return NULL;
220 static void
221 __luaA_keystore(keybinding_t *key, const char *str)
223 if(!a_strlen(str))
224 return;
225 else if(*str != '#')
226 key->keysym = XStringToKeysym(str);
227 else
228 key->keycode = atoi(str + 1);
231 /** Define a global key binding. This key binding will always be available.
232 * \param L The Lua VM state.
234 * \luastack
235 * \lparam A table with modifier keys.
236 * \lparam A key name.
237 * \lparam A function to execute.
238 * \lreturn The keybinding.
240 static int
241 luaA_keybinding_new(lua_State *L)
243 size_t i, len;
244 keybinding_t *k;
245 const char *key;
247 /* arg 2 is key mod table */
248 luaA_checktable(L, 2);
249 /* arg 3 is key */
250 key = luaL_checkstring(L, 3);
251 /* arg 4 is cmd to run */
252 luaA_checkfunction(L, 4);
254 /* get the last arg as function */
255 k = p_new(keybinding_t, 1);
256 __luaA_keystore(k, key);
257 k->fct = luaL_ref(L, LUA_REGISTRYINDEX);
259 len = lua_objlen(L, 2);
260 for(i = 1; i <= len; i++)
262 lua_rawgeti(L, 2, i);
263 k->mod |= xutil_key_mask_fromstr(luaL_checkstring(L, -1));
266 return luaA_keybinding_userdata_new(L, k);
269 /** Add a global key binding. This key binding will always be available.
270 * \param L The Lua VM state.
272 * \luastack
273 * \lvalue A keybinding.
275 static int
276 luaA_keybinding_add(lua_State *L)
278 keybinding_t **k = luaA_checkudata(L, 1, "keybinding");
280 keybinding_register_root(*k);
281 return 0;
284 /** Remove a global key binding.
285 * \param L The Lua VM state.
287 * \luastack
288 * \lvalue A keybinding.
290 static int
291 luaA_keybinding_remove(lua_State *L)
293 keybinding_t **k = luaA_checkudata(L, 1, "keybinding");
294 keybinding_unregister_root(k);
295 return 0;
298 /** Convert a keybinding to a printable string.
299 * \return A string.
301 static int
302 luaA_keybinding_tostring(lua_State *L)
304 keybinding_t **p = luaA_checkudata(L, 1, "keybinding");
305 lua_pushfstring(L, "[keybinding udata(%p)]", *p);
306 return 1;
309 const struct luaL_reg awesome_keybinding_methods[] =
311 { "__call", luaA_keybinding_new },
312 { NULL, NULL }
314 const struct luaL_reg awesome_keybinding_meta[] =
316 {"add", luaA_keybinding_add },
317 {"remove", luaA_keybinding_remove },
318 {"__tostring", luaA_keybinding_tostring },
319 {"__gc", luaA_keybinding_gc },
320 { NULL, NULL },