Merge pull request #2307 from Elv13/replace_keygrabber
Keyboard and keygrabbing handling overhaul
This commit is contained in:
commit
56b2111b86
|
@ -78,7 +78,6 @@ sort_modules=true
|
|||
file = {
|
||||
-- C parts of libraries
|
||||
'../dbus.c',
|
||||
'../keygrabber.c',
|
||||
'../luaa.c',
|
||||
'../mouse.c',
|
||||
'../mousegrabber.c',
|
||||
|
|
30
keygrabber.c
30
keygrabber.c
|
@ -19,7 +19,7 @@
|
|||
*
|
||||
*/
|
||||
|
||||
/** awesome keygrabber API
|
||||
/*
|
||||
* @author Julien Danjou <julien@danjou.info>
|
||||
* @copyright 2008-2009 Julien Danjou
|
||||
* @module keygrabber
|
||||
|
@ -109,31 +109,12 @@ keygrabber_handlekpress(lua_State *L, xcb_key_press_event_t *e)
|
|||
return true;
|
||||
}
|
||||
|
||||
/** Grab keyboard input and read pressed keys, calling a callback function at
|
||||
/* Grab keyboard input and read pressed keys, calling a callback function at
|
||||
* each keypress, until `keygrabber.stop` is called.
|
||||
* The callback function receives three arguments:
|
||||
*
|
||||
* * a table containing modifiers keys
|
||||
* * a string with the pressed key
|
||||
* * a string with either "press" or "release" to indicate the event type.
|
||||
*
|
||||
* @param callback A callback function as described above.
|
||||
* @function run
|
||||
* @usage The following function can be bound to a key, and will be used to
|
||||
* resize a client using keyboard.
|
||||
*
|
||||
* function resize(c)
|
||||
* keygrabber.run(function(mod, key, event)
|
||||
* if event == "release" then return end
|
||||
*
|
||||
* if key == 'Up' then c:relative_move(0, 0, 0, 5)
|
||||
* elseif key == 'Down' then c:relative_move(0, 0, 0, -5)
|
||||
* elseif key == 'Right' then c:relative_move(0, 0, 5, 0)
|
||||
* elseif key == 'Left' then c:relative_move(0, 0, -5, 0)
|
||||
* else keygrabber.stop()
|
||||
* end
|
||||
* end)
|
||||
* end
|
||||
* @deprecated keygrabber.run
|
||||
*/
|
||||
static int
|
||||
luaA_keygrabber_run(lua_State *L)
|
||||
|
@ -153,7 +134,7 @@ luaA_keygrabber_run(lua_State *L)
|
|||
}
|
||||
|
||||
/** Stop grabbing the keyboard.
|
||||
* @function stop
|
||||
* @deprecated keygrabber.stop
|
||||
*/
|
||||
int
|
||||
luaA_keygrabber_stop(lua_State *L)
|
||||
|
@ -164,8 +145,9 @@ luaA_keygrabber_stop(lua_State *L)
|
|||
}
|
||||
|
||||
/** Check if keygrabber is running.
|
||||
* @function isrunning
|
||||
* @deprecated keygrabber.isrunning
|
||||
* @treturn bool A boolean value, true if keygrabber is running, false otherwise.
|
||||
* @see keygrabber.is_running
|
||||
*/
|
||||
static int
|
||||
luaA_keygrabber_isrunning(lua_State *L)
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
local capi = {
|
||||
screen = screen,
|
||||
client = client,
|
||||
keygrabber = keygrabber,
|
||||
}
|
||||
local awful = require("awful")
|
||||
local gtable = require("gears.table")
|
||||
|
@ -519,7 +518,7 @@ function widget.new(args)
|
|||
local help_wibox = self._cached_wiboxes[s][joined_groups]
|
||||
help_wibox:show()
|
||||
|
||||
return capi.keygrabber.run(function(_, key, event)
|
||||
return awful.keygrabber.run(function(_, key, event)
|
||||
if event == "release" then return end
|
||||
if key then
|
||||
if key == "Next" then
|
||||
|
@ -527,7 +526,7 @@ function widget.new(args)
|
|||
elseif key == "Prior" then
|
||||
help_wibox:page_prev()
|
||||
else
|
||||
capi.keygrabber.stop()
|
||||
awful.keygrabber.stop()
|
||||
help_wibox:hide()
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,22 +1,162 @@
|
|||
---------------------------------------------------------------------------
|
||||
--- Keygrabber Stack
|
||||
--- A keyboard grabbing and transaction object.
|
||||
--
|
||||
-- This module allows to grab all keyboard inputs until stopped. It is used
|
||||
-- to gather input data (such as in `awful.prompt`), implement VI-like keybindings
|
||||
-- or multi key transactions such as emulating the Alt+Tab behavior.
|
||||
--
|
||||
-- Note that this module has been redesigned in Awesome 4.3 to be object oriented
|
||||
-- and stateful. The use of the older global keygrabbing API is discouraged
|
||||
-- going forward since it had problem with recursive keygrabbers and required
|
||||
-- a lot of boiler plate code to get anything done.
|
||||
--
|
||||
-- Using keygrabber as transactions
|
||||
-- --------------------------------
|
||||
--
|
||||
-- The transactional keybindings are keybindings that start with a normal
|
||||
-- keybindings, but only end when a (mod)key is released. In the classic
|
||||
-- "Alt+Tab" transaction, first pressing "Alt+Tab" will display a popup listing
|
||||
-- all windows (clients). This popup will only disappear when "Alt" is released.
|
||||
--
|
||||
-- @DOC_text_awful_keygrabber_alttab_EXAMPLE@
|
||||
--
|
||||
-- In that example, because `export_keybindings` is set to `true`, pressing
|
||||
-- `alt+tab` or `alt+shift+tab` will start the transaction even if the
|
||||
-- keygrabber is not (yet) running.
|
||||
--
|
||||
-- Using keygrabber for modal keybindings (VI like)
|
||||
-- ------------------------------------------------
|
||||
--
|
||||
-- VI-like modal keybindings are trigerred by a key, like `Escape`, followed by
|
||||
-- either a number, an adjective (or noun) and closed by a verb. For example
|
||||
-- `<Escape>+2+t+f` could mean "focus (f) the second (2) tag (t)".
|
||||
-- `<Escape>+2+h+t+f` would "focus (f) two (2) tags (t) to the right (h)".
|
||||
--
|
||||
-- Here is a basic implementation of such a system. Note that the action
|
||||
-- functions themselves are not implemented to keep the example size and
|
||||
-- complexity to a minimum. The implementation is just if/elseif of all action
|
||||
-- and the code can be found in the normal `rc.lua` keybindings section:
|
||||
--
|
||||
-- @DOC_text_awful_keygrabber_vimode_EXAMPLE@
|
||||
--
|
||||
-- Using signals
|
||||
-- -------------
|
||||
--
|
||||
-- When the keygrabber is running, it will emit signals on each event. The
|
||||
-- format is "key_name".."::".."pressed_or_released". For example, to attach
|
||||
-- a callback to `Escape` being pressed, do:
|
||||
--
|
||||
-- mykeygrabber:connect_signal("Escape::pressed", function(self, modifiers, modset)
|
||||
-- print("Escape called!")
|
||||
-- end)
|
||||
--
|
||||
-- The `self` argument is the keygrabber instance. The `modifiers` is a list of
|
||||
-- all the currently pressed modifiers and the `modset` is the same table, but
|
||||
-- with the modifiers as keys instead of value. It allow to save time by
|
||||
-- checking `if modset.Mod4 then ... end` instead of looping into the `modifiers`
|
||||
-- table or using `gears.table`.
|
||||
--
|
||||
-- @author dodo
|
||||
-- @author Emmanuel Lepage Vallee <elv1313@gmail.com>
|
||||
-- @copyright 2012 dodo
|
||||
-- @module awful.keygrabber
|
||||
-- @copyright 2017 Emmanuel Lepage Vallee
|
||||
-- @classmod awful.keygrabber
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
local ipairs = ipairs
|
||||
local table = table
|
||||
local capi = {
|
||||
keygrabber = keygrabber }
|
||||
local gdebug = require('gears.debug')
|
||||
local akey = require("awful.key")
|
||||
local unpack = unpack or table.unpack -- luacheck: globals unpack (compatibility with Lua 5.1)
|
||||
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 keygrabber = {}
|
||||
local keygrab = {}
|
||||
|
||||
-- Private data
|
||||
local grabbers = {}
|
||||
local keygrabbing = false
|
||||
|
||||
local keygrabber = {
|
||||
object = {}
|
||||
}
|
||||
|
||||
-- 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",
|
||||
}
|
||||
|
||||
--BEGIN one day create a proper API to add and remove keybindings at runtime.
|
||||
-- Doing it this way is horrible.
|
||||
|
||||
-- This list of keybindings to add in the next event loop cycle.
|
||||
local delay_list = {}
|
||||
|
||||
local function add_root_keybindings(self, list)
|
||||
assert(
|
||||
list, "`add_root_keybindings` needs to be called with a list of keybindings"
|
||||
)
|
||||
|
||||
local was_started = #delay_list > 0
|
||||
|
||||
-- When multiple `awful.keygrabber` objects are created in `rc.lua`, avoid
|
||||
-- unpacking and repacking all keys for each instance and instead merge
|
||||
-- everything into one operation. In not so extreme cases, not doing so
|
||||
-- would slow down `awesome.restart()` by a small, but noticeable amount
|
||||
-- of time.
|
||||
gtable.merge(delay_list, list)
|
||||
|
||||
-- As of Awesome v4.3, `root.keys()` is an all or nothing API and there
|
||||
-- isn't a standard mechanism to add and remove keybindings at runtime
|
||||
-- without replacing the full list. Given `rc.lua` sets this list, not
|
||||
-- using a delayed call would cause all `awful.keygrabber` created above
|
||||
-- `root.keys(globalkeys)` to be silently overwritten. --FIXME v5
|
||||
if not was_started then
|
||||
gtimer.delayed_call(function()
|
||||
local ret = {}
|
||||
|
||||
for _, v in ipairs(delay_list) do
|
||||
local mods, key, press, release, description = unpack(v)
|
||||
|
||||
if press then
|
||||
local old_press = press
|
||||
press = function(...)
|
||||
self:start()
|
||||
old_press(...)
|
||||
end
|
||||
end
|
||||
|
||||
if release then
|
||||
local old_release = release
|
||||
release = function(...)
|
||||
self:start()
|
||||
old_release(...)
|
||||
end
|
||||
end
|
||||
|
||||
table.insert(ret, akey(mods, key, press, release, description))
|
||||
end
|
||||
|
||||
-- Wow...
|
||||
capi.root.keys(gtable.join( capi.root.keys(), unpack(ret) ))
|
||||
|
||||
delay_list = {}
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
--END hack
|
||||
|
||||
local function grabber(mod, key, event)
|
||||
for _, keygrabber_function in ipairs(grabbers) do
|
||||
|
@ -27,17 +167,119 @@ local function grabber(mod, key, event)
|
|||
end
|
||||
end
|
||||
|
||||
local function runner(self, modifiers, key, event)
|
||||
-- Stop the keygrabber with the `stop_key`
|
||||
if key == self.stop_key
|
||||
and event == self.release_event and self.stop_key then
|
||||
self:stop(key, modifiers)
|
||||
return false
|
||||
end
|
||||
|
||||
-- Stop when only a subset of keys are allowed and it isn't one of them.
|
||||
if self._private.allowed_keys and not self._private.allowed_keys[key] then
|
||||
self:stop(key, modifiers)
|
||||
return false
|
||||
end
|
||||
|
||||
-- Support multiple stop keys
|
||||
if type(self.stop_key) == "table" and event == self.release_event then
|
||||
for _, k in ipairs(self.stop_key) do
|
||||
if k == key then
|
||||
self:stop(k, modifiers)
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local is_modifier = conversion[key] ~= nil
|
||||
|
||||
-- Reset the inactivity timer on each events.
|
||||
if self._private.timer and self._private.timer.started then
|
||||
self._private.timer:again()
|
||||
end
|
||||
|
||||
-- Lua strings are bytes, to handle UTF-8, use GLib
|
||||
local seq_len = glib.utf8_strlen(self.sequence, -1)
|
||||
|
||||
-- Record the key sequence
|
||||
if key == "BackSpace" and seq_len > 0 then
|
||||
self.sequence = glib.utf8_substring(self.sequence, 0, seq_len - 2)
|
||||
elseif glib.utf8_strlen(key, -1) == 1 and event == "release" then
|
||||
self.sequence = self.sequence..key
|
||||
end
|
||||
|
||||
-- Convert index array to hash table
|
||||
local mod = {}
|
||||
for _, v in ipairs(modifiers) do mod[v] = true end
|
||||
|
||||
-- Emit some signals so the user can connect to a single type of event.
|
||||
self:emit_signal(key.."::"..event, modifiers, mod)
|
||||
|
||||
local filtered_modifiers = {}
|
||||
|
||||
-- User defined cases
|
||||
if self._private.keybindings[key] and event == "press" then
|
||||
-- Remove caps and num lock
|
||||
for _, m in ipairs(modifiers) do
|
||||
if not gtable.hasitem(akey.ignore_modifiers, m) then
|
||||
table.insert(filtered_modifiers, m)
|
||||
end
|
||||
end
|
||||
|
||||
for _,v in ipairs(self._private.keybindings[key]) do
|
||||
if #filtered_modifiers == #v[1] then
|
||||
local match = true
|
||||
for _,v2 in ipairs(v[1]) do
|
||||
match = match and mod[v2]
|
||||
end
|
||||
if match then
|
||||
v[3](self)
|
||||
|
||||
if self.mask_event_callback ~= false then
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Do not call the callbacks on modkeys
|
||||
if is_modifier and self.mask_modkeys then
|
||||
return
|
||||
end
|
||||
|
||||
if self.keypressed_callback and event == "press" then
|
||||
self.keypressed_callback(self, modifiers, key, event)
|
||||
elseif self.keyreleased_callback and event == "release" then
|
||||
self.keyreleased_callback(self, modifiers, key, event)
|
||||
end
|
||||
end
|
||||
|
||||
--- Stop grabbing the keyboard for the provided callback.
|
||||
--
|
||||
-- When no callback is given, the last grabber gets removed (last one added to
|
||||
-- the stack).
|
||||
-- @param g The key grabber that must be removed.
|
||||
function keygrabber.stop(g)
|
||||
--
|
||||
-- @param[opt] g The key grabber that must be removed.
|
||||
-- @deprecated awful.keygrabber.stop
|
||||
function keygrab.stop(g)
|
||||
-- If `run` is used directly and stop() is called with `g==nil`, get the
|
||||
-- first keygrabber.
|
||||
g = g
|
||||
or keygrab.current_instance and keygrab.current_instance.grabber
|
||||
or grabbers[#grabbers]
|
||||
|
||||
for i, v in ipairs(grabbers) do
|
||||
if v == g then
|
||||
table.remove(grabbers, i)
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if g and keygrab.current_instance and keygrab.current_instance.grabber == g then
|
||||
keygrab.current_instance = nil
|
||||
end
|
||||
|
||||
-- Stop the global key grabber if the last grabber disappears from stack.
|
||||
if #grabbers == 0 then
|
||||
keygrabbing = false
|
||||
|
@ -45,7 +287,419 @@ function keygrabber.stop(g)
|
|||
end
|
||||
end
|
||||
|
||||
---
|
||||
--- The keygrabber timeout.
|
||||
--
|
||||
-- @DOC_text_awful_keygrabber_timeout_EXAMPLE@
|
||||
--
|
||||
-- @property timeout
|
||||
-- @see gears.timer
|
||||
-- @see timeout_callback
|
||||
|
||||
--- The key on which the keygrabber listen to terminate itself.
|
||||
--
|
||||
-- When this is set, the running keygrabber will quit when [one of] the release
|
||||
-- key event occurs.
|
||||
--
|
||||
-- By default, the event is `press`. It is common for use case like the
|
||||
-- `awful.prompt` where `return` (enter) will terminate the keygrabbing. Using
|
||||
-- `release` as an event is more appropriate when the keygrabber is tied to a
|
||||
-- modifier key. For example, an Alt+Tab implementation stops when `mod1` (Alt)
|
||||
-- is released.
|
||||
--
|
||||
-- It can also be a table containing many keys (as values).
|
||||
--
|
||||
-- @DOC_text_awful_keygrabber_stop_keys_EXAMPLE@
|
||||
--
|
||||
-- @DOC_text_awful_keygrabber_stop_key_EXAMPLE@
|
||||
--
|
||||
-- Please note that modkeys are not accepted as `stop_key`s. You have to use
|
||||
-- their corresponding key names such as `Control_L`.
|
||||
--
|
||||
-- @property stop_key
|
||||
-- @param[opt=nil] string|table stop_key
|
||||
-- @see release_event
|
||||
|
||||
--- The event on which the keygrabbing will be terminated.
|
||||
--
|
||||
-- the valid values are:
|
||||
--
|
||||
-- * "press" (default)
|
||||
-- * "release"
|
||||
--
|
||||
-- @property release_event
|
||||
-- @param string
|
||||
-- @see stop_key
|
||||
|
||||
--- Whether or not to execute the key press/release callbacks when keybindings are called.
|
||||
--
|
||||
-- When this property is set to false, the `keyreleased_callback` and
|
||||
-- `keypressed_callback` callbacks will be executed even when the event was
|
||||
-- consumed by a `keybinding`.
|
||||
--
|
||||
-- By default, keybindings block those callbacks from being executed.
|
||||
--
|
||||
-- @property mask_event_callback
|
||||
-- @param[opt=true] boolean
|
||||
-- @see keybindings
|
||||
-- @see keyreleased_callback
|
||||
-- @see keypressed_callback
|
||||
|
||||
--- Do not call the callbacks on modifier keys (like `Control` or `Mod4`) events.
|
||||
-- @property mask_modkeys
|
||||
-- @param[opt=false] boolean
|
||||
-- @see mask_event_callback
|
||||
|
||||
--- Export all keygrabber keybindings as root (global) keybindings.
|
||||
--
|
||||
-- When this is enabled, calling all of the keygrabber object `keybinding`s will
|
||||
-- will create root `awful.key` and will automatically starts the grabbing.
|
||||
--
|
||||
-- Use this with caution. If many keygrabber or "real" root keybindings are set
|
||||
-- on the same key combination, they are all executed and there is almost no
|
||||
-- safe way to undo that. Make sure the `keygrabber` that use this option
|
||||
-- have a single instance.
|
||||
--
|
||||
-- @property export_keybindings
|
||||
-- @param[opt=false] boolean
|
||||
|
||||
--- The root (global) keybinding to start this keygrabber.
|
||||
--
|
||||
-- Instead of adding an entry to `root.keys` or `rc.lua` `globalkeys` section,
|
||||
-- this property can be used to take care of everything. This way, it becomes
|
||||
-- easier to package modules using keygrabbers.
|
||||
--
|
||||
-- @DOC_text_awful_keygrabber_root_keybindings_EXAMPLE@
|
||||
--
|
||||
-- @property root_keybindings
|
||||
-- @see export_keybindings
|
||||
-- @see keybindings
|
||||
|
||||
--- The keybindings associated with this keygrabber.
|
||||
--
|
||||
-- The keybindings syntax is the same as `awful.key` or `awful.prompt.hooks`. It
|
||||
-- consists of a table with 4 entries.
|
||||
--
|
||||
-- * `mods` A table with modifier keys, such as `shift`, `mod4`, `mod1` (alt) or
|
||||
-- `control`.
|
||||
-- * `key` The key name, such as `left` or `f`
|
||||
-- * `callback` A function that will be called when the key combination is
|
||||
-- pressed.
|
||||
-- * `description` A table various metadata to be used for `awful.hotkeys_popup`.
|
||||
--
|
||||
-- @property keybindings
|
||||
-- @param table
|
||||
-- @see export_keybindings
|
||||
-- @see root_keybindings
|
||||
|
||||
--- If any key is pressed that is not in this list, the keygrabber is stopped.
|
||||
--
|
||||
-- When defined, this property allows to define an implicit way to release the
|
||||
-- keygrabber. It helps save some boilerplate code in the handler callbacks.
|
||||
--
|
||||
-- It is useful when a transaction only handle a limited number of keys. If
|
||||
-- a key unhandled by the transaction is trigerred, the transaction is
|
||||
-- canceled.
|
||||
--
|
||||
-- @DOC_text_awful_keygrabber_allowed_keys_EXAMPLE@
|
||||
--
|
||||
-- @property allowed_keys
|
||||
-- @tparam[opt=nil] table|nil allowed_keys The list of keys.
|
||||
|
||||
--- The sequence of keys recorded since the start of the keygrabber.
|
||||
--
|
||||
-- In this example, the `stop_callback` is used to retrieve the final key
|
||||
-- sequence.
|
||||
--
|
||||
-- Please note that backspace will remove elements from the sequence and
|
||||
-- named keys and modifiers are ignored.
|
||||
--
|
||||
-- @DOC_text_awful_keygrabber_autostart_EXAMPLE@
|
||||
--
|
||||
-- @property sequence
|
||||
-- @param string
|
||||
--
|
||||
|
||||
--- The current (running) instance of this keygrabber.
|
||||
--
|
||||
-- The keygrabber instance is created when the keygrabber starts. It is an object
|
||||
-- mirroring this keygrabber object, but where extra properties can be added. It
|
||||
-- is useful to keep some contextual data part of the current transaction without
|
||||
-- poluting the original object of having extra boilerplate code.
|
||||
--
|
||||
-- @tfield keygrabber current_instance
|
||||
-- @see property::current_instance
|
||||
|
||||
--- The global signal used to track the `current_instance`.
|
||||
--
|
||||
-- @signal property::current_instance
|
||||
|
||||
--- If a keygrabber is currently running.
|
||||
-- @tfield boolean is_running
|
||||
|
||||
--- Start the keygrabber.
|
||||
--
|
||||
-- Note that only a single keygrabber can be started at any one time. If another
|
||||
-- keygrabber (or this one) is currently running. This method returns false.
|
||||
--
|
||||
-- @treturn boolean If the keygrabber was successfully started.
|
||||
function keygrabber:start()
|
||||
if self.grabber or keygrab.current_instance then
|
||||
return false
|
||||
end
|
||||
|
||||
self.current_instance = setmetatable({}, {
|
||||
__index = self,
|
||||
__newindex = function(tab, key, value)
|
||||
if keygrabber["set_"..key] then
|
||||
self[key](self, value)
|
||||
else
|
||||
rawset(tab, key, value)
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
self.sequence = ""
|
||||
|
||||
if self.start_callback then
|
||||
self.start_callback(self.current_instance)
|
||||
end
|
||||
|
||||
self.grabber = keygrab.run(function(...) return runner(self, ...) end)
|
||||
|
||||
-- Ease making keygrabber that wont hang forever if no action is taken.
|
||||
if self.timeout and not self._private.timer then
|
||||
self._private.timer = gtimer {
|
||||
timeout = self.timeout,
|
||||
single_shot = true,
|
||||
callback = function()
|
||||
if self.timeout_callback then
|
||||
pcall(self.timeout_callback, self)
|
||||
end
|
||||
self:stop()
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
if self._private.timer then
|
||||
self._private.timer:start()
|
||||
end
|
||||
|
||||
keygrab.current_instance = self
|
||||
|
||||
keygrab.emit_signal("property::current_instance", keygrab.current_instance)
|
||||
|
||||
self:emit_signal("started")
|
||||
end
|
||||
|
||||
--- Stop the keygrabber.
|
||||
-- @function keygrabber:stop
|
||||
function keygrabber:stop(_stop_key, _release_mods) -- (at)function disables ldoc params
|
||||
keygrab.stop(self.grabber)
|
||||
|
||||
if self.stop_callback then
|
||||
self.stop_callback(
|
||||
self.current_instance, _stop_key, _release_mods, self.sequence
|
||||
)
|
||||
end
|
||||
|
||||
keygrab.emit_signal("property::current_instance", nil)
|
||||
|
||||
self.grabber = nil
|
||||
self:emit_signal("stopped")
|
||||
end
|
||||
|
||||
--- Add a keybinding to this keygrabber.
|
||||
--
|
||||
-- Those keybindings will automatically start the keygrabbing when hit.
|
||||
--
|
||||
-- @tparam table mods A table with modifier keys, such as `shift`, `mod4`, `mod1` (alt) or
|
||||
-- `control`.
|
||||
-- @tparam string key The key name, such as `left` or `f`
|
||||
-- @tparam function callback A function that will be called when the key
|
||||
-- combination is pressed.
|
||||
-- @tparam[opt] table description A table various metadata to be used for `awful.hotkeys_popup`.
|
||||
-- @tparam string description.description The keybinding description
|
||||
-- @tparam string description.group The keybinding group
|
||||
function keygrabber:add_keybinding(mods, key, callback, description)
|
||||
self._private.keybindings[key] = self._private.keybindings[key] or {}
|
||||
table.insert(self._private.keybindings[key], {
|
||||
mods, key, callback, description
|
||||
})
|
||||
|
||||
if self.export_keybindings then
|
||||
add_root_keybindings(self, {{mods, key, callback, description}})
|
||||
end
|
||||
end
|
||||
|
||||
function keygrabber:set_root_keybindings(keys)
|
||||
add_root_keybindings(self, keys)
|
||||
end
|
||||
|
||||
-- Turn into a set.
|
||||
function keygrabber:set_allowed_keys(keys)
|
||||
self._private.allowed_keys = {}
|
||||
|
||||
for _, v in ipairs(keys) do
|
||||
self._private.allowed_keys[v] = true
|
||||
end
|
||||
end
|
||||
|
||||
--- When the keygrabber starts.
|
||||
-- @signal started
|
||||
|
||||
--- When the keygrabber stops.
|
||||
-- @signal stopped
|
||||
|
||||
--- A function called when a keygrabber starts.
|
||||
-- @callback start_callback
|
||||
-- @tparam keygrabber keygrabber The keygrabber.
|
||||
|
||||
--- The callback when the keygrabbing stops.
|
||||
--
|
||||
-- @usage local function my_done_cb(self, stop_key, release_mods, sequence)
|
||||
-- -- do something
|
||||
-- end
|
||||
-- @tparam table self The current transaction object.
|
||||
-- @tparam string stop_key The key(s) that stop the keygrabbing (if any)
|
||||
-- @tparam table release_mods The release modifiers key (if any)
|
||||
-- @tparam sting sequence The recorded key sequence.
|
||||
-- @callback stop_callback
|
||||
-- @see sequence
|
||||
-- @see stop
|
||||
|
||||
--- The function called when the keygrabber stops because of a `timeout`.
|
||||
--
|
||||
-- Note that the `stop_callback` is also called when the keygrabbers timeout.
|
||||
--
|
||||
-- @callback timeout_callback
|
||||
-- @see timeout
|
||||
|
||||
--- The callback function to call with mod table, key and command as arguments
|
||||
-- when a key was pressed.
|
||||
--
|
||||
-- @callback keypressed_callback
|
||||
-- @tparam table self The current transaction object.
|
||||
-- @tparam table mod The current modifiers (like "Control" or "Shift").
|
||||
-- @tparam string key The key name.
|
||||
-- @tparam string event The event ("press" or "release").
|
||||
-- @usage local function my_keypressed_cb(mod, key, command)
|
||||
-- -- do something
|
||||
-- end
|
||||
|
||||
--- The callback function to call with mod table, key and command as arguments
|
||||
-- when a key was released.
|
||||
-- @usage local function my_keyreleased_cb(mod, key, command)
|
||||
-- -- do something
|
||||
-- end
|
||||
-- @callback keyreleased_callback
|
||||
-- @tparam table self The current transaction object.
|
||||
-- @tparam table mod The current modifiers (like "Control" or "Shift").
|
||||
-- @tparam string key The key name.
|
||||
-- @tparam string event The event ("press" or "release")
|
||||
|
||||
--- A wrapper around the keygrabber to make it easier to add keybindings.
|
||||
--
|
||||
-- This is similar to the `awful.prompt`, but without an actual widget. It can
|
||||
-- be used to implement custom transactions such as alt+tab or keyboard menu
|
||||
-- navigation.
|
||||
--
|
||||
-- Note that the object returned can be used as a client or global keybinding
|
||||
-- callback without modification. Make sure to set `stop_key` and `release_event`
|
||||
-- to proper values to avoid permanently locking the keyboard.
|
||||
--
|
||||
-- @tparam table args
|
||||
-- @tparam[opt="press"] string args.release_event Release event ("press" or "release")
|
||||
-- @tparam[opt=nil] string|table args.stop_key The key that has to be kept pressed.
|
||||
-- @tparam table args.keybindings All keybindings.
|
||||
-- @tparam[opt=-1] number args.timeout The maximum inactivity delay.
|
||||
-- @tparam[opt=true] boolean args.mask_event_callback Do not call `keypressed_callback`
|
||||
-- or `keyreleased_callback` when a hook is called.
|
||||
-- @tparam[opt] function args.start_callback
|
||||
-- @tparam[opt] function args.stop_callback
|
||||
-- @tparam[opt] function args.timeout_callback
|
||||
-- @tparam[opt] function args.keypressed_callback
|
||||
-- @tparam[opt] function args.keyreleased_callback
|
||||
-- @tparam[opt=nil] table|nil args.allowed_keys A table with all keys that
|
||||
-- **wont** stop the keygrabber.
|
||||
-- @tparam[opt] table args.root_keybindings The root (global) keybindings.
|
||||
-- @tparam[opt=false] boolean args.export_keybindings Create root (global) keybindings.
|
||||
-- @tparam[opt=false] boolean args.autostart Start the grabbing immediately
|
||||
-- @tparam[opt=false] boolean args.mask_modkeys Do not call the callbacks on
|
||||
-- modifier keys (like `Control` or `Mod4`) events.
|
||||
-- @function awful.keygrabber
|
||||
function keygrab.run_with_keybindings(args)
|
||||
args = args or {}
|
||||
|
||||
local ret = gobject {
|
||||
enable_properties = true,
|
||||
enable_auto_signals = true
|
||||
}
|
||||
|
||||
rawset(ret, "_private", {})
|
||||
|
||||
-- Do not use `rawset` or `_private` because it uses the `gears.object`
|
||||
-- auto signals.
|
||||
ret.sequence = ""
|
||||
|
||||
gtable.crush(ret, keygrabber, true)
|
||||
|
||||
gtable.crush(ret, args)
|
||||
|
||||
ret._private.keybindings = {}
|
||||
ret.release_event = args.release_event or "press"
|
||||
|
||||
-- Build the hook map
|
||||
for _,v in ipairs(args.keybindings or {}) do
|
||||
if #v >= 3 and #v <= 4 then
|
||||
local _,key,callback = unpack(v)
|
||||
if type(callback) == "function" then
|
||||
ret._private.keybindings[key] = ret._private.keybindings[key] or {}
|
||||
table.insert(ret._private.keybindings[key], v)
|
||||
else
|
||||
gdebug.print_warning(
|
||||
"The hook's 3rd parameter has to be a function. " ..
|
||||
gdebug.dump(v)
|
||||
)
|
||||
end
|
||||
else
|
||||
gdebug.print_warning(
|
||||
"The hook has to have at least 3 parameters. ".. gdebug.dump(v)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
if args.export_keybindings then
|
||||
add_root_keybindings(ret, args.keybindings)
|
||||
end
|
||||
|
||||
local mt = getmetatable(ret)
|
||||
|
||||
-- Add syntax-sugar to call `:start()`.
|
||||
-- This allows keygrabbers object to be used as callbacks "functions".
|
||||
function mt.__call()
|
||||
ret:start()
|
||||
end
|
||||
|
||||
if args.autostart then
|
||||
ret:start()
|
||||
end
|
||||
|
||||
return ret
|
||||
end
|
||||
|
||||
--- Run a grabbing function.
|
||||
--
|
||||
-- Calling this is equivalent to `keygrabber.run`.
|
||||
--
|
||||
-- @param g The key grabber callback that will get the key events until it
|
||||
-- will be deleted or a new grabber is added.
|
||||
-- @return the given callback `g`.
|
||||
--
|
||||
-- @deprecated awful.keygrabber.run
|
||||
-- @see keygrabber.run
|
||||
|
||||
--- A lower level API to interact with the keygrabber directly.
|
||||
--
|
||||
-- Grab keyboard input and read pressed keys, calling the least callback
|
||||
-- function from the stack at each keypress, until the stack is empty.
|
||||
--
|
||||
|
@ -56,11 +710,27 @@ end
|
|||
--
|
||||
-- * a table containing modifiers keys
|
||||
-- * a string with the pressed key
|
||||
-- * a string with either "press" or "release" to indicate the event type
|
||||
-- * a string with either "press" or "release" to indicate the event type.
|
||||
--
|
||||
-- Here is the content of the modifier table:
|
||||
--
|
||||
-- <table>
|
||||
-- <tr style='font-weight: bold;'>
|
||||
-- <th align='center'>Modifier name </th>
|
||||
-- <th align='center'>Key name</th>
|
||||
-- <th align='center'>Alternate key name</th>
|
||||
-- </tr>
|
||||
-- <tr><td> Mod4</td><td align='center'> Super_L </td><td align='center'> Super_R </td></tr>
|
||||
-- <tr><td> Control </td><td align='center'> Control_L </td><td align='center'> Control_R </td></tr>
|
||||
-- <tr><td> Shift </td><td align='center'> Shift_L </td><td align='center'> Shift_R </td></tr>
|
||||
-- <tr><td> Mod1</td><td align='center'> Alt_L </td><td align='center'> Alt_R </td></tr>
|
||||
-- </table>
|
||||
--
|
||||
-- A callback can return `false` to pass the events to the next
|
||||
-- keygrabber in the stack.
|
||||
-- @param g The key grabber callback that will get the key events until it will be deleted or a new grabber is added.
|
||||
--
|
||||
-- @param g The key grabber callback that will get the key events until it
|
||||
-- will be deleted or a new grabber is added.
|
||||
-- @return the given callback `g`.
|
||||
-- @usage
|
||||
-- -- The following function can be bound to a key, and be used to resize a
|
||||
|
@ -79,19 +749,80 @@ end
|
|||
-- end
|
||||
-- end)
|
||||
-- end
|
||||
function keygrabber.run(g)
|
||||
-- @function awful.keygrabber.run
|
||||
function keygrab.run(g)
|
||||
-- Remove the grabber if it is in the stack.
|
||||
keygrabber.stop(g)
|
||||
keygrab.stop(g)
|
||||
|
||||
-- Record the grabber that has been added most recently.
|
||||
table.insert(grabbers, 1, g)
|
||||
|
||||
-- Start the keygrabber if it is not running already.
|
||||
if not keygrabbing then
|
||||
keygrabbing = true
|
||||
capi.keygrabber.run(grabber)
|
||||
end
|
||||
|
||||
return g
|
||||
end
|
||||
|
||||
return keygrabber
|
||||
-- Implement the signal system for the keygrabber.
|
||||
|
||||
local signals = {}
|
||||
|
||||
--- Connect to a signal for all keygrabbers at once.
|
||||
-- @function awful.keygrabber.connect_signal
|
||||
-- @tparam string name The signal name.
|
||||
-- @tparam function callback The callback.
|
||||
function keygrab.connect_signal(name, callback)
|
||||
signals[name] = signals[name] or {}
|
||||
|
||||
-- Use table.insert instead of signals[name][callback] = true to make
|
||||
-- the execution order stable across `emit_signal`. This avoids some
|
||||
-- heisenbugs.
|
||||
table.insert(signals[name], callback)
|
||||
end
|
||||
|
||||
--- Disconnect to a signal for all keygrabbers at once.
|
||||
-- @function awful.keygrabber.disconnect_signal
|
||||
-- @tparam string name The signal name.
|
||||
-- @tparam function callback The callback.
|
||||
function keygrab.disconnect_signal(name, callback)
|
||||
signals[name] = signals[name] or {}
|
||||
|
||||
for k, v in ipairs(signals[name]) do
|
||||
if v == callback then
|
||||
table.remove(signals[name], k)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Emit a signal on the keygrabber module itself.
|
||||
--
|
||||
-- Warning, usually don't use this directly, use
|
||||
-- `my_keygrabber:emit_signal(name, ...)`. This function works on the whole
|
||||
-- keygrabber module, not one of its instance.
|
||||
--
|
||||
-- @function awful.keygrabber.emit_signal
|
||||
-- @tparam string name The signal name.
|
||||
-- @param ... Other arguments for the callbacks.
|
||||
function keygrab.emit_signal(name, ...)
|
||||
signals[name] = signals[name] or {}
|
||||
|
||||
for _, cb in ipairs(signals[name]) do
|
||||
cb(...)
|
||||
end
|
||||
end
|
||||
|
||||
function keygrab.get_is_running()
|
||||
return keygrab.current_instance ~= nil
|
||||
end
|
||||
|
||||
--@DOC_object_COMMON@
|
||||
|
||||
return setmetatable(keygrab, {
|
||||
__call = function(_, args) return keygrab.run_with_keybindings(args) end
|
||||
})
|
||||
|
||||
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
||||
|
|
|
@ -158,7 +158,7 @@ function timer.new(args)
|
|||
args = args or {}
|
||||
local ret = object()
|
||||
|
||||
ret.data = { timeout = 0 }
|
||||
ret.data = { timeout = 0 } --TODO v5 rename to ._private
|
||||
setmetatable(ret, timer_instance_mt)
|
||||
|
||||
for k, v in pairs(args) do
|
||||
|
|
52
root.c
52
root.c
|
@ -211,10 +211,56 @@ _string_to_key_code(const char *s)
|
|||
}
|
||||
}
|
||||
|
||||
/** Send fake events. Usually the currently focused client will get it.
|
||||
/** Send fake keyboard or mouse events.
|
||||
*
|
||||
* @param event_type The event type: key_press, key_release, button_press,
|
||||
* button_release or motion_notify.
|
||||
* Usually the currently focused client or the keybindings will receive those
|
||||
* events. If a `keygrabber` or `mousegrabber` is running, then it will get them.
|
||||
*
|
||||
* Some keys have different names compared to the ones generally used in
|
||||
* Awesome. For example, Awesome uses "modifier keys" for keybindings using
|
||||
* their X11 names such as "Control" or "Mod1" (for "Alt"). These are not the
|
||||
* name of the key but is only the name of the modifier they represent. Some
|
||||
* modifiers are even present twice on some keyboard like the left and right
|
||||
* "Shift". Here is a list of the "real" key names matching the modifiers in
|
||||
* `fake_input`:
|
||||
*
|
||||
* <table>
|
||||
* <tr style='font-weight: bold;'>
|
||||
* <th align='center'>Modifier name </th>
|
||||
* <th align='center'>Key name</th>
|
||||
* <th align='center'>Other key name</th>
|
||||
* </tr>
|
||||
* <tr><td> Mod4</td><td align='center'> Super_L </td><td align='center'> Super_R </td></tr>
|
||||
* <tr><td> Control </td><td align='center'> Control_L </td><td align='center'> Control_R </td></tr>
|
||||
* <tr><td> Shift </td><td align='center'> Shift_L </td><td align='center'> Shift_R </td></tr>
|
||||
* <tr><td> Mod1</td><td align='center'> Alt_L </td><td align='center'> Alt_R </td></tr>
|
||||
* </table>
|
||||
*
|
||||
* Note that this is valid for most of the modern "western" keyboard layouts.
|
||||
* Some older, custom or foreign layouts may break this convention.
|
||||
*
|
||||
* This function is very low level, to be more useful, it can be wrapped into
|
||||
* higher level constructs such as:
|
||||
*
|
||||
* **Sending strings:**
|
||||
*
|
||||
* @DOC_text_root_fake_string_EXAMPLE@
|
||||
*
|
||||
* Note that this example works for most ASCII inputs but may fail depending on
|
||||
* how the string is encoded. Some multi-byte characters may not represent
|
||||
* keys and some UTF-8 encoding format create characters by combining multiple
|
||||
* elements such as accent + base character or various escape sequences. If you
|
||||
* wish to use this example for "real world" i18n use cases, learning about
|
||||
* XKB event and UTF-8 encoding is a prerequisites.
|
||||
*
|
||||
* **Clicking:**
|
||||
*
|
||||
* ![Client geometry](../images/mouse.svg)
|
||||
*
|
||||
* @DOC_text_root_fake_click_EXAMPLE@
|
||||
*
|
||||
* @param event_type The event type: key\_press, key\_release, button\_press,
|
||||
* button\_release or motion\_notify.
|
||||
* @param detail The detail: in case of a key event, this is the keycode
|
||||
* to send, in case of a button event this is the number of the button. In
|
||||
* case of a motion event, this is a boolean value which if true makes the
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
-- Needed for root.fake_inputs
|
||||
-- Needed for root.fake_input
|
||||
local keygrabber = {_current_grabber = nil}
|
||||
|
||||
local function stop()
|
||||
|
|
|
@ -112,12 +112,58 @@ local fake_input_handlers = {
|
|||
motion_notify = function() --[[TODO]] end,
|
||||
}
|
||||
|
||||
function root.fake_inputs(event_type, detail, x, y)
|
||||
function root.fake_input(event_type, detail, x, y)
|
||||
assert(fake_input_handlers[event_type], "Unknown event_type")
|
||||
|
||||
fake_input_handlers[event_type](detail, x, y)
|
||||
end
|
||||
|
||||
|
||||
-- Send an artificial set of key events to trigger a key combination.
|
||||
-- It only works in the shims and should not be used with UTF-8 chars.
|
||||
function root._execute_keybinding(modifiers, key)
|
||||
for _, mod in ipairs(modifiers) do
|
||||
for real_key, mod_name in pairs(conversion) do
|
||||
if mod == mod_name then
|
||||
root.fake_input("key_press", real_key)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
root.fake_input("key_press" , key)
|
||||
root.fake_input("key_release", key)
|
||||
|
||||
for _, mod in ipairs(modifiers) do
|
||||
for real_key, mod_name in pairs(conversion) do
|
||||
if mod == mod_name then
|
||||
root.fake_input("key_release", real_key)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Send artificial key events to write a string.
|
||||
-- It only works in the shims and should not be used with UTF-8 strings.
|
||||
function root._write_string(string, c)
|
||||
local old_c = client.focus
|
||||
|
||||
if c then
|
||||
client.focus = c
|
||||
end
|
||||
|
||||
for i=1, #string do
|
||||
local char = string:sub(i,i)
|
||||
root.fake_input("key_press" , char)
|
||||
root.fake_input("key_release", char)
|
||||
end
|
||||
|
||||
if c then
|
||||
client.focus = old_c
|
||||
end
|
||||
end
|
||||
|
||||
return root
|
||||
|
||||
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
|
||||
local awful = { keygrabber = require("awful.keygrabber") } --DOC_HIDE
|
||||
|
||||
local allowed_keys_works = false --DOC_HIDE
|
||||
|
||||
awful.keygrabber {
|
||||
autostart = true,
|
||||
allowed_keys = {"a", "w", "e", "s", "o", "m", "e"},
|
||||
stop_callback = function() --DOC_HIDE
|
||||
allowed_keys_works = true --DOC_HIDE
|
||||
end, --DOC_HIDE
|
||||
}
|
||||
|
||||
root.fake_input("key_press" , "w")--DOC_HIDE
|
||||
root.fake_input("key_release", "w")--DOC_HIDE
|
||||
|
||||
assert(not allowed_keys_works) --DOC_HIDE
|
||||
|
||||
root.fake_input("key_press" , "q")--DOC_HIDE
|
||||
root.fake_input("key_release", "q")--DOC_HIDE
|
||||
|
||||
assert(allowed_keys_works) --DOC_HIDE
|
|
@ -0,0 +1,41 @@
|
|||
--DOC_HEADER --DOC_NO_USAGE
|
||||
|
||||
local was_called = {} --DOC_HIDE
|
||||
|
||||
local awful = {keygrabber = require("awful.keygrabber"), --DOC_HIDE
|
||||
client={focus={history={--DOC_HIDE
|
||||
disable_tracking = function() was_called[1] = true end, --DOC_HIDE
|
||||
enable_tracking = function() was_called[2] = true end, --DOC_HIDE
|
||||
select_next = function() was_called[3] = true end, --DOC_HIDE
|
||||
select_previous = function() was_called[4] = true end, --DOC_HIDE
|
||||
}}}}--DOC_HIDE
|
||||
|
||||
awful.keygrabber {
|
||||
keybindings = {
|
||||
{{"Mod1" }, "Tab", awful.client.focus.history.select_previous},
|
||||
{{"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",
|
||||
release_event = "release",
|
||||
start_callback = awful.client.focus.history.disable_tracking,
|
||||
stop_callback = awful.client.focus.history.enable_tracking,
|
||||
export_keybindings = true,
|
||||
}
|
||||
|
||||
--DOC_HIDE Trigger the keybinging
|
||||
awesome.emit_signal("refresh") --DOC_HIDE `export_keybindings` is async
|
||||
root.fake_input("key_press", "Alt_L")--DOC_HIDE
|
||||
root.fake_input("key_press", "Tab")--DOC_HIDE
|
||||
root.fake_input("key_release", "Tab")--DOC_HIDE
|
||||
root.fake_input("key_release", "Alt_L")--DOC_HIDE
|
||||
assert(was_called[1] and was_called[1] and was_called[2] and was_called[4])--DOC_HIDE
|
||||
assert(not was_called[3]) --DOC_HIDE
|
||||
|
||||
--DOC_HIDE Now make sure it can be triggered again
|
||||
root.fake_input("key_press", "Alt_L")--DOC_HIDE
|
||||
root.fake_input("key_press", "Shift_L")--DOC_HIDE
|
||||
root.fake_input("key_press", "Tab")--DOC_HIDE
|
||||
root.fake_input("key_release", "Tab")--DOC_HIDE
|
||||
|
||||
assert(was_called[3]) --DOC_HIDE
|
|
@ -0,0 +1,26 @@
|
|||
|
||||
local awful = { keygrabber = require("awful.keygrabber") } --DOC_HIDE
|
||||
|
||||
local naughty = { notify = function() end } --DOC_HIDE
|
||||
|
||||
local autostart_works = false --DOC_HIDE
|
||||
|
||||
awful.keygrabber {
|
||||
autostart = true,
|
||||
stop_key = "Return",
|
||||
stop_callback = function(_, _, _, sequence)
|
||||
autostart_works = true --DOC_HIDE
|
||||
assert(sequence == "abc") --DOC_HIDE
|
||||
naughty.notify{text="The keys were:"..sequence}
|
||||
end,
|
||||
}
|
||||
|
||||
for _, v in ipairs {"a", "b", "c"} do--DOC_HIDE
|
||||
root.fake_input("key_press" , v)--DOC_HIDE
|
||||
root.fake_input("key_release", v)--DOC_HIDE
|
||||
end--DOC_HIDE
|
||||
|
||||
root.fake_input("key_press" , "Return")--DOC_HIDE
|
||||
root.fake_input("key_release", "Return")--DOC_HIDE
|
||||
|
||||
assert(autostart_works) --DOC_HIDE
|
|
@ -0,0 +1,20 @@
|
|||
|
||||
local awful = { keygrabber = require("awful.keygrabber") } --DOC_HIDE
|
||||
|
||||
local allowed_keys_works = false --DOC_HIDE
|
||||
|
||||
awful.keygrabber {
|
||||
autostart = true, --DOC_HIDE
|
||||
stop_key = "Return",
|
||||
stop_callback = function() --DOC_HIDE
|
||||
allowed_keys_works = true --DOC_HIDE
|
||||
end, --DOC_HIDE
|
||||
}
|
||||
|
||||
assert(not allowed_keys_works) --DOC_HIDE
|
||||
root.fake_input("key_press" , "a")--DOC_HIDE
|
||||
root.fake_input("key_release", "a")--DOC_HIDE
|
||||
assert(not allowed_keys_works) --DOC_HIDE
|
||||
root.fake_input("key_press" , "Return")--DOC_HIDE
|
||||
root.fake_input("key_release", "Return")--DOC_HIDE
|
||||
assert(allowed_keys_works) --DOC_HIDE
|
|
@ -0,0 +1,20 @@
|
|||
--DOC_NO_USAGE
|
||||
local awful = { keygrabber = require("awful.keygrabber") } --DOC_HIDE
|
||||
|
||||
local allowed_keys_works = false --DOC_HIDE
|
||||
|
||||
awful.keygrabber {
|
||||
autostart = true, --DOC_HIDE
|
||||
stop_key = {"Return", "Escape"},
|
||||
stop_callback = function() --DOC_HIDE
|
||||
allowed_keys_works = true --DOC_HIDE
|
||||
end, --DOC_HIDE
|
||||
}
|
||||
|
||||
assert(not allowed_keys_works) --DOC_HIDE
|
||||
root.fake_input("key_press" , "a")--DOC_HIDE
|
||||
root.fake_input("key_release", "a")--DOC_HIDE
|
||||
assert(not allowed_keys_works) --DOC_HIDE
|
||||
root.fake_input("key_press" , "Escape")--DOC_HIDE
|
||||
root.fake_input("key_release", "Escape")--DOC_HIDE
|
||||
assert(allowed_keys_works) --DOC_HIDE
|
|
@ -0,0 +1,64 @@
|
|||
--DOC_GEN_OUTPUT --DOC_HIDE
|
||||
local awful = { keygrabber = require("awful.keygrabber") } --DOC_HIDE
|
||||
|
||||
local keybinding_works = {} --DOC_HIDE
|
||||
|
||||
local g = --DOC_HIDE
|
||||
awful.keygrabber {
|
||||
mask_modkeys = true,
|
||||
root_keybindings = {
|
||||
{{"Mod4"}, "i", function(self)
|
||||
print("Is now active!", self)
|
||||
keybinding_works[1] = true --DOC_HIDE
|
||||
end},
|
||||
},
|
||||
keybindings = {
|
||||
{{"Mod4", "Shift"}, "i", function(self)
|
||||
print("Called again!")
|
||||
keybinding_works[3] = true --DOC_HIDE
|
||||
self:stop()
|
||||
end},
|
||||
},
|
||||
keypressed_callback = function(_, modifiers, key)
|
||||
print("A key was pressed:", key, "with", #modifiers, "modifier!")
|
||||
keybinding_works[2] = keybinding_works[2] and keybinding_works[2] + 1 or 1 --DOC_HIDE
|
||||
end,
|
||||
}
|
||||
--DOC_NEWLINE
|
||||
-- The following will **NOT** trigger the keygrabbing because it isn't exported
|
||||
-- to the root (global) keys. Adding `export_keybindings` would solve that
|
||||
awesome.emit_signal("refresh") --DOC_HIDE `root_keybindings` is async
|
||||
root._execute_keybinding({"Mod4", "Shift"}, "i")
|
||||
assert(#keybinding_works == 0)
|
||||
|
||||
--DOC_NEWLINE
|
||||
-- But this will start the keygrabber because it is part of the root_keybindings
|
||||
root._execute_keybinding({"Mod4"}, "i")
|
||||
assert(keybinding_works[1]) --DOC_HIDE
|
||||
assert(not keybinding_works[2]) --DOC_HIDE
|
||||
|
||||
--DOC_NEWLINE
|
||||
-- Note that that keygrabber is running, all callbacks should work:
|
||||
root.fake_input("key_press" , "a")
|
||||
root.fake_input("key_release" , "a")
|
||||
assert(keybinding_works[2] == 1) --DOC_HIDE
|
||||
|
||||
--DOC_NEWLINE
|
||||
-- Calling the root keybindings now wont work because they are not part of
|
||||
-- the keygrabber internal (own) keybindings, so `keypressed_callback` will
|
||||
-- be called.
|
||||
root._execute_keybinding({"Mod4"}, "i")
|
||||
assert(keybinding_works[2] == 2) --DOC_HIDE because mask_modkeys is set
|
||||
assert(g == awful.keygrabber.current_instance) --DOC_HIDE
|
||||
assert(not keybinding_works[3])--DOC_HIDE
|
||||
|
||||
|
||||
--DOC_NEWLINE
|
||||
-- Now the keygrabber own keybindings will work
|
||||
root._execute_keybinding({"Mod4", "Shift"}, "i")
|
||||
assert(keybinding_works[3])--DOC_HIDE
|
||||
keybinding_works[2] = 0--DOC_HIDE
|
||||
assert(not awful.keygrabber.current_instance) --DOC_HIDE
|
||||
root.fake_input("key_press" , "a") --DOC_HIDE
|
||||
root.fake_input("key_release" , "a") --DOC_HIDE
|
||||
assert(keybinding_works[2] == 0) --DOC_HIDE
|
|
@ -0,0 +1,4 @@
|
|||
Is now active! nil
|
||||
A key was pressed: a with 0 modifier!
|
||||
A key was pressed: i with 1 modifier!
|
||||
Called again!
|
|
@ -0,0 +1,24 @@
|
|||
|
||||
local awful = { keygrabber = require("awful.keygrabber") } --DOC_HIDE
|
||||
|
||||
local timeout_works = false --DOC_HIDE
|
||||
|
||||
local function print() end --DOC_HIDE be silent
|
||||
|
||||
local g = --DOC_HIDE
|
||||
|
||||
awful.keygrabber {
|
||||
autostart = true,
|
||||
timeout = 1, -- second
|
||||
timeout_callback = function()
|
||||
print("The keygrabber has expired")
|
||||
timeout_works = true --DOC_HIDE
|
||||
end,
|
||||
}
|
||||
|
||||
assert(g) --DOC_HIDE Returning the object works
|
||||
assert(g._private.timer)--DOC_HIDE The timer has been created
|
||||
assert(g._private.timer.started) --DOC_HIDE The timer is started
|
||||
assert(not timeout_works) --DOC_HIDE The callback isn't called by accident
|
||||
g._private.timer:emit_signal("timeout") --DOC_HIDE
|
||||
assert(timeout_works) --DOC_HIDE The callback has been called
|
|
@ -0,0 +1,49 @@
|
|||
--DOC_HEADER --DOC_NO_USAGE
|
||||
|
||||
local gears = {table = require("gears.table")} --DOC_HIDE
|
||||
|
||||
local awful = { keygrabber = require("awful.keygrabber") } --DOC_HIDE
|
||||
|
||||
local map, actions = {
|
||||
verbs = {
|
||||
m = "move" , f = "focus" , d = "delete" , a = "append",
|
||||
w = "swap" , p = "print" , n = "new" ,
|
||||
},
|
||||
adjectives = { h = "left" , j = "down" , k = "up" , l = "right" , },
|
||||
nouns = { c = "client", t = "tag" , s = "screen", y = "layout", },
|
||||
}, {}
|
||||
--DOC_NEWLINE
|
||||
function actions.client(action, adj) print("IN CLIENT!") end --luacheck: no unused args
|
||||
function actions.tag (action, adj) print("IN TAG!" ) end --luacheck: no unused args
|
||||
function actions.screen(action, adj) print("IN SCREEN!") end --luacheck: no unused args
|
||||
function actions.layout(action, adj) print("IN LAYOUT!") end --luacheck: no unused args
|
||||
--DOC_NEWLINE
|
||||
local function parse(_, stop_key, _, sequence)
|
||||
local parsed, count = { verbs = "", adjectives = "", nouns = "", }, ""
|
||||
sequence = sequence..stop_key
|
||||
--DOC_NEWLINE
|
||||
for i=1, #sequence do
|
||||
local char = sequence:sub(i,i)
|
||||
if char >= "0" and char <= "9" then
|
||||
count = count .. char
|
||||
else
|
||||
for kind in pairs(parsed) do
|
||||
parsed[kind] = map[kind][char] or parsed[kind]
|
||||
end
|
||||
end
|
||||
end
|
||||
--DOC_NEWLINE
|
||||
if parsed.nouns == "" then return end
|
||||
|
||||
for _=1, count == "" and 1 or tonumber(count) do
|
||||
actions[parsed.nouns](parsed.verbs, parsed.adjectives)
|
||||
end
|
||||
end
|
||||
--DOC_NEWLINE
|
||||
awful.keygrabber {
|
||||
stop_callback = parse,
|
||||
stop_key = gears.table.keys(map.verbs),
|
||||
root_keybingins = {
|
||||
{{"Mod4"}, "v"}
|
||||
},
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
--DOC_NO_USAGE --DOC_ASTERISK
|
||||
|
||||
local function click(button_id, x, y)
|
||||
mouse.coords {x = x, y = y}
|
||||
root.fake_input("button_press" , button_id)
|
||||
root.fake_input("button_release", button_id)
|
||||
end
|
||||
--DOC_NEWLINE
|
||||
click(1, 42, 42)
|
|
@ -0,0 +1,23 @@
|
|||
--DOC_HEADER --DOC_NO_USAGE --DOC_ASTERISK
|
||||
|
||||
local awful = { keygrabber = require("awful.keygrabber") } --DOC_HIDE
|
||||
|
||||
local works = false --DOC_HIDE
|
||||
|
||||
awful.keygrabber{autostart=true, stop_key = "!", --DOC_HIDE
|
||||
stop_callback = function(_,_,_, seq) works=seq=="Hello world" end} --DOC_HIDE
|
||||
|
||||
local function send_string_to_client(s, c)
|
||||
local old_c = client.focus
|
||||
client.focus = c
|
||||
for i=1, #s do
|
||||
local char = s:sub(i,i)
|
||||
root.fake_input("key_press" , char)
|
||||
root.fake_input("key_release", char)
|
||||
end
|
||||
client.focus = old_c
|
||||
end
|
||||
--DOC_NEWLINE
|
||||
send_string_to_client("Hello world!")
|
||||
|
||||
assert(works) --DOC_HIDE
|
Loading…
Reference in New Issue