From 0d80b3f0e947694492c16647adf024c371fb7c52 Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Sun, 20 Jan 2019 23:04:39 -0500 Subject: [PATCH] keygrabber: Do not try to guess the modifiers name There is no an API to query the correct value Fixes #2515 --- lib/awful/key.lua | 58 +++++++++++++++++++++++++++++++--------- lib/awful/keygrabber.lua | 43 ++++++++++++++++++----------- 2 files changed, 74 insertions(+), 27 deletions(-) diff --git a/lib/awful/key.lua b/lib/awful/key.lua index 87d27ab3..ee4b5148 100644 --- a/lib/awful/key.lua +++ b/lib/awful/key.lua @@ -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. diff --git a/lib/awful/keygrabber.lua b/lib/awful/keygrabber.lua index 499e25a0..9b41f8de 100644 --- a/lib/awful/keygrabber.lua +++ b/lib/awful/keygrabber.lua @@ -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