From 3a1c9e0cfb14035e7832f89c6f6fb475f79a7bfd Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Wed, 25 Jun 2008 00:08:20 +0200 Subject: [PATCH 1/6] Calling destructor on splice is wrong, take doesn't want it e.g. Also fix one assert. Signed-off-by: Pierre Habouzit --- common/array.h | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/common/array.h b/common/array.h index d6ddc9c16..04fa219fa 100644 --- a/common/array.h +++ b/common/array.h @@ -59,12 +59,9 @@ type_t items[], int count) \ { \ assert (pos >= 0 && len >= 0 && count >= 0); \ - assert (pos <= arr->len && pos + len < arr->len); \ + assert (pos <= arr->len && pos + len <= arr->len); \ if (len != count) { \ pfx##_array_grow(arr, arr->len + count - len); \ - for (int i = pos; i < pos + len; i++) { \ - dtor(&arr->tab[i]); \ - } \ memmove(arr->tab + pos + count, arr->tab + pos + len, \ (arr->len - pos - len) * sizeof(*items)); \ arr->len += count - len; \ From 8c1aec2e96ab913778b56dc8a9aefa6b6d36a9c7 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Wed, 25 Jun 2008 00:08:40 +0200 Subject: [PATCH 2/6] Put keybindings in arrays. Signed-off-by: Pierre Habouzit --- event.c | 6 ++++-- keybinding.c | 23 ++++++++++++++++------- structs.h | 10 +++++----- 3 files changed, 25 insertions(+), 14 deletions(-) diff --git a/event.c b/event.c index f9d650405..057fd77a6 100644 --- a/event.c +++ b/event.c @@ -391,7 +391,6 @@ event_handle_keypress(void *data __attribute__ ((unused)), xcb_key_press_event_t *ev) { xcb_keysym_t keysym; - keybinding_t *k; if(globalconf.keygrabber != LUA_REFNIL) { @@ -412,10 +411,13 @@ event_handle_keypress(void *data __attribute__ ((unused)), { keysym = xcb_key_symbols_get_keysym(globalconf.keysyms, ev->detail, 0); - for(k = globalconf.keys; k; k = k->next) + for(int i = 0; i < globalconf.keys.len; i++) + { + keybinding_t *k = globalconf.keys.tab[i]; if(((k->keycode && ev->detail == k->keycode) || (k->keysym && keysym == k->keysym)) && k->fct && CLEANMASK(k->mod) == CLEANMASK(ev->state)) luaA_dofunction(globalconf.L, k->fct, 0); + } } return 0; diff --git a/keybinding.c b/keybinding.c index 788189eaf..94711d1c1 100644 --- a/keybinding.c +++ b/keybinding.c @@ -31,6 +31,11 @@ extern awesome_t globalconf; DO_LUA_NEW(static, keybinding_t, keybinding, "keybinding", keybinding_ref) DO_LUA_GC(keybinding_t, keybinding, "keybinding", keybinding_unref) +void keybinding_delete(keybinding_t **kbp) +{ + p_delete(kbp); +} + static void __luaA_keystore(keybinding_t *key, const char *str) { @@ -96,14 +101,14 @@ luaA_keybinding_new(lua_State *L) static int luaA_keybinding_add(lua_State *L) { - keybinding_t *key, **k = luaA_checkudata(L, 1, "keybinding"); + keybinding_t **k = luaA_checkudata(L, 1, "keybinding"); /* Check that the keybinding has not been already added. */ - for(key = globalconf.keys; key; key = key->next) - if(key == *k) + for(int i = 0; i < globalconf.keys.len; i++) + if(globalconf.keys.tab[i] == *k) luaL_error(L, "keybinding already added"); - keybinding_list_push(&globalconf.keys, keybinding_ref(k)); + keybinding_array_append(&globalconf.keys, keybinding_ref(k)); window_root_grabkey(*k); return 0; @@ -120,9 +125,13 @@ luaA_keybinding_remove(lua_State *L) { keybinding_t **k = luaA_checkudata(L, 1, "keybinding"); - keybinding_list_detach(&globalconf.keys, *k); - window_root_ungrabkey(*k); - keybinding_unref(k); + for(int i = 0; i < globalconf.keys.len; i++) + if(globalconf.keys.tab[i] == *k) + { + keybinding_array_take(&globalconf.keys, i); + window_root_ungrabkey(*k); + keybinding_unref(k); + } return 0; } diff --git a/structs.h b/structs.h index a31fabce0..70c62a7e9 100644 --- a/structs.h +++ b/structs.h @@ -206,6 +206,7 @@ titlebar_delete(titlebar_t **t) DO_RCNT(titlebar_t, titlebar, titlebar_delete) /** Keys bindings */ +ARRAY_TYPE(struct keybinding_t *, keybinding); struct keybinding_t { /** Ref count */ @@ -218,12 +219,11 @@ struct keybinding_t xcb_keycode_t keycode; /** Lua function to execute. */ luaA_function fct; - /** Next and previous keys */ - keybinding_t *prev, *next; }; -DO_SLIST(keybinding_t, keybinding, p_delete) -DO_RCNT(keybinding_t, keybinding, p_delete) +void keybinding_delete(keybinding_t **); +DO_RCNT(keybinding_t, keybinding, keybinding_delete) +ARRAY_FUNCS(keybinding_t *, keybinding, keybinding_unref) /** Status bar */ struct statusbar_t @@ -399,7 +399,7 @@ struct awesome_t /** Screens info */ screens_info_t *screens_info; /** Keys bindings list */ - keybinding_t *keys; + keybinding_array_t keys; /** Mouse bindings list */ struct { From 4c288c006b291f4073ada29aeba8954658413325 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Wed, 25 Jun 2008 01:00:25 +0200 Subject: [PATCH 3/6] Put keybindings in binary searchable arrays. This patch sorts keybindings in arrays by keycode or keysym to speed up look up using binary searches. This is a preliminary work to enable more powerful keybindings stuff, where keybindings can be cascaded or why not, attached to specific clients. Interstingly enough, this patch saves 100ko of initial memory (Heap) usage here. The underlying idea is that we should be able to define keybindings_t as trees of keybindings_t which would then define key sequences. The OO approach kind of make sense in fact, since you create a base keybinding (e.g. reacting on Mod4-w) and then you will probably (with appropriate apis) be able to populate new submaps from that point more or less dynamically. And if you have two keybindings on Mod4-w, then adding them will replace the previous one. This means that you can fake per-client bindings with e.g.: k_default = keybindings.new({"Mod4"}, "w", something); k_mplayer = keybindings.new({"Mod4"}, "w", something_else); k_default:add() and in your focus hook: if /* code for testing if it's mplayer */ then k_mplayer:add() else k_default:add() end This would not work before, it does now. It will take way more sense with submaps of course. Signed-off-by: Pierre Habouzit --- event.c | 15 ++--- keybinding.c | 154 ++++++++++++++++++++++++++++++++++++++++++--------- keybinding.h | 38 +++++++++++++ structs.h | 12 ++-- 4 files changed, 176 insertions(+), 43 deletions(-) create mode 100644 keybinding.h diff --git a/event.c b/event.c index 057fd77a6..676c96ba5 100644 --- a/event.c +++ b/event.c @@ -33,6 +33,7 @@ #include "client.h" #include "widget.h" #include "titlebar.h" +#include "keybinding.h" #include "keygrabber.h" #include "lua.h" #include "systray.h" @@ -390,8 +391,6 @@ event_handle_keypress(void *data __attribute__ ((unused)), xcb_connection_t *connection __attribute__ ((unused)), xcb_key_press_event_t *ev) { - xcb_keysym_t keysym; - if(globalconf.keygrabber != LUA_REFNIL) { lua_rawgeti(globalconf.L, LUA_REGISTRYINDEX, globalconf.keygrabber); @@ -409,15 +408,9 @@ event_handle_keypress(void *data __attribute__ ((unused)), } else { - keysym = xcb_key_symbols_get_keysym(globalconf.keysyms, ev->detail, 0); - - for(int i = 0; i < globalconf.keys.len; i++) - { - keybinding_t *k = globalconf.keys.tab[i]; - if(((k->keycode && ev->detail == k->keycode) || (k->keysym && keysym == k->keysym)) - && k->fct && CLEANMASK(k->mod) == CLEANMASK(ev->state)) - luaA_dofunction(globalconf.L, k->fct, 0); - } + keybinding_t *k = keybinding_find(&globalconf.keys, ev); + if (k && k->fct) + luaA_dofunction(globalconf.L, k->fct, 0); } return 0; diff --git a/keybinding.c b/keybinding.c index 94711d1c1..7a16121df 100644 --- a/keybinding.c +++ b/keybinding.c @@ -2,6 +2,7 @@ * 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 @@ -22,7 +23,8 @@ /* XStringToKeysym() */ #include -#include "structs.h" +#include "keybinding.h" +#include "event.h" #include "lua.h" #include "window.h" @@ -31,27 +33,140 @@ extern awesome_t globalconf; DO_LUA_NEW(static, keybinding_t, keybinding, "keybinding", keybinding_ref) DO_LUA_GC(keybinding_t, keybinding, "keybinding", keybinding_unref) +void keybinding_idx_wipe(keybinding_idx_t *idx) +{ + keybinding_array_wipe(&idx->by_code); + keybinding_array_wipe(&idx->by_sym); +} + void keybinding_delete(keybinding_t **kbp) { p_delete(kbp); } +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); +} + +void +keybinding_register_root(keybinding_t *k) +{ + keybinding_idx_t *idx = &globalconf.keys; + keybinding_array_t *arr = k->keysym ? &idx->by_sym : &idx->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); +} + +void +keybinding_unregister_root(keybinding_t **k) +{ + keybinding_idx_t *idx = &globalconf.keys; + keybinding_array_t *arr = (*k)->keysym ? &idx->by_sym : &idx->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; + } + } +} + +keybinding_t * +keybinding_find(const keybinding_idx_t *idx, const xcb_key_press_event_t *ev) +{ + const keybinding_array_t *arr = &idx->by_sym; + int l, r, mod = CLEANMASK(ev->state); + xcb_keysym_t keysym; + + keysym = xcb_key_symbols_get_keysym(globalconf.keysyms, ev->detail, 0); + + 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 != &idx->by_code) { + arr = &idx->by_code; + goto again; + } + return NULL; +} + static void __luaA_keystore(keybinding_t *key, const char *str) { - xcb_keycode_t kc; - int ikc; - if(!a_strlen(str)) return; - else if(a_strncmp(str, "#", 1)) + else if(*str != '#') key->keysym = XStringToKeysym(str); else - { - ikc = atoi(str + 1); - memcpy(&kc, &ikc, sizeof(KeyCode)); - key->keycode = kc; - } + key->keycode = atoi(str + 1); } /** Define a global key binding. This key binding will always be available. @@ -103,14 +218,7 @@ luaA_keybinding_add(lua_State *L) { keybinding_t **k = luaA_checkudata(L, 1, "keybinding"); - /* Check that the keybinding has not been already added. */ - for(int i = 0; i < globalconf.keys.len; i++) - if(globalconf.keys.tab[i] == *k) - luaL_error(L, "keybinding already added"); - - keybinding_array_append(&globalconf.keys, keybinding_ref(k)); - window_root_grabkey(*k); - + keybinding_register_root(*k); return 0; } @@ -124,15 +232,7 @@ static int luaA_keybinding_remove(lua_State *L) { keybinding_t **k = luaA_checkudata(L, 1, "keybinding"); - - for(int i = 0; i < globalconf.keys.len; i++) - if(globalconf.keys.tab[i] == *k) - { - keybinding_array_take(&globalconf.keys, i); - window_root_ungrabkey(*k); - keybinding_unref(k); - } - + keybinding_unregister_root(k); return 0; } diff --git a/keybinding.h b/keybinding.h new file mode 100644 index 000000000..ee0f5032e --- /dev/null +++ b/keybinding.h @@ -0,0 +1,38 @@ +/* + * keybinding.h - Keybinding helpers + * + * 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. + * + */ + +#ifndef AWESOME_KEYBINDING_H +#define AWESOME_KEYBINDING_H + +#include "structs.h" + +void keybinding_delete(keybinding_t **); +DO_RCNT(keybinding_t, keybinding, keybinding_delete) +ARRAY_FUNCS(keybinding_t *, keybinding, keybinding_unref) + +void keybinding_idx_wipe(keybinding_idx_t *); + +void keybinding_register_root(keybinding_t *); +void keybinding_unregiste_rootr(keybinding_t **); +keybinding_t *keybinding_find(const keybinding_idx_t *, + const xcb_key_press_event_t *); + +#endif diff --git a/structs.h b/structs.h index 70c62a7e9..8450e7363 100644 --- a/structs.h +++ b/structs.h @@ -207,6 +207,12 @@ DO_RCNT(titlebar_t, titlebar, titlebar_delete) /** Keys bindings */ ARRAY_TYPE(struct keybinding_t *, keybinding); + +typedef struct keybinding_idx_t { + keybinding_array_t by_code; + keybinding_array_t by_sym; +} keybinding_idx_t; + struct keybinding_t { /** Ref count */ @@ -221,10 +227,6 @@ struct keybinding_t luaA_function fct; }; -void keybinding_delete(keybinding_t **); -DO_RCNT(keybinding_t, keybinding, keybinding_delete) -ARRAY_FUNCS(keybinding_t *, keybinding, keybinding_unref) - /** Status bar */ struct statusbar_t { @@ -399,7 +401,7 @@ struct awesome_t /** Screens info */ screens_info_t *screens_info; /** Keys bindings list */ - keybinding_array_t keys; + keybinding_idx_t keys; /** Mouse bindings list */ struct { From d3b030aa2ce517b162e9ff7f43602e79307c9e0f Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Wed, 25 Jun 2008 01:44:21 +0200 Subject: [PATCH 4/6] More ignores. Signed-off-by: Pierre Habouzit --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index e012fecc4..a67dc0fae 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +.tags +.version_stamp .*.sw? *.luac *.o From 0f23ce88cbef8eb124135557e874ed382d2f296a Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Wed, 25 Jun 2008 11:42:06 +0200 Subject: [PATCH 5/6] Divert lua string.len to a multibyte aware implementation. Signed-off-by: Pierre Habouzit --- lua.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/lua.c b/lua.c index 36de23aaa..a48f3a64e 100644 --- a/lua.c +++ b/lua.c @@ -453,6 +453,23 @@ luaA_openlib(lua_State *L, const char *name, luaL_register(L, name, methods); } +static int +luaA_mbstrlen(lua_State *L) +{ + const char *cmd = luaL_checkstring(L, 1); + lua_pushnumber(L, mbstowcs(NULL, NONULL(cmd), 0)); + return 1; +} + +static void +luaA_fixups(lua_State *L) +{ + lua_getglobal(L, "string"); + lua_pushcfunction(L, luaA_mbstrlen); + lua_setfield(L, -2, "len"); + lua_pop(L, 1); +} + /** Initialize the Lua VM */ void @@ -497,6 +514,8 @@ luaA_init(void) luaL_openlibs(L); + luaA_fixups(L); + /* Export awesome lib */ luaL_register(L, "awesome", awesome_lib); From ead90f618c46f7296afcbc5c268ce638efd58ef6 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Wed, 25 Jun 2008 11:45:57 +0200 Subject: [PATCH 6/6] Ignore keys that have a multibyte length of more than 1. Signed-off-by: Pierre Habouzit --- lib/awful.lua | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/awful.lua b/lib/awful.lua index a053dc2bd..1390ef748 100644 --- a/lib/awful.lua +++ b/lib/awful.lua @@ -15,6 +15,7 @@ else end -- Grab environment we need +local string = string local assert = assert local loadstring = loadstring local ipairs = ipairs @@ -667,8 +668,10 @@ function P.menu(args, textbox, exe_callback, completion_callback) elseif key == "Right" then cur_pos = cur_pos + 1 else - command = command:sub(1, cur_pos - 1) .. key .. command:sub(cur_pos) - cur_pos = cur_pos + 1 + if string.len(key) == 1 then + command = command:sub(1, cur_pos - 1) .. key .. command:sub(cur_pos) + cur_pos = cur_pos + 1 + end end if cur_pos < 1 then cur_pos = 1