keybinding: move to key

Signed-off-by: Julien Danjou <julien@danjou.info>
This commit is contained in:
Julien Danjou 2008-12-18 11:33:19 +01:00
parent fa41e0d395
commit 53f58aa1a6
9 changed files with 521 additions and 469 deletions

View File

@ -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

View File

@ -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
-- }}}

View File

@ -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]);

400
key.c Normal file
View File

@ -0,0 +1,400 @@
/*
* key.c - Key bindings configuration management
*
* Copyright © 2008 Julien Danjou <julien@danjou.info>
* Copyright © 2008 Pierre Habouzit <madcoder@debian.org>
*
* 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 <X11/Xlib.h>
#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 },
};

View File

@ -1,5 +1,5 @@
/*
* keybinding.h - Keybinding helpers
* key.h - Keybinding helpers
*
* Copyright © 2008 Pierre Habouzit <madcoder@debian.org>
*
@ -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

View File

@ -2,7 +2,6 @@
* keybinding.c - Key bindings configuration management
*
* Copyright © 2008 Julien Danjou <julien@danjou.info>
* Copyright © 2008 Pierre Habouzit <madcoder@debian.org>
*
* 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 <X11/Xlib.h>
#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 },
};

View File

@ -23,7 +23,7 @@
#include "structs.h"
#include "keygrabber.h"
#include "keybinding.h"
#include "key.h"
extern awesome_t globalconf;

8
luaa.c
View File

@ -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);

View File

@ -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;