From 53f58aa1a68c4a9dc791987265cd77ae5a110418 Mon Sep 17 00:00:00 2001 From: Julien Danjou Date: Thu, 18 Dec 2008 11:33:19 +0100 Subject: [PATCH] keybinding: move to key Signed-off-by: Julien Danjou --- CMakeLists.txt | 1 + awesomerc.lua.in | 188 ++++++++++---------- event.c | 6 +- key.c | 400 ++++++++++++++++++++++++++++++++++++++++++ keybinding.h => key.h | 14 +- keybinding.c | 365 +------------------------------------- keygrabber.c | 2 +- luaa.c | 8 +- structs.h | 6 +- 9 files changed, 521 insertions(+), 469 deletions(-) create mode 100644 key.c rename keybinding.h => key.h (85%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 494d2cf1..cb09cc4b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,6 +45,7 @@ set(AWE_SRCS ${SOURCE_DIR}/property.c ${SOURCE_DIR}/ewmh.c ${SOURCE_DIR}/keybinding.c + ${SOURCE_DIR}/key.c ${SOURCE_DIR}/keygrabber.c ${SOURCE_DIR}/mousegrabber.c ${SOURCE_DIR}/layout.c diff --git a/awesomerc.lua.in b/awesomerc.lua.in index a4086a4b..eb120840 100644 --- a/awesomerc.lua.in +++ b/awesomerc.lua.in @@ -177,116 +177,118 @@ for s = 1, screen.count() do end for i = 1, keynumber do - keybinding({ modkey }, i, - function () - local screen = mouse.screen - if tags[screen][i] then - awful.tag.viewonly(tags[screen][i]) - end - end):add() - keybinding({ modkey, "Control" }, i, - function () - local screen = mouse.screen - if tags[screen][i] then - tags[screen][i].selected = not tags[screen][i].selected - end - end):add() - keybinding({ modkey, "Shift" }, i, - function () - if client.focus and tags[client.focus.screen][i] then - awful.client.movetotag(tags[client.focus.screen][i]) - end - end):add() - keybinding({ modkey, "Control", "Shift" }, i, - function () - if client.focus and tags[client.focus.screen][i] then - awful.client.toggletag(tags[client.focus.screen][i]) - end - end):add() + key({ modkey }, i, + function () + local screen = mouse.screen + if tags[screen][i] then + awful.tag.viewonly(tags[screen][i]) + end + end):add() + key({ modkey, "Control" }, i, + function () + local screen = mouse.screen + if tags[screen][i] then + tags[screen][i].selected = not tags[screen][i].selected + end + end):add() + key({ modkey, "Shift" }, i, + function () + if client.focus and tags[client.focus.screen][i] then + awful.client.movetotag(tags[client.focus.screen][i]) + end + end):add() + key({ modkey, "Control", "Shift" }, i, + function () + if client.focus and tags[client.focus.screen][i] then + awful.client.toggletag(tags[client.focus.screen][i]) + end + end):add() end -keybinding({ modkey }, "Left", awful.tag.viewprev):add() -keybinding({ modkey }, "Right", awful.tag.viewnext):add() -keybinding({ modkey }, "Escape", awful.tag.history.restore):add() +key({ modkey }, "Left", awful.tag.viewprev):add() +key({ modkey }, "Right", awful.tag.viewnext):add() +key({ modkey }, "Escape", awful.tag.history.restore):add() -- Standard program -keybinding({ modkey }, "Return", function () awful.util.spawn(terminal) end):add() +key({ modkey }, "Return", function () awful.util.spawn(terminal) end):add() -keybinding({ modkey, "Control" }, "r", function () - mypromptbox[mouse.screen].text = - awful.util.escape(awful.util.restart()) - end):add() -keybinding({ modkey, "Shift" }, "q", awesome.quit):add() +key({ modkey, "Control" }, "r", function () + mypromptbox[mouse.screen].text = + awful.util.escape(awful.util.restart()) + end):add() +key({ modkey, "Shift" }, "q", awesome.quit):add() -- Client manipulation -keybinding({ modkey }, "m", function () if client.focus then client.focus.maximized_horizontal = not client.focus.maximized_horizontal +key({ modkey }, "m", function () if client.focus then client.focus.maximized_horizontal = not client.focus.maximized_horizontal client.focus.maximized_vertical = not client.focus.maximized_vertical end end):add() -keybinding({ modkey }, "f", function () if client.focus then client.focus.fullscreen = not client.focus.fullscreen end end):add() -keybinding({ modkey, "Shift" }, "c", function () if client.focus then client.focus:kill() end end):add() -keybinding({ modkey }, "j", function () awful.client.focus.byidx(1); if client.focus then client.focus:raise() end end):add() -keybinding({ modkey }, "k", function () awful.client.focus.byidx(-1); if client.focus then client.focus:raise() end end):add() -keybinding({ modkey, "Shift" }, "j", function () awful.client.swap.byidx(1) end):add() -keybinding({ modkey, "Shift" }, "k", function () awful.client.swap.byidx(-1) end):add() -keybinding({ modkey, "Control" }, "j", function () awful.screen.focus(1) end):add() -keybinding({ modkey, "Control" }, "k", function () awful.screen.focus(-1) end):add() -keybinding({ modkey, "Control" }, "space", awful.client.floating.toggle):add() -keybinding({ modkey, "Control" }, "Return", function () if client.focus then client.focus:swap(awful.client.getmaster()) end end):add() -keybinding({ modkey }, "o", awful.client.movetoscreen):add() -keybinding({ modkey }, "Tab", awful.client.focus.history.previous):add() -keybinding({ modkey }, "u", awful.client.urgent.jumpto):add() -keybinding({ modkey, "Shift" }, "r", function () if client.focus then client.focus:redraw() end end):add() +key({ modkey }, "f", function () if client.focus then client.focus.fullscreen = not client.focus.fullscreen end end):add() +key({ modkey, "Shift" }, "c", function () if client.focus then client.focus:kill() end end):add() +key({ modkey }, "j", function () awful.client.focus.byidx(1); if client.focus then client.focus:raise() end end):add() +key({ modkey }, "k", function () awful.client.focus.byidx(-1); if client.focus then client.focus:raise() end end):add() +key({ modkey, "Shift" }, "j", function () awful.client.swap.byidx(1) end):add() +key({ modkey, "Shift" }, "k", function () awful.client.swap.byidx(-1) end):add() +key({ modkey, "Control" }, "j", function () awful.screen.focus(1) end):add() +key({ modkey, "Control" }, "k", function () awful.screen.focus(-1) end):add() +key({ modkey, "Control" }, "space", awful.client.floating.toggle):add() +key({ modkey, "Control" }, "Return", function () if client.focus then client.focus:swap(awful.client.getmaster()) end end):add() +key({ modkey }, "o", awful.client.movetoscreen):add() +key({ modkey }, "Tab", awful.client.focus.history.previous):add() +key({ modkey }, "u", awful.client.urgent.jumpto):add() +key({ modkey, "Shift" }, "r", function () if client.focus then client.focus:redraw() end end):add() -- Layout manipulation -keybinding({ modkey }, "l", function () awful.tag.incmwfact(0.05) end):add() -keybinding({ modkey }, "h", function () awful.tag.incmwfact(-0.05) end):add() -keybinding({ modkey, "Shift" }, "h", function () awful.tag.incnmaster(1) end):add() -keybinding({ modkey, "Shift" }, "l", function () awful.tag.incnmaster(-1) end):add() -keybinding({ modkey, "Control" }, "h", function () awful.tag.incncol(1) end):add() -keybinding({ modkey, "Control" }, "l", function () awful.tag.incncol(-1) end):add() -keybinding({ modkey }, "space", function () awful.layout.inc(layouts, 1) end):add() -keybinding({ modkey, "Shift" }, "space", function () awful.layout.inc(layouts, -1) end):add() +key({ modkey }, "l", function () awful.tag.incmwfact(0.05) end):add() +key({ modkey }, "h", function () awful.tag.incmwfact(-0.05) end):add() +key({ modkey, "Shift" }, "h", function () awful.tag.incnmaster(1) end):add() +key({ modkey, "Shift" }, "l", function () awful.tag.incnmaster(-1) end):add() +key({ modkey, "Control" }, "h", function () awful.tag.incncol(1) end):add() +key({ modkey, "Control" }, "l", function () awful.tag.incncol(-1) end):add() +key({ modkey }, "space", function () awful.layout.inc(layouts, 1) end):add() +key({ modkey, "Shift" }, "space", function () awful.layout.inc(layouts, -1) end):add() -- Prompt -keybinding({ modkey }, "F1", function () - awful.prompt.run({ prompt = "Run: " }, mypromptbox[mouse.screen], awful.util.spawn, awful.completion.bash, - awful.util.getdir("cache") .. "/history") - end):add() -keybinding({ modkey }, "F4", function () - awful.prompt.run({ prompt = "Run Lua code: " }, mypromptbox[mouse.screen], awful.util.eval, awful.prompt.bash, - awful.util.getdir("cache") .. "/history_eval") - end):add() +key({ modkey }, "F1", function () + awful.prompt.run({ prompt = "Run: " }, mypromptbox[mouse.screen], + awful.util.spawn, awful.completion.bash, + awful.util.getdir("cache") .. "/history") + end):add() +key({ modkey }, "F4", function () + awful.prompt.run({ prompt = "Run Lua code: " }, mypromptbox[mouse.screen], + awful.util.eval, awful.prompt.bash, + awful.util.getdir("cache") .. "/history_eval") + end):add() -keybinding({ modkey, "Ctrl" }, "i", function () - local s = mouse.screen - if mypromptbox[s].text then - mypromptbox[s].text = nil - elseif client.focus then - mypromptbox[s].text = nil - if client.focus.class then - mypromptbox[s].text = "Class: " .. client.focus.class .. " " - end - if client.focus.instance then - mypromptbox[s].text = mypromptbox[s].text .. "Instance: ".. client.focus.instance .. " " - end - if client.focus.role then - mypromptbox[s].text = mypromptbox[s].text .. "Role: ".. client.focus.role - end - end - end):add() +key({ modkey, "Ctrl" }, "i", function () + local s = mouse.screen + if mypromptbox[s].text then + mypromptbox[s].text = nil + elseif client.focus then + mypromptbox[s].text = nil + if client.focus.class then + mypromptbox[s].text = "Class: " .. client.focus.class .. " " + end + if client.focus.instance then + mypromptbox[s].text = mypromptbox[s].text .. "Instance: ".. client.focus.instance .. " " + end + if client.focus.role then + mypromptbox[s].text = mypromptbox[s].text .. "Role: ".. client.focus.role + end + end + end):add() -- Client awful tagging: this is useful to tag some clients and then do stuff like move to tag on them -keybinding({ modkey }, "t", awful.client.togglemarked):add() +key({ modkey }, "t", awful.client.togglemarked):add() for i = 1, keynumber do - keybinding({ modkey, "Shift" }, "F" .. i, - function () - local screen = mouse.screen - if tags[screen][i] then - for k, c in pairs(awful.client.getmarked()) do - awful.client.movetotag(tags[screen][i], c) - end - end - end):add() + key({ modkey, "Shift" }, "F" .. i, + function () + local screen = mouse.screen + if tags[screen][i] then + for k, c in pairs(awful.client.getmarked()) do + awful.client.movetotag(tags[screen][i], c) + end + end + end):add() end -- }}} diff --git a/event.c b/event.c index b77c119b..d38a92e6 100644 --- a/event.c +++ b/event.c @@ -33,7 +33,7 @@ #include "client.h" #include "widget.h" #include "titlebar.h" -#include "keybinding.h" +#include "key.h" #include "keygrabber.h" #include "mousegrabber.h" #include "luaa.h" @@ -559,7 +559,7 @@ event_handle_key(void *data __attribute__ ((unused)), } else { - keybinding_t *k = keybinding_find(ev); + keyb_t *k = key_find(ev); if(k) switch(ev->response_type) @@ -787,7 +787,7 @@ event_handle_mappingnotify(void *data, } while(phys_screen < nscreen); /* regrab everything */ - keybinding_array_t *arr = &globalconf.keys.by_sym; + key_array_t *arr = &globalconf.keys.by_sym; for(int i = 0; i < arr->len; i++) window_root_grabkey(arr->tab[i]); diff --git a/key.c b/key.c new file mode 100644 index 00000000..3055d8da --- /dev/null +++ b/key.c @@ -0,0 +1,400 @@ +/* + * key.c - Key bindings configuration management + * + * Copyright © 2008 Julien Danjou + * Copyright © 2008 Pierre Habouzit + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +/* XStringToKeysym() */ +#include + +#include "structs.h" + +extern awesome_t globalconf; + +static void +key_delete(keyb_t **kbp) +{ + luaL_unref(globalconf.L, LUA_REGISTRYINDEX, (*kbp)->press); + luaL_unref(globalconf.L, LUA_REGISTRYINDEX, (*kbp)->release); + p_delete(kbp); +} + +DO_RCNT(keyb_t, key, key_delete) +ARRAY_FUNCS(keyb_t *, key, key_unref) +DO_LUA_NEW(static, keyb_t, key, "key", key_ref) +DO_LUA_GC(keyb_t, key, "key", key_unref) + +static int +key_ev_cmp(xcb_keysym_t keysym, xcb_keycode_t keycode, + unsigned long mod, const keyb_t *k) +{ + if (k->keysym) { + if (k->keysym != keysym) + return k->keysym > keysym ? 1 : -1; + } + if (k->keycode) { + if (k->keycode != keycode) + return k->keycode > keycode ? 1 : -1; + } + return k->mod == mod ? 0 : (k->mod > mod ? 1 : -1); +} + +static int +key_cmp(const keyb_t *k1, const keyb_t *k2) +{ + assert ((k1->keysym && k2->keysym) || (k1->keycode && k2->keycode)); + assert ((!k1->keysym && !k2->keysym) || (!k1->keycode && !k2->keycode)); + + if (k1->keysym != k2->keysym) + return k2->keysym > k1->keysym ? 1 : -1; + if (k1->keycode != k2->keycode) + return k2->keycode > k1->keycode ? 1 : -1; + return k1->mod == k2->mod ? 0 : (k2->mod > k1->mod ? 1 : -1); +} + +/** Grab key on the root windows. + * \param k The key. + */ +void +window_root_grabkey(keyb_t *k) +{ + int phys_screen = 0; + int nscreen = xcb_setup_roots_length(xcb_get_setup(globalconf.connection)); + xcb_screen_t *s; + xcb_keycode_t kc; + + if((kc = k->keycode) + || (k->keysym + && (kc = xcb_key_symbols_get_keycode(globalconf.keysyms, k->keysym)))) + do + { + s = xutil_screen_get(globalconf.connection, phys_screen); + xcb_grab_key(globalconf.connection, true, s->root, + k->mod, kc, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC); + xcb_grab_key(globalconf.connection, true, s->root, + k->mod | XCB_MOD_MASK_LOCK, kc, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC); + xcb_grab_key(globalconf.connection, true, s->root, + k->mod | globalconf.numlockmask, kc, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC); + xcb_grab_key(globalconf.connection, true, s->root, + k->mod | globalconf.numlockmask | XCB_MOD_MASK_LOCK, kc, XCB_GRAB_MODE_ASYNC, + XCB_GRAB_MODE_ASYNC); + phys_screen++; + } while(phys_screen < nscreen); +} + +/** Ungrab key on the root windows. + * \param k The key. + */ +static void +window_root_ungrabkey(keyb_t *k) +{ + int phys_screen = 0; + int nscreen = xcb_setup_roots_length(xcb_get_setup(globalconf.connection)); + xcb_screen_t *s; + xcb_keycode_t kc; + + if((kc = k->keycode) + || (k->keysym && (kc = xcb_key_symbols_get_keycode(globalconf.keysyms, k->keysym)))) + do + { + s = xutil_screen_get(globalconf.connection, phys_screen); + xcb_ungrab_key(globalconf.connection, kc, s->root, + k->mod); + xcb_ungrab_key(globalconf.connection, kc, s->root, + k->mod | XCB_MOD_MASK_LOCK); + xcb_ungrab_key(globalconf.connection, kc, s->root, + k->mod | globalconf.numlockmask); + xcb_ungrab_key(globalconf.connection, kc, s->root, + k->mod | globalconf.numlockmask | XCB_MOD_MASK_LOCK); + phys_screen++; + } while(phys_screen < nscreen); +} + +static void +key_register_root(keyb_t *k) +{ + key_array_t *arr = k->keysym ? &globalconf.keys.by_sym : &globalconf.keys.by_code; + int l = 0, r = arr->len; + + key_ref(&k); + + while (l < r) { + int i = (r + l) / 2; + switch (key_cmp(k, arr->tab[i])) { + case -1: /* k < arr->tab[i] */ + r = i; + break; + case 0: /* k == arr->tab[i] */ + key_unref(&arr->tab[i]); + arr->tab[i] = k; + return; + case 1: /* k > arr->tab[i] */ + l = i + 1; + break; + } + } + + key_array_splice(arr, r, 0, &k, 1); + window_root_grabkey(k); +} + +static void +key_unregister_root(keyb_t **k) +{ + key_array_t *arr = (*k)->keysym ? &globalconf.keys.by_sym : &globalconf.keys.by_code; + int l = 0, r = arr->len; + + while (l < r) { + int i = (r + l) / 2; + switch (key_cmp(*k, arr->tab[i])) { + case -1: /* k < arr->tab[i] */ + r = i; + break; + case 0: /* k == arr->tab[i] */ + key_array_take(arr, i); + window_root_ungrabkey(*k); + key_unref(k); + return; + case 1: /* k > arr->tab[i] */ + l = i + 1; + break; + } + } +} + +/** Return the keysym from keycode. + * \param detail The keycode received. + * \param state The modifier state. + * \return A keysym. + */ +xcb_keysym_t +key_getkeysym(xcb_keycode_t detail, uint16_t state) +{ + xcb_keysym_t k0, k1; + + /* 'col' (third parameter) is used to get the proper KeySym + * according to modifier (XCB doesn't provide an equivalent to + * XLookupString()). + * + * If Mod5 is ON we look into second group. + */ + if(state & XCB_MOD_MASK_5) + { + k0 = xcb_key_symbols_get_keysym(globalconf.keysyms, detail, 2); + k1 = xcb_key_symbols_get_keysym(globalconf.keysyms, detail, 3); + } + else + { + k0 = xcb_key_symbols_get_keysym(globalconf.keysyms, detail, 0); + k1 = xcb_key_symbols_get_keysym(globalconf.keysyms, detail, 1); + } + + /* The numlock modifier is on and the second KeySym is a keypad + * KeySym */ + if((state & globalconf.numlockmask) && xcb_is_keypad_key(k1)) + { + /* The Shift modifier is on, or if the Lock modifier is on and + * is interpreted as ShiftLock, use the first KeySym */ + if((state & XCB_MOD_MASK_SHIFT) || + (state & XCB_MOD_MASK_LOCK && (state & globalconf.shiftlockmask))) + return k0; + else + return k1; + } + + /* The Shift and Lock modifers are both off, use the first + * KeySym */ + else if(!(state & XCB_MOD_MASK_SHIFT) && !(state & XCB_MOD_MASK_LOCK)) + return k0; + + /* The Shift modifier is off and the Lock modifier is on and is + * interpreted as CapsLock */ + else if(!(state & XCB_MOD_MASK_SHIFT) && + (state & XCB_MOD_MASK_LOCK && (state & globalconf.capslockmask))) + /* The first Keysym is used but if that KeySym is lowercase + * alphabetic, then the corresponding uppercase KeySym is used + * instead */ + return k1; + + /* The Shift modifier is on, and the Lock modifier is on and is + * interpreted as CapsLock */ + else if((state & XCB_MOD_MASK_SHIFT) && + (state & XCB_MOD_MASK_LOCK && (state & globalconf.capslockmask))) + /* The second Keysym is used but if that KeySym is lowercase + * alphabetic, then the corresponding uppercase KeySym is used + * instead */ + return k1; + + /* The Shift modifer is on, or the Lock modifier is on and is + * interpreted as ShiftLock, or both */ + else if((state & XCB_MOD_MASK_SHIFT) || + (state & XCB_MOD_MASK_LOCK && (state & globalconf.shiftlockmask))) + return k1; + + return XCB_NO_SYMBOL; +} + + +keyb_t * +key_find(const xcb_key_press_event_t *ev) +{ + const key_array_t *arr = &globalconf.keys.by_sym; + int l, r, mod = XUTIL_MASK_CLEAN(ev->state); + xcb_keysym_t keysym; + + /* get keysym ignoring shift and mod5 */ + keysym = key_getkeysym(ev->detail, ev->state & ~(XCB_MOD_MASK_SHIFT | XCB_MOD_MASK_5 | XCB_MOD_MASK_LOCK)); + + again: + l = 0; + r = arr->len; + while (l < r) + { + int i = (r + l) / 2; + switch (key_ev_cmp(keysym, ev->detail, mod, arr->tab[i])) + { + case -1: /* ev < arr->tab[i] */ + r = i; + break; + case 0: /* ev == arr->tab[i] */ + return arr->tab[i]; + case 1: /* ev > arr->tab[i] */ + l = i + 1; + break; + } + } + if (arr != &globalconf.keys.by_code) + { + arr = &globalconf.keys.by_code; + goto again; + } + return NULL; +} + +static void +luaA_keystore(keyb_t *key, const char *str, ssize_t len) +{ + if(len) + { + if(*str != '#') + { + key->keysym = XStringToKeysym(str); + if (!key->keysym) + { + if (len == 1) + key->keysym = *str; + else + warn("there's no keysym named \"%s\"", str); + } + } + else + key->keycode = atoi(str + 1); + } +} + +/** Define a global key binding. This key binding will always be available. + * \param L The Lua VM state. + * + * \luastack + * \lparam A table with modifier keys. + * \lparam A key name. + * \lparam A function to execute on key press. + * \lparam A function to execute on key release. + * \lreturn The key. + */ +int +luaA_key_new(lua_State *L) +{ + size_t i, len; + keyb_t *k; + const char *key; + luaA_ref press = LUA_REFNIL, release = LUA_REFNIL; + + /* arg 2 is key mod table */ + luaA_checktable(L, 2); + /* arg 3 is key */ + key = luaL_checklstring(L, 3, &len); + + if(!lua_isnil(L, 4)) + luaA_registerfct(L, 4, &press); + + if(lua_gettop(L) == 5 && !lua_isnil(L, 5)) + luaA_registerfct(L, 5, &release); + + /* get the last arg as function */ + k = p_new(keyb_t, 1); + luaA_keystore(k, key, len); + + k->press = press; + k->release = release; + + len = lua_objlen(L, 2); + for(i = 1; i <= len; i++) + { + size_t blen; + lua_rawgeti(L, 2, i); + key = luaL_checklstring(L, -1, &blen); + k->mod |= xutil_key_mask_fromstr(key, blen); + } + + return luaA_key_userdata_new(L, k); +} + +/** Add a global key binding. This key binding will always be available. + * \param L The Lua VM state. + * + * \luastack + * \lvalue A key. + */ +static int +luaA_key_add(lua_State *L) +{ + keyb_t **k = luaA_checkudata(L, 1, "key"); + key_register_root(*k); + return 0; +} + +/** Remove a global key binding. + * \param L The Lua VM state. + * + * \luastack + * \lvalue A key. + */ +static int +luaA_key_remove(lua_State *L) +{ + keyb_t **k = luaA_checkudata(L, 1, "key"); + key_unregister_root(k); + return 0; +} + +const struct luaL_reg awesome_key_methods[] = +{ + { "__call", luaA_key_new }, + { NULL, NULL } +}; +const struct luaL_reg awesome_key_meta[] = +{ + { "add", luaA_key_add }, + { "remove", luaA_key_remove }, + { "__tostring", luaA_key_tostring }, + { "__gc", luaA_key_gc }, + { NULL, NULL }, +}; + diff --git a/keybinding.h b/key.h similarity index 85% rename from keybinding.h rename to key.h index 7cc1fb5a..8c974529 100644 --- a/keybinding.h +++ b/key.h @@ -1,5 +1,5 @@ /* - * keybinding.h - Keybinding helpers + * key.h - Keybinding helpers * * Copyright © 2008 Pierre Habouzit * @@ -26,7 +26,7 @@ #include "luaa.h" #include "common/array.h" -typedef struct keybinding_t +typedef struct keyb_t { /** Ref count */ int refcount; @@ -40,12 +40,14 @@ typedef struct keybinding_t luaA_ref press; /** Lua function to execute on release */ luaA_ref release; -} keybinding_t; +} keyb_t; -ARRAY_TYPE(keybinding_t *, keybinding) +ARRAY_TYPE(keyb_t *, key) -keybinding_t *keybinding_find(const xcb_key_press_event_t *); +keyb_t *key_find(const xcb_key_press_event_t *); xcb_keysym_t key_getkeysym(xcb_keycode_t, uint16_t); -void window_root_grabkey(keybinding_t *); +void window_root_grabkey(keyb_t *); + +int luaA_key_new(lua_State *); #endif diff --git a/keybinding.c b/keybinding.c index 89618977..bb03a5ff 100644 --- a/keybinding.c +++ b/keybinding.c @@ -2,7 +2,6 @@ * keybinding.c - Key bindings configuration management * * Copyright © 2008 Julien Danjou - * Copyright © 2008 Pierre Habouzit * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,368 +19,23 @@ * */ -/* XStringToKeysym() */ -#include - -#include "structs.h" - -extern awesome_t globalconf; - -static void -keybinding_delete(keybinding_t **kbp) -{ - luaL_unref(globalconf.L, LUA_REGISTRYINDEX, (*kbp)->press); - luaL_unref(globalconf.L, LUA_REGISTRYINDEX, (*kbp)->release); - p_delete(kbp); -} - -DO_RCNT(keybinding_t, keybinding, keybinding_delete) -ARRAY_FUNCS(keybinding_t *, keybinding, keybinding_unref) -DO_LUA_NEW(static, keybinding_t, keybinding, "keybinding", keybinding_ref) -DO_LUA_GC(keybinding_t, keybinding, "keybinding", keybinding_unref) - -static int -keybinding_ev_cmp(xcb_keysym_t keysym, xcb_keycode_t keycode, - unsigned long mod, const keybinding_t *k) -{ - if (k->keysym) { - if (k->keysym != keysym) - return k->keysym > keysym ? 1 : -1; - } - if (k->keycode) { - if (k->keycode != keycode) - return k->keycode > keycode ? 1 : -1; - } - return k->mod == mod ? 0 : (k->mod > mod ? 1 : -1); -} - -static int -keybinding_cmp(const keybinding_t *k1, const keybinding_t *k2) -{ - assert ((k1->keysym && k2->keysym) || (k1->keycode && k2->keycode)); - assert ((!k1->keysym && !k2->keysym) || (!k1->keycode && !k2->keycode)); - - if (k1->keysym != k2->keysym) - return k2->keysym > k1->keysym ? 1 : -1; - if (k1->keycode != k2->keycode) - return k2->keycode > k1->keycode ? 1 : -1; - return k1->mod == k2->mod ? 0 : (k2->mod > k1->mod ? 1 : -1); -} - -/** Grab key on the root windows. - * \param k The keybinding. - */ -void -window_root_grabkey(keybinding_t *k) -{ - int phys_screen = 0; - int nscreen = xcb_setup_roots_length(xcb_get_setup(globalconf.connection)); - xcb_screen_t *s; - xcb_keycode_t kc; - - if((kc = k->keycode) - || (k->keysym - && (kc = xcb_key_symbols_get_keycode(globalconf.keysyms, k->keysym)))) - do - { - s = xutil_screen_get(globalconf.connection, phys_screen); - xcb_grab_key(globalconf.connection, true, s->root, - k->mod, kc, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC); - xcb_grab_key(globalconf.connection, true, s->root, - k->mod | XCB_MOD_MASK_LOCK, kc, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC); - xcb_grab_key(globalconf.connection, true, s->root, - k->mod | globalconf.numlockmask, kc, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC); - xcb_grab_key(globalconf.connection, true, s->root, - k->mod | globalconf.numlockmask | XCB_MOD_MASK_LOCK, kc, XCB_GRAB_MODE_ASYNC, - XCB_GRAB_MODE_ASYNC); - phys_screen++; - } while(phys_screen < nscreen); -} - -/** Ungrab key on the root windows. - * \param k The keybinding. - */ -static void -window_root_ungrabkey(keybinding_t *k) -{ - int phys_screen = 0; - int nscreen = xcb_setup_roots_length(xcb_get_setup(globalconf.connection)); - xcb_screen_t *s; - xcb_keycode_t kc; - - if((kc = k->keycode) - || (k->keysym && (kc = xcb_key_symbols_get_keycode(globalconf.keysyms, k->keysym)))) - do - { - s = xutil_screen_get(globalconf.connection, phys_screen); - xcb_ungrab_key(globalconf.connection, kc, s->root, - k->mod); - xcb_ungrab_key(globalconf.connection, kc, s->root, - k->mod | XCB_MOD_MASK_LOCK); - xcb_ungrab_key(globalconf.connection, kc, s->root, - k->mod | globalconf.numlockmask); - xcb_ungrab_key(globalconf.connection, kc, s->root, - k->mod | globalconf.numlockmask | XCB_MOD_MASK_LOCK); - phys_screen++; - } while(phys_screen < nscreen); -} - -static void -keybinding_register_root(keybinding_t *k) -{ - keybinding_array_t *arr = k->keysym ? &globalconf.keys.by_sym : &globalconf.keys.by_code; - int l = 0, r = arr->len; - - keybinding_ref(&k); - - while (l < r) { - int i = (r + l) / 2; - switch (keybinding_cmp(k, arr->tab[i])) { - case -1: /* k < arr->tab[i] */ - r = i; - break; - case 0: /* k == arr->tab[i] */ - keybinding_unref(&arr->tab[i]); - arr->tab[i] = k; - return; - case 1: /* k > arr->tab[i] */ - l = i + 1; - break; - } - } - - keybinding_array_splice(arr, r, 0, &k, 1); - window_root_grabkey(k); -} - -static void -keybinding_unregister_root(keybinding_t **k) -{ - keybinding_array_t *arr = (*k)->keysym ? &globalconf.keys.by_sym : &globalconf.keys.by_code; - int l = 0, r = arr->len; - - while (l < r) { - int i = (r + l) / 2; - switch (keybinding_cmp(*k, arr->tab[i])) { - case -1: /* k < arr->tab[i] */ - r = i; - break; - case 0: /* k == arr->tab[i] */ - keybinding_array_take(arr, i); - window_root_ungrabkey(*k); - keybinding_unref(k); - return; - case 1: /* k > arr->tab[i] */ - l = i + 1; - break; - } - } -} - -/** Return the keysym from keycode. - * \param detail The keycode received. - * \param state The modifier state. - * \return A keysym. - */ -xcb_keysym_t -key_getkeysym(xcb_keycode_t detail, uint16_t state) -{ - xcb_keysym_t k0, k1; - - /* 'col' (third parameter) is used to get the proper KeySym - * according to modifier (XCB doesn't provide an equivalent to - * XLookupString()). - * - * If Mod5 is ON we look into second group. - */ - if(state & XCB_MOD_MASK_5) - { - k0 = xcb_key_symbols_get_keysym(globalconf.keysyms, detail, 2); - k1 = xcb_key_symbols_get_keysym(globalconf.keysyms, detail, 3); - } - else - { - k0 = xcb_key_symbols_get_keysym(globalconf.keysyms, detail, 0); - k1 = xcb_key_symbols_get_keysym(globalconf.keysyms, detail, 1); - } - - /* The numlock modifier is on and the second KeySym is a keypad - * KeySym */ - if((state & globalconf.numlockmask) && xcb_is_keypad_key(k1)) - { - /* The Shift modifier is on, or if the Lock modifier is on and - * is interpreted as ShiftLock, use the first KeySym */ - if((state & XCB_MOD_MASK_SHIFT) || - (state & XCB_MOD_MASK_LOCK && (state & globalconf.shiftlockmask))) - return k0; - else - return k1; - } - - /* The Shift and Lock modifers are both off, use the first - * KeySym */ - else if(!(state & XCB_MOD_MASK_SHIFT) && !(state & XCB_MOD_MASK_LOCK)) - return k0; - - /* The Shift modifier is off and the Lock modifier is on and is - * interpreted as CapsLock */ - else if(!(state & XCB_MOD_MASK_SHIFT) && - (state & XCB_MOD_MASK_LOCK && (state & globalconf.capslockmask))) - /* The first Keysym is used but if that KeySym is lowercase - * alphabetic, then the corresponding uppercase KeySym is used - * instead */ - return k1; - - /* The Shift modifier is on, and the Lock modifier is on and is - * interpreted as CapsLock */ - else if((state & XCB_MOD_MASK_SHIFT) && - (state & XCB_MOD_MASK_LOCK && (state & globalconf.capslockmask))) - /* The second Keysym is used but if that KeySym is lowercase - * alphabetic, then the corresponding uppercase KeySym is used - * instead */ - return k1; - - /* The Shift modifer is on, or the Lock modifier is on and is - * interpreted as ShiftLock, or both */ - else if((state & XCB_MOD_MASK_SHIFT) || - (state & XCB_MOD_MASK_LOCK && (state & globalconf.shiftlockmask))) - return k1; - - return XCB_NO_SYMBOL; -} - - -keybinding_t * -keybinding_find(const xcb_key_press_event_t *ev) -{ - const keybinding_array_t *arr = &globalconf.keys.by_sym; - int l, r, mod = XUTIL_MASK_CLEAN(ev->state); - xcb_keysym_t keysym; - - /* get keysym ignoring shift and mod5 */ - keysym = key_getkeysym(ev->detail, ev->state & ~(XCB_MOD_MASK_SHIFT | XCB_MOD_MASK_5 | XCB_MOD_MASK_LOCK)); - - again: - l = 0; - r = arr->len; - while (l < r) - { - int i = (r + l) / 2; - switch (keybinding_ev_cmp(keysym, ev->detail, mod, arr->tab[i])) - { - case -1: /* ev < arr->tab[i] */ - r = i; - break; - case 0: /* ev == arr->tab[i] */ - return arr->tab[i]; - case 1: /* ev > arr->tab[i] */ - l = i + 1; - break; - } - } - if (arr != &globalconf.keys.by_code) - { - arr = &globalconf.keys.by_code; - goto again; - } - return NULL; -} - -static void -luaA_keystore(keybinding_t *key, const char *str, ssize_t len) -{ - if(len) - { - if(*str != '#') - { - key->keysym = XStringToKeysym(str); - if (!key->keysym) - { - if (len == 1) - key->keysym = *str; - else - warn("there's no keysym named \"%s\"", str); - } - } - else - key->keycode = atoi(str + 1); - } -} +#include "key.h" /** Define a global key binding. This key binding will always be available. + * (DEPRECATED, see key). * \param L The Lua VM state. - * * \luastack * \lparam A table with modifier keys. * \lparam A key name. * \lparam A function to execute on key press. * \lparam A function to execute on key release. - * \lreturn The keybinding. + * \lreturn The key. */ static int luaA_keybinding_new(lua_State *L) { - size_t i, len; - keybinding_t *k; - const char *key; - luaA_ref press = LUA_REFNIL, release = LUA_REFNIL; - - /* arg 2 is key mod table */ - luaA_checktable(L, 2); - /* arg 3 is key */ - key = luaL_checklstring(L, 3, &len); - - if(!lua_isnil(L, 4)) - luaA_registerfct(L, 4, &press); - - if(lua_gettop(L) == 5 && !lua_isnil(L, 5)) - luaA_registerfct(L, 5, &release); - - /* get the last arg as function */ - k = p_new(keybinding_t, 1); - luaA_keystore(k, key, len); - - k->press = press; - k->release = release; - - len = lua_objlen(L, 2); - for(i = 1; i <= len; i++) - { - size_t blen; - lua_rawgeti(L, 2, i); - key = luaL_checklstring(L, -1, &blen); - k->mod |= xutil_key_mask_fromstr(key, blen); - } - - return luaA_keybinding_userdata_new(L, k); -} - -/** Add a global key binding. This key binding will always be available. - * \param L The Lua VM state. - * - * \luastack - * \lvalue A keybinding. - */ -static int -luaA_keybinding_add(lua_State *L) -{ - keybinding_t **k = luaA_checkudata(L, 1, "keybinding"); - keybinding_register_root(*k); - return 0; -} - -/** Remove a global key binding. - * \param L The Lua VM state. - * - * \luastack - * \lvalue A keybinding. - */ -static int -luaA_keybinding_remove(lua_State *L) -{ - keybinding_t **k = luaA_checkudata(L, 1, "keybinding"); - keybinding_unregister_root(k); - return 0; + luaA_deprecate(L, "key"); + return luaA_key_new(L); } const struct luaL_reg awesome_keybinding_methods[] = @@ -389,12 +43,3 @@ const struct luaL_reg awesome_keybinding_methods[] = { "__call", luaA_keybinding_new }, { NULL, NULL } }; -const struct luaL_reg awesome_keybinding_meta[] = -{ - { "add", luaA_keybinding_add }, - { "remove", luaA_keybinding_remove }, - { "__tostring", luaA_keybinding_tostring }, - { "__gc", luaA_keybinding_gc }, - { NULL, NULL }, -}; - diff --git a/keygrabber.c b/keygrabber.c index dc425bfc..e9ac9bbf 100644 --- a/keygrabber.c +++ b/keygrabber.c @@ -23,7 +23,7 @@ #include "structs.h" #include "keygrabber.h" -#include "keybinding.h" +#include "key.h" extern awesome_t globalconf; diff --git a/luaa.c b/luaa.c index ee3b5491..6e6a379c 100644 --- a/luaa.c +++ b/luaa.c @@ -73,8 +73,9 @@ extern const struct luaL_reg awesome_widget_methods[]; extern const struct luaL_reg awesome_widget_meta[]; extern const struct luaL_reg awesome_wibox_methods[]; extern const struct luaL_reg awesome_wibox_meta[]; +extern const struct luaL_reg awesome_key_methods[]; +extern const struct luaL_reg awesome_key_meta[]; extern const struct luaL_reg awesome_keybinding_methods[]; -extern const struct luaL_reg awesome_keybinding_meta[]; static struct sockaddr_un *addr; static ev_io csio = { .fd = -1 }; @@ -304,7 +305,7 @@ luaAe_type(lua_State *L) CHECK_TYPE(wibox); CHECK_TYPE(client); CHECK_TYPE(image); -CHECK_TYPE(keybinding); +CHECK_TYPE(key); CHECK_TYPE(button); CHECK_TYPE(tag); CHECK_TYPE(widget); @@ -836,7 +837,8 @@ luaA_init(void) luaA_openlib(L, "client", awesome_client_methods, awesome_client_meta); /* Export keys */ - luaA_openlib(L, "keybinding", awesome_keybinding_methods, awesome_keybinding_meta); + luaA_openlib(L, "key", awesome_key_methods, awesome_key_meta); + luaA_openlib(L, "keybinding", awesome_keybinding_methods, awesome_key_meta); lua_pushliteral(L, "AWESOME_VERSION"); lua_pushstring(L, AWESOME_VERSION); diff --git a/structs.h b/structs.h index d64e8db6..97beb40a 100644 --- a/structs.h +++ b/structs.h @@ -28,7 +28,7 @@ #include "config.h" #include "luaa.h" #include "swindow.h" -#include "keybinding.h" +#include "key.h" #include "common/xutil.h" #include "common/xembed.h" #include "common/refcount.h" @@ -293,8 +293,8 @@ struct awesome_t /** Key bindings */ struct { - keybinding_array_t by_code; - keybinding_array_t by_sym; + key_array_t by_code; + key_array_t by_sym; } keys; /** Mouse bindings list */ button_array_t buttons;