Merge pull request #2521 from Elv13/modifiers

awesome: Add a `modifiers` field.
This commit is contained in:
Emmanuel Lepage Vallée 2019-01-24 23:59:54 -05:00 committed by GitHub
commit 7e7733c6d1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 277 additions and 33 deletions

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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