Merge pull request #2521 from Elv13/modifiers
awesome: Add a `modifiers` field.
This commit is contained in:
commit
7e7733c6d1
|
@ -9,15 +9,12 @@
|
|||
-- Grab environment we need
|
||||
local setmetatable = setmetatable
|
||||
local ipairs = ipairs
|
||||
local capi = { key = key, root = root }
|
||||
local capi = { key = key, root = root, awesome = awesome }
|
||||
local gmath = require("gears.math")
|
||||
local gtable = require("gears.table")
|
||||
|
||||
|
||||
|
||||
local key = { mt = {}, hotkeys = {} }
|
||||
|
||||
|
||||
--- Modifiers to ignore.
|
||||
-- By default this is initialized as { "Lock", "Mod2" }
|
||||
-- so the Caps Lock or Num Lock modifier are not taking into account by awesome
|
||||
|
@ -27,23 +24,53 @@ local key = { mt = {}, hotkeys = {} }
|
|||
key.ignore_modifiers = { "Lock", "Mod2" }
|
||||
|
||||
--- Convert the modifiers into pc105 key names
|
||||
local conversion = {
|
||||
mod4 = "Super_L",
|
||||
control = "Control_L",
|
||||
shift = "Shift_L",
|
||||
mod1 = "Alt_L",
|
||||
}
|
||||
local conversion = nil
|
||||
|
||||
local function generate_conversion_map()
|
||||
if conversion then return conversion end
|
||||
|
||||
local mods = capi.awesome._modifiers
|
||||
assert(mods)
|
||||
|
||||
conversion = {}
|
||||
|
||||
for mod, keysyms in pairs(mods) do
|
||||
for _, keysym in ipairs(keysyms) do
|
||||
assert(keysym.keysym)
|
||||
conversion[mod] = conversion[mod] or keysym.keysym
|
||||
conversion[keysym.keysym] = mod
|
||||
end
|
||||
end
|
||||
|
||||
return conversion
|
||||
end
|
||||
|
||||
capi.awesome.connect_signal("xkb::map_changed" , function() conversion = nil end)
|
||||
|
||||
--- Execute a key combination.
|
||||
-- If an awesome keybinding is assigned to the combination, it should be
|
||||
-- executed.
|
||||
--
|
||||
-- To limit the chances of accidentally leaving a modifier key locked when
|
||||
-- calling this function from a keybinding, make sure is attached to the
|
||||
-- release event and not the press event.
|
||||
--
|
||||
-- @see root.fake_input
|
||||
-- @tparam table mod A modified table. Valid modifiers are: Any, Mod1,
|
||||
-- Mod2, Mod3, Mod4, Mod5, Shift, Lock and Control.
|
||||
-- @tparam string k The key
|
||||
function key.execute(mod, k)
|
||||
local modmap = generate_conversion_map()
|
||||
local active = capi.awesome._active_modifiers
|
||||
|
||||
-- Release all modifiers
|
||||
for _, m in ipairs(active) do
|
||||
assert(modmap[m])
|
||||
root.fake_input("key_release", modmap[m])
|
||||
end
|
||||
|
||||
for _, v in ipairs(mod) do
|
||||
local m = conversion[v:lower()]
|
||||
local m = modmap[v]
|
||||
if m then
|
||||
root.fake_input("key_press", m)
|
||||
end
|
||||
|
@ -53,11 +80,18 @@ function key.execute(mod, k)
|
|||
root.fake_input("key_release", k)
|
||||
|
||||
for _, v in ipairs(mod) do
|
||||
local m = conversion[v:lower()]
|
||||
local m = modmap[v]
|
||||
if m then
|
||||
root.fake_input("key_release", m)
|
||||
end
|
||||
end
|
||||
|
||||
-- Restore the previous modifiers all modifiers. Please note that yes,
|
||||
-- there is a race condition if the user was fast enough to release the
|
||||
-- key during this operation.
|
||||
for _, m in ipairs(active) do
|
||||
root.fake_input("key_press", modmap[m])
|
||||
end
|
||||
end
|
||||
|
||||
--- Create a new key to use as binding.
|
||||
|
|
|
@ -72,7 +72,7 @@ local gtable = require("gears.table")
|
|||
local gobject = require("gears.object")
|
||||
local gtimer = require("gears.timer")
|
||||
local glib = require("lgi").GLib
|
||||
local capi = { keygrabber = keygrabber, root = root }
|
||||
local capi = { keygrabber = keygrabber, root = root, awesome = awesome }
|
||||
|
||||
local keygrab = {}
|
||||
|
||||
|
@ -85,17 +85,7 @@ local keygrabber = {
|
|||
}
|
||||
|
||||
-- Instead of checking for every modifiers, check the key directly.
|
||||
--FIXME This is slightly broken but still good enough for `mask_modkeys`
|
||||
local conversion = {
|
||||
Super_L = "Mod4",
|
||||
Control_L = "Control",
|
||||
Shift_L = "Shift",
|
||||
Alt_L = "Mod1",
|
||||
Super_R = "Mod4",
|
||||
Control_R = "Control",
|
||||
Shift_R = "Shift",
|
||||
Alt_R = "Mod1",
|
||||
}
|
||||
local conversion = nil
|
||||
|
||||
--BEGIN one day create a proper API to add and remove keybindings at runtime.
|
||||
-- Doing it this way is horrible.
|
||||
|
@ -103,6 +93,27 @@ local conversion = {
|
|||
-- This list of keybindings to add in the next event loop cycle.
|
||||
local delay_list = {}
|
||||
|
||||
-- Read the modifiers name and map their keysyms to the modkeys
|
||||
local function generate_conversion_map()
|
||||
if conversion then return conversion end
|
||||
|
||||
local mods = capi.awesome._modifiers
|
||||
assert(mods)
|
||||
|
||||
conversion = {}
|
||||
|
||||
for mod, keysyms in pairs(mods) do
|
||||
for _, keysym in ipairs(keysyms) do
|
||||
assert(keysym.keysym)
|
||||
conversion[keysym.keysym] = mod
|
||||
end
|
||||
end
|
||||
|
||||
return conversion
|
||||
end
|
||||
|
||||
capi.awesome.connect_signal("xkb::map_changed" , function() conversion = nil end)
|
||||
|
||||
local function add_root_keybindings(self, list)
|
||||
assert(
|
||||
list, "`add_root_keybindings` needs to be called with a list of keybindings"
|
||||
|
@ -168,9 +179,11 @@ local function grabber(mod, key, event)
|
|||
end
|
||||
|
||||
local function runner(self, modifiers, key, event)
|
||||
local converted = generate_conversion_map()[key]
|
||||
|
||||
-- Stop the keygrabber with the `stop_key`
|
||||
if key == self.stop_key
|
||||
and event == self.stop_event and self.stop_key then
|
||||
if (key == self.stop_key or (converted and converted == self.stop_key))
|
||||
and event == self.stop_event and self.stop_key then
|
||||
self:stop(key, modifiers)
|
||||
return false
|
||||
end
|
||||
|
@ -191,7 +204,7 @@ local function runner(self, modifiers, key, event)
|
|||
end
|
||||
end
|
||||
|
||||
local is_modifier = conversion[key] ~= nil
|
||||
local is_modifier = converted ~= nil
|
||||
|
||||
-- Reset the inactivity timer on each events.
|
||||
if self._private.timer and self._private.timer.started then
|
||||
|
|
157
luaa.c
157
luaa.c
|
@ -388,6 +388,151 @@ luaA_fixups(lua_State *L)
|
|||
lua_setglobal(L, "selection");
|
||||
}
|
||||
|
||||
static const char *
|
||||
get_modifier_name(int map_index)
|
||||
{
|
||||
switch (map_index) {
|
||||
case XCB_MAP_INDEX_SHIFT: return "Shift";
|
||||
case XCB_MAP_INDEX_LOCK: return "Lock";
|
||||
case XCB_MAP_INDEX_CONTROL: return "Control";
|
||||
case XCB_MAP_INDEX_1: return "Mod1"; /* Alt */
|
||||
case XCB_MAP_INDEX_2: return "Mod2";
|
||||
case XCB_MAP_INDEX_3: return "Mod3";
|
||||
case XCB_MAP_INDEX_4: return "Mod4";
|
||||
case XCB_MAP_INDEX_5: return "Mod5";
|
||||
|
||||
}
|
||||
|
||||
return 0; /* \0 */
|
||||
}
|
||||
|
||||
/* Undocumented */
|
||||
/*
|
||||
* The table of keybindings modifiers.
|
||||
*
|
||||
* Each modifier has zero to many entries depending on the keyboard layout.
|
||||
* For example, `Shift` usually both has a left and right variant. Each
|
||||
* modifier entry has a `keysym` and `keycode` entry. For the US PC 105
|
||||
* keyboard, it looks like:
|
||||
*
|
||||
* awesome.modifiers = {
|
||||
* Shift = {
|
||||
* {keycode = 50 , keysym = 'Shift_L' },
|
||||
* {keycode = 62 , keysym = 'Shift_R' },
|
||||
* },
|
||||
* Lock = {},
|
||||
* Control = {
|
||||
* {keycode = 37 , keysym = 'Control_L' },
|
||||
* {keycode = 105, keysym = 'Control_R' },
|
||||
* },
|
||||
* Mod1 = {
|
||||
* {keycode = 64 , keysym = 'Alt_L' },
|
||||
* {keycode = 108, keysym = 'Alt_R' },
|
||||
* },
|
||||
* Mod2 = {
|
||||
* {keycode = 77 , keysym = 'Num_Lock' },
|
||||
* },
|
||||
* Mod3 = {},
|
||||
* Mod4 = {
|
||||
* {keycode = 133, keysym = 'Super_L' },
|
||||
* {keycode = 134, keysym = 'Super_R' },
|
||||
* },
|
||||
* Mod5 = {
|
||||
* {keycode = 203, keysym = 'Mode_switch'},
|
||||
* },
|
||||
* };
|
||||
*
|
||||
* @tfield table modifiers
|
||||
* @tparam table modifiers.Shift The Shift modifiers.
|
||||
* @tparam table modifiers.Lock The Lock modifiers.
|
||||
* @tparam table modifiers.Control The Control modifiers.
|
||||
* @tparam table modifiers.Mod1 The Mod1 (Alt) modifiers.
|
||||
* @tparam table modifiers.Mod2 The Mod2 modifiers.
|
||||
* @tparam table modifiers.Mod3 The Mod3 modifiers.
|
||||
* @tparam table modifiers.Mod4 The Mod4 modifiers.
|
||||
* @tparam table modifiers.Mod5 The Mod5 modifiers.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Modifiers can change over time, given they are currently not tracked, just
|
||||
* query them each time. Use with moderation.
|
||||
*/
|
||||
static int luaA_get_modifiers(lua_State *L)
|
||||
{
|
||||
xcb_get_modifier_mapping_reply_t *mods = xcb_get_modifier_mapping_reply(globalconf.connection,
|
||||
xcb_get_modifier_mapping(globalconf.connection), NULL);
|
||||
if (!mods)
|
||||
return 0;
|
||||
|
||||
xcb_keycode_t *mappings = xcb_get_modifier_mapping_keycodes(mods);
|
||||
struct xkb_keymap *keymap = xkb_state_get_keymap(globalconf.xkb_state);
|
||||
|
||||
lua_newtable(L);
|
||||
|
||||
/* This get the MAPPED modifiers, not all of them are */
|
||||
for (int i = XCB_MAP_INDEX_SHIFT; i <= XCB_MAP_INDEX_5; i++) {
|
||||
lua_pushstring(L, get_modifier_name(i));
|
||||
lua_newtable(L);
|
||||
|
||||
for (int j = 0; j < mods->keycodes_per_modifier; j++) {
|
||||
const xkb_keysym_t *keysyms;
|
||||
const xcb_keycode_t key_code = mappings[i * mods->keycodes_per_modifier + j];
|
||||
xkb_keymap_key_get_syms_by_level(keymap, key_code, 0, 0, &keysyms);
|
||||
if (keysyms != NULL) {
|
||||
/* The +1 because j starts at zero and Lua at 1 */
|
||||
lua_pushinteger(L, j+1);
|
||||
|
||||
lua_newtable(L);
|
||||
|
||||
lua_pushstring(L, "keycode");
|
||||
lua_pushinteger(L, key_code);
|
||||
lua_settable(L, -3);
|
||||
|
||||
/* Technically it is possible to get multiple keysyms here,
|
||||
* but... we just use the first one.
|
||||
*/
|
||||
lua_pushstring(L, "keysym");
|
||||
char *string = key_get_keysym_name(keysyms[0]);
|
||||
lua_pushstring(L, string);
|
||||
p_delete(&string);
|
||||
lua_settable(L, -3);
|
||||
|
||||
lua_settable(L, -3);
|
||||
}
|
||||
}
|
||||
lua_settable(L, -3);
|
||||
}
|
||||
|
||||
free(mods);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Undocumented */
|
||||
/*
|
||||
* A table with the currently active modifier names.
|
||||
*
|
||||
* @tfield table _active_modifiers
|
||||
*/
|
||||
|
||||
static int luaA_get_active_modifiers(lua_State *L)
|
||||
{
|
||||
int count = 1;
|
||||
lua_newtable(L);
|
||||
|
||||
for (int i = XCB_MAP_INDEX_SHIFT; i <= XCB_MAP_INDEX_5; i++) {
|
||||
const int active = xkb_state_mod_index_is_active (globalconf.xkb_state, i,
|
||||
XKB_STATE_MODS_DEPRESSED | XKB_STATE_MODS_EFFECTIVE
|
||||
);
|
||||
|
||||
if (active) {
|
||||
lua_pushstring(L, get_modifier_name(i));
|
||||
lua_rawseti(L,-2, count++);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* The version of awesome.
|
||||
|
@ -472,6 +617,18 @@ luaA_awesome_index(lua_State *L)
|
|||
return 1;
|
||||
}
|
||||
|
||||
if(A_STREQ(buf, "_modifiers"))
|
||||
{
|
||||
luaA_get_modifiers(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(A_STREQ(buf, "_active_modifiers"))
|
||||
{
|
||||
luaA_get_active_modifiers(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(A_STREQ(buf, "startup_errors"))
|
||||
{
|
||||
if (globalconf.startup_errors.len == 0)
|
||||
|
|
|
@ -275,8 +275,8 @@ luaA_key_set_modifiers(lua_State *L, keyb_t *k)
|
|||
LUA_OBJECT_EXPORT_PROPERTY(key, keyb_t, modifiers, luaA_pushmodifiers)
|
||||
|
||||
/* It's caller's responsibility to release the returned string. */
|
||||
static char *
|
||||
get_keysym_name(xkb_keysym_t keysym)
|
||||
char *
|
||||
key_get_keysym_name(xkb_keysym_t keysym)
|
||||
{
|
||||
const ssize_t bufsize = 64;
|
||||
char *buf = p_new(char, bufsize);
|
||||
|
@ -310,7 +310,7 @@ luaA_key_get_key(lua_State *L, keyb_t *k)
|
|||
}
|
||||
else
|
||||
{
|
||||
char *name = get_keysym_name(k->keysym);
|
||||
char *name = key_get_keysym_name(k->keysym);
|
||||
if(!name)
|
||||
return 0;
|
||||
lua_pushstring(L, name);
|
||||
|
@ -322,7 +322,7 @@ luaA_key_get_key(lua_State *L, keyb_t *k)
|
|||
static int
|
||||
luaA_key_get_keysym(lua_State *L, keyb_t *k)
|
||||
{
|
||||
char *name = get_keysym_name(k->keysym);
|
||||
char *name = key_get_keysym_name(k->keysym);
|
||||
if(!name)
|
||||
return 0;
|
||||
lua_pushstring(L, name);
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#define AWESOME_OBJECTS_KEY_H
|
||||
|
||||
#include "common/luaobject.h"
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
|
||||
typedef struct keyb_t
|
||||
{
|
||||
|
@ -47,6 +48,8 @@ int luaA_key_array_get(lua_State *, int, key_array_t *);
|
|||
int luaA_pushmodifiers(lua_State *, uint16_t);
|
||||
uint16_t luaA_tomodifiers(lua_State *L, int ud);
|
||||
|
||||
char * key_get_keysym_name(xkb_keysym_t keysym);
|
||||
|
||||
#endif
|
||||
|
||||
// vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
||||
|
|
|
@ -66,6 +66,35 @@ awesome.version = "v9999"
|
|||
-- SVG are composited. Without it we need a root surface
|
||||
awesome.composite_manager_running = true
|
||||
|
||||
awesome._modifiers = {
|
||||
Shift = {
|
||||
{keycode = 50 , keysym = 'Shift_L' },
|
||||
{keycode = 62 , keysym = 'Shift_R' },
|
||||
},
|
||||
Lock = {},
|
||||
Control = {
|
||||
{keycode = 37 , keysym = 'Control_L' },
|
||||
{keycode = 105, keysym = 'Control_R' },
|
||||
},
|
||||
Mod1 = {
|
||||
{keycode = 64 , keysym = 'Alt_L' },
|
||||
{keycode = 108, keysym = 'Alt_R' },
|
||||
},
|
||||
Mod2 = {
|
||||
{keycode = 77 , keysym = 'Num_Lock' },
|
||||
},
|
||||
Mod3 = {},
|
||||
Mod4 = {
|
||||
{keycode = 133, keysym = 'Super_L' },
|
||||
{keycode = 134, keysym = 'Super_R' },
|
||||
},
|
||||
Mod5 = {
|
||||
{keycode = 203, keysym = 'Mode_switch'},
|
||||
},
|
||||
}
|
||||
|
||||
awesome._active_modifiers = {}
|
||||
|
||||
return awesome
|
||||
|
||||
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
||||
|
|
|
@ -16,7 +16,7 @@ local awful = {keygrabber = require("awful.keygrabber"), --DOC_HIDE
|
|||
{{"Mod1", "Shift"}, "Tab", awful.client.focus.history.select_next },
|
||||
},
|
||||
-- Note that it is using the key name and not the modifier name.
|
||||
stop_key = "Alt_L",
|
||||
stop_key = "Mod1",
|
||||
stop_event = "release",
|
||||
start_callback = awful.client.focus.history.disable_tracking,
|
||||
stop_callback = awful.client.focus.history.enable_tracking,
|
||||
|
|
|
@ -9,6 +9,10 @@ local done
|
|||
local timer = GLib.Timer()
|
||||
|
||||
local steps = {
|
||||
function()
|
||||
assert(awesome._modifiers.Control)
|
||||
return true
|
||||
end,
|
||||
function(count)
|
||||
if count == 1 then
|
||||
-- POSIX allows us to use awk
|
||||
|
@ -22,7 +26,11 @@ local steps = {
|
|||
-- Apply some limit on how long awesome may need to process 'things'
|
||||
return timer:elapsed() < 5
|
||||
end
|
||||
end
|
||||
end,
|
||||
function()
|
||||
assert(awesome._modifiers.Control)
|
||||
return true
|
||||
end,
|
||||
}
|
||||
runner.run_steps(steps)
|
||||
|
||||
|
|
Loading…
Reference in New Issue