diff --git a/awesomerc.lua b/awesomerc.lua
index f7bc68c1e..25b355c74 100644
--- a/awesomerc.lua
+++ b/awesomerc.lua
@@ -219,25 +219,56 @@ end)
-- {{{ Mouse bindings
-- @DOC_ROOT_BUTTONS@
-root.buttons = {
+awful.mouse.append_global_mousebindings({
awful.button({ }, 3, function () mymainmenu:toggle() end),
awful.button({ }, 4, awful.tag.viewnext),
awful.button({ }, 5, awful.tag.viewprev),
-}
+})
-- }}}
-- {{{ Key bindings
-- @DOC_GLOBAL_KEYBINDINGS@
-globalkeys = {
+
+-- General Awesome keys
+awful.keyboard.append_global_keybindings({
awful.key({ modkey, }, "s", hotkeys_popup.show_help,
{description="show help", group="awesome"}),
+ awful.key({ modkey, }, "w", function () mymainmenu:show() end,
+ {description = "show main menu", group = "awesome"}),
+ awful.key({ modkey, "Control" }, "r", awesome.restart,
+ {description = "reload awesome", group = "awesome"}),
+ awful.key({ modkey, "Shift" }, "q", awesome.quit,
+ {description = "quit awesome", group = "awesome"}),
+ awful.key({ modkey }, "x",
+ function ()
+ awful.prompt.run {
+ prompt = "Run Lua code: ",
+ textbox = awful.screen.focused().mypromptbox.widget,
+ exe_callback = awful.util.eval,
+ history_path = awful.util.get_cache_dir() .. "/history_eval"
+ }
+ end,
+ {description = "lua execute prompt", group = "awesome"}),
+ awful.key({ modkey, }, "Return", function () awful.spawn(terminal) end,
+ {description = "open a terminal", group = "launcher"}),
+ awful.key({ modkey }, "r", function () awful.screen.focused().mypromptbox:run() end,
+ {description = "run prompt", group = "launcher"}),
+ awful.key({ modkey }, "p", function() menubar.show() end,
+ {description = "show the menubar", group = "launcher"}),
+})
+
+-- Tags related keybindings
+awful.keyboard.append_global_keybindings({
awful.key({ modkey, }, "Left", awful.tag.viewprev,
{description = "view previous", group = "tag"}),
awful.key({ modkey, }, "Right", awful.tag.viewnext,
{description = "view next", group = "tag"}),
awful.key({ modkey, }, "Escape", awful.tag.history.restore,
{description = "go back", group = "tag"}),
+})
+-- Focus related keybindings
+awful.keyboard.append_global_keybindings({
awful.key({ modkey, }, "j",
function ()
awful.client.focus.byidx( 1)
@@ -250,20 +281,6 @@ globalkeys = {
end,
{description = "focus previous by index", group = "client"}
),
- awful.key({ modkey, }, "w", function () mymainmenu:show() end,
- {description = "show main menu", group = "awesome"}),
-
- -- Layout manipulation
- awful.key({ modkey, "Shift" }, "j", function () awful.client.swap.byidx( 1) end,
- {description = "swap with next client by index", group = "client"}),
- awful.key({ modkey, "Shift" }, "k", function () awful.client.swap.byidx( -1) end,
- {description = "swap with previous client by index", group = "client"}),
- awful.key({ modkey, "Control" }, "j", function () awful.screen.focus_relative( 1) end,
- {description = "focus the next screen", group = "screen"}),
- awful.key({ modkey, "Control" }, "k", function () awful.screen.focus_relative(-1) end,
- {description = "focus the previous screen", group = "screen"}),
- awful.key({ modkey, }, "u", awful.client.urgent.jumpto,
- {description = "jump to urgent client", group = "client"}),
awful.key({ modkey, }, "Tab",
function ()
awful.client.focus.history.previous()
@@ -272,15 +289,31 @@ globalkeys = {
end
end,
{description = "go back", group = "client"}),
+ awful.key({ modkey, "Control" }, "j", function () awful.screen.focus_relative( 1) end,
+ {description = "focus the next screen", group = "screen"}),
+ awful.key({ modkey, "Control" }, "k", function () awful.screen.focus_relative(-1) end,
+ {description = "focus the previous screen", group = "screen"}),
+ awful.key({ modkey, "Control" }, "n",
+ function ()
+ local c = awful.client.restore()
+ -- Focus restored client
+ if c then
+ c:emit_signal(
+ "request::activate", "key.unminimize", {raise = true}
+ )
+ end
+ end,
+ {description = "restore minimized", group = "client"}),
+})
- -- Standard program
- awful.key({ modkey, }, "Return", function () awful.spawn(terminal) end,
- {description = "open a terminal", group = "launcher"}),
- awful.key({ modkey, "Control" }, "r", awesome.restart,
- {description = "reload awesome", group = "awesome"}),
- awful.key({ modkey, "Shift" }, "q", awesome.quit,
- {description = "quit awesome", group = "awesome"}),
-
+-- Layout related keybindings
+awful.keyboard.append_global_keybindings({
+ awful.key({ modkey, "Shift" }, "j", function () awful.client.swap.byidx( 1) end,
+ {description = "swap with next client by index", group = "client"}),
+ awful.key({ modkey, "Shift" }, "k", function () awful.client.swap.byidx( -1) end,
+ {description = "swap with previous client by index", group = "client"}),
+ awful.key({ modkey, }, "u", awful.client.urgent.jumpto,
+ {description = "jump to urgent client", group = "client"}),
awful.key({ modkey, }, "l", function () awful.tag.incmwfact( 0.05) end,
{description = "increase master width factor", group = "layout"}),
awful.key({ modkey, }, "h", function () awful.tag.incmwfact(-0.05) end,
@@ -297,37 +330,7 @@ globalkeys = {
{description = "select next", group = "layout"}),
awful.key({ modkey, "Shift" }, "space", function () awful.layout.inc(-1) end,
{description = "select previous", group = "layout"}),
-
- awful.key({ modkey, "Control" }, "n",
- function ()
- local c = awful.client.restore()
- -- Focus restored client
- if c then
- c:emit_signal(
- "request::activate", "key.unminimize", {raise = true}
- )
- end
- end,
- {description = "restore minimized", group = "client"}),
-
- -- Prompt
- awful.key({ modkey }, "r", function () awful.screen.focused().mypromptbox:run() end,
- {description = "run prompt", group = "launcher"}),
-
- awful.key({ modkey }, "x",
- function ()
- awful.prompt.run {
- prompt = "Run Lua code: ",
- textbox = awful.screen.focused().mypromptbox.widget,
- exe_callback = awful.util.eval,
- history_path = awful.util.get_cache_dir() .. "/history_eval"
- }
- end,
- {description = "lua execute prompt", group = "awesome"}),
- -- Menubar
- awful.key({ modkey }, "p", function() menubar.show() end,
- {description = "show the menubar", group = "launcher"}),
-}
+})
-- @DOC_CLIENT_KEYBINDINGS@
clientkeys = {
@@ -380,7 +383,8 @@ clientkeys = {
-- This should map on the top row of your keyboard, usually 1 to 9.
for i = 1, 9 do
-- View tag only.
- table.insert(globalkeys, awful.key({ modkey }, "#" .. i + 9,
+ awful.keyboard.append_global_keybinding(awful.key(
+ { modkey }, "#" .. i + 9,
function ()
local screen = awful.screen.focused()
local tag = screen.tags[i]
@@ -388,11 +392,12 @@ for i = 1, 9 do
tag:view_only()
end
end,
- {description = "view tag #"..i, group = "tag"})
- )
+ {description = "view tag #"..i, group = "tag"}
+ ))
-- Toggle tag display.
- table.insert(globalkeys, awful.key({ modkey, "Control" }, "#" .. i + 9,
+ awful.keyboard.append_global_keybinding(awful.key(
+ { modkey, "Control" }, "#" .. i + 9,
function ()
local screen = awful.screen.focused()
local tag = screen.tags[i]
@@ -400,11 +405,12 @@ for i = 1, 9 do
awful.tag.viewtoggle(tag)
end
end,
- {description = "toggle tag #" .. i, group = "tag"})
- )
+ {description = "toggle tag #" .. i, group = "tag"}
+ ))
-- Move client to tag.
- table.insert(globalkeys, awful.key({ modkey, "Shift" }, "#" .. i + 9,
+ awful.keyboard.append_global_keybinding(awful.key(
+ { modkey, "Shift" }, "#" .. i + 9,
function ()
if client.focus then
local tag = client.focus.screen.tags[i]
@@ -413,11 +419,12 @@ for i = 1, 9 do
end
end
end,
- {description = "move focused client to tag #"..i, group = "tag"})
- )
+ {description = "move focused client to tag #"..i, group = "tag"}
+ ))
-- Toggle tag on focused client.
- table.insert(globalkeys, awful.key({ modkey, "Control", "Shift" }, "#" .. i + 9,
+ awful.keyboard.append_global_keybinding(awful.key(
+ { modkey, "Control", "Shift" }, "#" .. i + 9,
function ()
if client.focus then
local tag = client.focus.screen.tags[i]
@@ -426,8 +433,8 @@ for i = 1, 9 do
end
end
end,
- {description = "toggle focused client on tag #" .. i, group = "tag"})
- )
+ {description = "toggle focused client on tag #" .. i, group = "tag"}
+ ))
end
-- @DOC_CLIENT_BUTTONS@
@@ -445,8 +452,6 @@ clientbuttons = {
end),
}
--- Set keys
-root.keys = globalkeys
-- }}}
-- {{{ Rules
diff --git a/common/luaclass.c b/common/luaclass.c
index e3533a35d..d5c784f39 100644
--- a/common/luaclass.c
+++ b/common/luaclass.c
@@ -437,16 +437,23 @@ luaA_class_index(lua_State *L)
lua_class_property_t *prop = luaA_class_property_get(L, class, 2);
- /* Is this the special 'data' property? This is available on all objects and
- * thus not implemented as a lua_class_property_t.
+ /* This is the table storing the object private variables.
*/
- if (A_STREQ(attr, "data"))
+ if (A_STREQ(attr, "_private"))
{
luaA_checkudata(L, 1, class);
luaA_getuservalue(L, 1);
lua_getfield(L, -1, "data");
return 1;
}
+ else if (A_STREQ(attr, "data"))
+ {
+ luaA_deprecate(L, "Use `._private` instead of `.data`");
+ luaA_checkudata(L, 1, class);
+ luaA_getuservalue(L, 1);
+ lua_getfield(L, -1, "data");
+ return 1;
+ }
/* Property does exist and has an index callback */
if(prop)
diff --git a/docs/common/rules_index.ldoc b/docs/common/rules_index.ldoc
index 8f5e2f538..eeecdbda3 100644
--- a/docs/common/rules_index.ldoc
+++ b/docs/common/rules_index.ldoc
@@ -69,4 +69,6 @@
--
startup\_id | The FreeDesktop StartId |
-- valid | If the client that this object refers to is still managed by awesome |
-- first\_tag | The first tag of the client |
+-- buttons | Get or set mouse buttons bindings for a client |
+-- keys | Get or set keys bindings for a client |
--
diff --git a/docs/config.ld b/docs/config.ld
index 120ba85c4..76508b46e 100644
--- a/docs/config.ld
+++ b/docs/config.ld
@@ -54,6 +54,7 @@ tparam_alias('screen_or_idx', 'screen|int')
-- The first stereotype are the constructors.
new_type("constructorfct", "Constructors", false, "Parameters")
+new_type("constructorfct2", "ldoc_skip", false, "Parameters")
-- Hack to get the functions on top of the signals and properties
new_type("function", "Functions", false, "Parameters")
-- For "classes", use an explicit type for static functions. This allows
@@ -100,6 +101,7 @@ sort_modules=true
-- Add more project level (left side index) types.
new_type("coreclassmod", "Core_components" , true)
+new_type("inputmodule" , "Input handling" , true)
new_type("widgetmod" , "Widgets" , true)
new_type("containermod", "Widget_containers", true)
new_type("layoutmod" , "Widget_layouts" , true)
@@ -264,17 +266,19 @@ local coreclassmap = {
-- Add the full module name in front.
local add_mod = {
- ["function"] = true,
- constructorfct = true,
- staticfct = true,
- deprecated = true,
- field = true,
+ ["function"] = true,
+ constructorfct = true,
+ constructorfct2 = true,
+ staticfct = true,
+ deprecated = true,
+ field = true,
}
-- Add the arguments.
local add_args = {
- constructorfct = true,
- staticfct = true,
+ constructorfct = true,
+ constructorfct2 = true,
+ staticfct = true,
}
-- Add a type column to the summary and type field in the description.
@@ -287,11 +291,12 @@ local display_type = {
-- Show return values.
local show_return = {
- ["function"] = true,
- constructorfct = true,
- staticfct = true,
- method = true,
- deprecated = true,
+ ["function"] = true,
+ constructorfct = true,
+ constructorfct2 = true,
+ staticfct = true,
+ method = true,
+ deprecated = true,
}
custom_display_name_handler = function(item, default_handler)
diff --git a/docs/ldoc.ltp b/docs/ldoc.ltp
index ee8f6534f..eebb6b436 100644
--- a/docs/ldoc.ltp
+++ b/docs/ldoc.ltp
@@ -49,22 +49,28 @@
Contents
# for kind,items in module.kinds() do
+# if not kind:match("^ldoc_skip") then
- $(kind)
# end
+# end
# end
# if ldoc.no_summary and module and not ldoc.one then -- bang out the functions on the side
# for kind, items in module.kinds() do
-$(kind)
+# if not kind:match("^ldoc_skip") then
+$(kind)dasdasd
# end
# end
+# end
# -------- contents of project ----------
# local this_mod = module and module.name
# for kind, mods, type in ldoc.kinds() do
@@ -125,9 +131,15 @@
# if not ldoc.no_summary then
# -- bang out the tables of item types for this module (e.g Functions, Tables, etc)
+# local last_kind = ""
# for kind,items in module.kinds() do
+# if not kind:match("^ldoc_skip") then
+# if last_kind ~= "" then
+
+# end
+# end
# for item in items() do
# local dn = display_name(item)
# if item.sanitize_type then item.sanitize_type(item, ldoc) end
@@ -141,8 +153,9 @@
$(M(item.summary,item)) |
# end -- for items
-
+# last_kind = kind
#end -- for kinds
+
@@ -152,11 +165,16 @@
# --- currently works for both Functions and Tables. The params field either contains
# --- function parameters or table fields.
# local show_return = not ldoc.no_return_or_parms
-# local show_parms = show_return
+# local show_parms, last_kind = show_return, ""
# for kind, items in module.kinds() do
# local kitem = module.kinds:get_item(kind)
# local has_description = kitem and ldoc.descript(kitem) ~= ""
+# if not kind:match("^ldoc_skip") then
+# if last_kind ~= "" then
+
+# end
$(kind)
+# end
$(M(module.kinds:get_section_description(kind),nil))
# if kitem then
# if has_description then
@@ -169,7 +187,9 @@
$(ldoc.prettify(kitem.usage[1]))
# end
# end
+# if not kind:match("^ldoc_skip") then
+# end
# for item in items() do
-
@@ -289,8 +309,9 @@
# end -- for items
-
+# last_kind = kind
# end -- for kinds
+
# else -- if module; project-level contents
diff --git a/lib/awful/_compat.lua b/lib/awful/_compat.lua
index b7af2bb2d..c25714f0e 100644
--- a/lib/awful/_compat.lua
+++ b/lib/awful/_compat.lua
@@ -77,4 +77,4 @@ gprop._legacy_accessors(capi.root, "keys", "_keys", false, function(new_btns)
) or false
end, true)
-assert(root.keys)
+require("awful.root")
diff --git a/lib/awful/button.lua b/lib/awful/button.lua
index b59303f05..4fbe98d6a 100644
--- a/lib/awful/button.lua
+++ b/lib/awful/button.lua
@@ -2,19 +2,29 @@
--- Create easily new buttons objects ignoring certain modifiers.
--
-- @author Julien Danjou <julien@danjou.info>
+-- @author Emmanuel Lepage Vallee <elv1313@gmail.com>
+-- @copyright 2018 Emmanuel Lepage Vallee
-- @copyright 2009 Julien Danjou
--- @classmod awful.button
+-- @inputmodule awful.button
---------------------------------------------------------------------------
-- Grab environment we need
local setmetatable = setmetatable
local ipairs = ipairs
-local capi = { button = button }
+local capi = { button = button, root = root }
local gmath = require("gears.math")
local gtable = require("gears.table")
+local gobject = require("gears.object")
local button = { mt = {} }
+-- Due to non trivial abuse or `pairs` in older code, the private data cannot
+-- be stored in the object itself without creating subtle bugs. This cannot be
+-- fixed internally because the default `rc.lua` uses `gears.table.join`, which
+-- is affected.
+--TODO v6: Drop this
+local reverse_map = setmetatable({}, {__mode="k"})
+
--- Modifiers to ignore.
--
-- By default this is initialized as `{ "Lock", "Mod2" }`
@@ -24,38 +34,289 @@ local button = { mt = {} }
-- @table ignore_modifiers
local ignore_modifiers = { "Lock", "Mod2" }
---- Create a new button to use as binding.
+--- The mouse buttons names.
--
--- This function is useful to create several buttons from one, because it will use
--- the ignore_modifier variable to create more button with or without the ignored
--- modifiers activated.
+-- It can be used instead of the button ids.
--
--- For example if you want to ignore CapsLock in your buttonbinding (which is
--- ignored by default by this function), creating button binding with this function
--- will return 2 button objects: one with CapsLock on, and the other one with
--- CapsLock off.
+-- @table names
+button.names = {
+ LEFT = 1,-- The left mouse button.
+ MIDDLE = 2,-- The scrollwheel button.
+ RIGHT = 3,-- The context menu button.
+ SCROLL_UP = 4,-- A scroll up increment.
+ SCROLL_DOWN = 5,-- A scroll down increment.
+}
+
+--- The table of modifier keys.
--
--- @treturn table A table with one or several button objects.
-function button.new(mod, _button, press, release)
- local ret = {}
+-- A modifier, such as `Control` are a predetermined set of keys that can be
+-- used to implement keybindings. Note that this list is fix and cannot be
+-- extended using random key names, code or characters.
+--
+-- Common modifiers are:
+--
+--
+--
+-- Please note that Awesome ignores the status of "Lock" and "Mod2" (Num Lock).
+--
+-- @property modifiers
+
+--- The mouse button identifier.
+--
+-- ![Mouse buttons](../images/mouse.svg)
+--
+-- @property button
+-- @param integer
+
+--- The button description.
+--
+-- @property description
+-- @param string
+
+--- The button name.
+--
+-- @property name
+-- @param string
+
+--- The button group.
+--
+-- @property group
+-- @param string
+
+--- The callback when this button is pressed.
+--
+-- @property on_press
+-- @param function
+
+--- The callback when this button is released.
+--
+-- @property on_release
+-- @param function
+
+--- Execute this mousebinding.
+-- @method :trigger
+
+function button:set_button(b)
+ for _, v in ipairs(self) do
+ v.button = b
+ end
+end
+
+function button:set_modifiers(mod)
local subsets = gmath.subsets(ignore_modifiers)
- for _, set in ipairs(subsets) do
- ret[#ret + 1] = capi.button({ modifiers = gtable.join(mod, set),
- button = _button })
- if press then
- ret[#ret]:connect_signal("press", function(_, ...) press(...) end)
+ for k, set in ipairs(subsets) do
+ self[k].modifiers = gtable.join(mod, set)
+ end
+end
+
+for _, prop in ipairs { "description", "group", "name" } do
+ button["get_"..prop] = function(self)
+ return reverse_map[self][prop]
+ end
+ button["set_"..prop] = function(self, value)
+ reverse_map[self][prop] = value
+ end
+end
+
+-- Store the callbacks as weakly as possible.
+--
+-- Lua 5.1 GC is not very smart with reference loops. When the titlebar has
+-- buttons, they usually contain the client in their lambda. In turn, storing
+-- the callback in something stored by the titlebar, which is stored by the
+-- clients, will not be GC-able as-is.
+--
+-- To work around this, we use weak `v` references stored within a weak `k`
+-- table. So both the `awful.button` and the callbacks are weak in their own
+-- realm. To avoid the GC of the callback, it is important to use
+-- `connect_signal` on the `capi.button` to keep a reference alive.
+for _, prop in ipairs { "press", "release" } do
+ local long_name = "on_"..prop
+
+ button["get_"..long_name] = function(self)
+ return reverse_map[self].weak_content[long_name]
+ end
+ button["set_"..long_name] = function(self, value)
+ local old = reverse_map[self].weak_content[long_name]
+ local new = function(_, ...) value(...) end
+
+ if old then
+ for i=1, 4 do
+ self[i]:disconnect_signal(prop, old)
+ end
end
- if release then
- ret[#ret]:connect_signal("release", function (_, ...) release(...) end)
+
+ reverse_map[self].weak_content[prop] = new
+
+ for i=1, 4 do
+ self[i]:connect_signal(prop, new)
end
end
- return ret
+end
+
+function button:get_button()
+ return self[1].button
+end
+
+function button:trigger()
+ local data = reverse_map[self]
+
+ local press = self.on_press or data.weak_content.press
+
+ if press then
+ press()
+ end
+
+ local release = self.on_release or data.weak_content.release
+
+ if release then
+ release()
+ end
+end
+
+function button:get_has_root_binding()
+ return capi.root.has_button(self)
+end
+
+-- This is used by the keygrabber and prompt to identify valid awful.button
+-- objects. It *cannot* be put directly in the object since `capi` uses a lot
+-- of `next` internally and fixing that would suck more.
+function button:get__is_awful_button()
+ return true
+end
+
+local function index_handler(self, k)
+ if button["get_"..k] then
+ return button["get_"..k](self)
+ end
+
+ if type(button[k]) == "function" then
+ return button[k]
+ end
+
+ local data = reverse_map[self]
+ assert(data)
+
+ if data[k] ~= nil then
+ return data[k]
+ else
+ return data.weak_content[k]
+ end
+end
+
+local function newindex_handler(self, key, value)
+ if button["set_"..key] then
+ return button["set_"..key](self, value)
+ end
+
+ local data = reverse_map[self]
+ assert(data)
+
+ if data.weak_content[key] ~= nil then
+ data.weak_content[key] = value
+ else
+ data[key] = value
+ end
+end
+
+local obj_mt = {
+ __index = index_handler,
+ __newindex = newindex_handler
+}
+
+--- Create a new button to use as binding.
+--
+-- @constructorfct awful.button
+-- @tparam table mod A list of modifier keys. Valid modifiers are:
+-- `Any`, `Mod1`, Mod2`, `Mod3`, `Mod4`, `Mod5`, `Shift`, `Lock` and `Control`.
+-- This argument is (**mandatory**).
+-- @tparam number button The mouse button (it is recommended to use the
+-- `awful.button.names` constants.
+-- @tparam function press Callback for when the button is pressed.
+-- @tparam function release Callback for when the button is released.
+-- @treturn table An `awful.button` object.
+
+--- Create a new button to use as binding.
+--
+-- @constructorfct2 awful.button
+-- @tparam table args
+-- @tparam table args.modifiers A list of modifier keys. Valid modifiers are:
+-- `Any`, `Mod1`, Mod2`, `Mod3`, `Mod4`, `Mod5`, `Shift`, `Lock` and `Control`.
+-- This argument is (**mandatory**).
+-- @tparam number args.button The mouse button (it is recommended to use the
+-- `awful.button.names` constants.
+-- @tparam function args.on_press Callback for when the button is pressed.
+-- @tparam function args.on_release Callback for when the button is released.
+-- @treturn table An `awful.button` object.
+
+local function new_common(mod, _button, press, release)
+ local ret = {}
+ local subsets = gmath.subsets(ignore_modifiers)
+
+ for _, set in ipairs(subsets) do
+ local sub_button = capi.button {
+ modifiers = gtable.join(mod, set),
+ button = _button
+ }
+
+ sub_button._private._legacy_convert_to = ret
+
+ ret[#ret + 1] = sub_button
+ end
+
+ reverse_map[ret] = {
+ -- Use weak tables to let Lua 5.1 and Luajit GC the `awful.buttons`,
+ -- Lua 5.3 is smart enough to figure this out.
+ weak_content = setmetatable({
+ press = press,
+ release = release,
+ }, {__mode = "v"}),
+ _is_capi_button = false
+ }
+
+ if press then
+ button.set_on_press(ret, press)
+ end
+
+ if release then
+ button.set_on_release(ret, release)
+ end
+
+ return setmetatable(ret, obj_mt)
+end
+
+function button.new(args, _button, press, release)
+ -- Assume this is the new constructor.
+ if not _button then
+ assert(not (press or release), "Calling awful.button() requires a button name")
+ return new_common(
+ args.modifiers,
+ args.button,
+ args.on_press,
+ args.on_release
+ )
+ else
+ return new_common(args, _button, press, release)
+ end
end
function button.mt:__call(...)
return button.new(...)
end
+gobject.properties(capi.button, {
+ auto_emit = true,
+})
+
return setmetatable(button, button.mt)
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
diff --git a/lib/awful/client.lua b/lib/awful/client.lua
index ccf28325a..630733264 100644
--- a/lib/awful/client.lua
+++ b/lib/awful/client.lua
@@ -1167,8 +1167,8 @@ end)
-- @return The property.
-- @deprecated awful.client.property.get
function client.property.get(c, prop)
- if not c.data._persistent_properties_loaded then
- c.data._persistent_properties_loaded = true
+ if not c._private._persistent_properties_loaded then
+ c._private._persistent_properties_loaded = true
for p in pairs(client.data.persistent_properties_registered) do
local value = c:get_xproperty("awful.client.property." .. p)
if value ~= nil then
@@ -1176,8 +1176,8 @@ function client.property.get(c, prop)
end
end
end
- if c.data.awful_client_properties then
- return c.data.awful_client_properties[prop]
+ if c._private.awful_client_properties then
+ return c._private.awful_client_properties[prop]
end
end
@@ -1191,14 +1191,14 @@ end
-- @param value The value.
-- @deprecated awful.client.property.set
function client.property.set(c, prop, value)
- if not c.data.awful_client_properties then
- c.data.awful_client_properties = {}
+ if not c._private.awful_client_properties then
+ c._private.awful_client_properties = {}
end
- if c.data.awful_client_properties[prop] ~= value then
+ if c._private.awful_client_properties[prop] ~= value then
if client.data.persistent_properties_registered[prop] then
c:set_xproperty("awful.client.property." .. prop, value)
end
- c.data.awful_client_properties[prop] = value
+ c._private.awful_client_properties[prop] = value
c:emit_signal("property::" .. prop)
end
end
@@ -1216,8 +1216,8 @@ function client.property.persist(prop, kind)
-- Make already-set properties persistent
for c in pairs(capi.client.get()) do
- if c.data.awful_client_properties and c.data.awful_client_properties[prop] ~= nil then
- c:set_xproperty(xprop, c.data.awful_client_properties[prop])
+ if c._private.awful_client_properties and c._private.awful_client_properties[prop] ~= nil then
+ c:set_xproperty(xprop, c._private.awful_client_properties[prop])
end
end
end
diff --git a/lib/awful/init.lua b/lib/awful/init.lua
index cf5764273..992590324 100644
--- a/lib/awful/init.lua
+++ b/lib/awful/init.lua
@@ -24,6 +24,7 @@ return
mouse = require("awful.mouse");
remote = require("awful.remote");
key = require("awful.key");
+ keyboard = require("awful.keyboard");
button = require("awful.button");
wibar = require("awful.wibar");
wibox = require("awful.wibox");
diff --git a/lib/awful/key.lua b/lib/awful/key.lua
index 5826a7c97..6d8b2dcc9 100644
--- a/lib/awful/key.lua
+++ b/lib/awful/key.lua
@@ -2,8 +2,9 @@
--- Create easily new key objects ignoring certain modifiers.
--
-- @author Julien Danjou <julien@danjou.info>
--- @copyright 2009 Julien Danjou
--- @classmod awful.key
+-- @author Emmanuel Lepage Vallee <elv1313@gmail.com>
+-- @copyright 2018 Emmanuel Lepage Vallee
+-- @inputmodule awful.key
---------------------------------------------------------------------------
-- Grab environment we need
@@ -12,41 +13,168 @@ local ipairs = ipairs
local capi = { key = key, root = root, awesome = awesome }
local gmath = require("gears.math")
local gtable = require("gears.table")
+local gdebug = require("gears.debug")
+local gobject = require("gears.object")
+
+--- The keyboard key used to trigger this keybinding.
+--
+-- It can be the key symbol, such as `space`, the character, such as ` ` or the
+-- keycode such as `#65`.
+--
+-- @property key
+-- @param string
+
+--- The table of modifier keys.
+--
+-- A modifier, such as `Control` are a predetermined set of keys that can be
+-- used to implement keybindings. Note that this list is fix and cannot be
+-- extended using random key names, code or characters.
+--
+-- Common modifiers are:
+--
+--
+--
+-- Please note that Awesome ignores the status of "Lock" and "Mod2" (Num Lock).
+--
+-- @property modifiers
+-- @tparam table modifiers
+
+--- The key description.
+--
+-- This is used, for example, by the `awful.hotkey_popup`.
+--
+-- @property description
+-- @param string
+
+--- The key name.
+--
+-- This can be useful when searching for keybindings by keywords.
+--
+-- @property name
+-- @param string
+
+--- The key group.
+--
+-- This is used, for example, by the `awful.hotkey_popup`.
+--
+-- @property group
+-- @param string
+
+--- The callback when this key is pressed.
+--
+-- @property on_press
+-- @param function
+
+--- The callback when this key is released.
+--
+-- @property on_release
+-- @param function
local key = { mt = {}, hotkeys = {} }
+local reverse_map = setmetatable({}, {__mode="k"})
+
+function key:set_key(k)
+ for _, v in ipairs(self) do
+ v.key = k
+ end
+end
+
+function key:get_key()
+ return self[1].key
+end
+
+function key:set_modifiers(mod)
+ local subsets = gmath.subsets(key.ignore_modifiers)
+ for k, set in ipairs(subsets) do
+ self[k].modifiers = gtable.join(mod, set)
+ end
+end
+
+for _, prop in ipairs { "description", "group", "on_press", "on_release", "name" } do
+ key["get_"..prop] = function(self)
+ return reverse_map[self][prop]
+ end
+ key["set_"..prop] = function(self, value)
+ reverse_map[self][prop] = value
+ end
+end
+
+--- Execute this keybinding.
+--
+-- @method :trigger
+
+function key:trigger()
+ local data = reverse_map[self]
+ if data.on_press then
+ data.on_press()
+ end
+
+ if data.on_release then
+ data.on_release()
+ end
+end
+
+function key:get_has_root_binding()
+ return capi.root.has_key(self)
+end
+
+-- This is used by the keygrabber and prompt to identify valid awful.key
+-- objects. It *cannot* be put directly in the object since `capi` uses a lot
+-- of `next` internally and fixing that would suck more.
+function key:get__is_awful_key()
+ return true
+end
+
+local function index_handler(self, k)
+ if key["get_"..k] then
+ return key["get_"..k](self)
+ end
+
+ if type(key[k]) == "function" then
+ return key[k]
+ end
+
+ local data = reverse_map[self]
+ assert(data)
+
+ return data[k]
+end
+
+local function newindex_handler(self, k, value)
+ if key["set_"..k] then
+ return key["set_"..k](self, value)
+ end
+
+ local data = reverse_map[self]
+ assert(data)
+
+ data[k] = value
+end
+
+local obj_mt = {
+ __index = index_handler,
+ __newindex = newindex_handler
+}
+
--- Modifiers to ignore.
--- By default this is initialized as { "Lock", "Mod2" }
+-- By default this is initialized as `{ "Lock", "Mod2" }`
-- so the Caps Lock or Num Lock modifier are not taking into account by awesome
-- when pressing keys.
-- @name awful.key.ignore_modifiers
-- @class table
key.ignore_modifiers = { "Lock", "Mod2" }
---- Convert the modifiers into pc105 key names
-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.
@@ -59,89 +187,106 @@ capi.awesome.connect_signal("xkb::map_changed" , function() conversion = nil en
-- @tparam table mod A modified table. Valid modifiers are: Any, Mod1,
-- Mod2, Mod3, Mod4, Mod5, Shift, Lock and Control.
-- @tparam string k The key
--- @staticfct awful.key.execute
+-- @deprecated awful.key.execute
function key.execute(mod, k)
- local modmap = generate_conversion_map()
- local active = capi.awesome._active_modifiers
+ gdebug.deprecate("Use `awful.keyboard.emulate_key_combination` or "..
+ "`my_key:trigger()` instead of `awful.key.execute()`",
+ {deprecated_in=5}
+ )
- -- 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 = modmap[v]
- if m then
- root.fake_input("key_press", m)
- end
- end
-
- root.fake_input("key_press" , k)
- root.fake_input("key_release", k)
-
- for _, v in ipairs(mod) do
- 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
+ require("awful.keyboard").emulate_key_combination(mod, k)
end
---- Create a new key to use as binding.
--- This function is useful to create several keys from one, because it will use
--- the ignore_modifier variable to create several keys with and without the
--- ignored modifiers activated.
--- For example if you want to ignore CapsLock in your keybinding (which is
--- ignored by default by this function), creating a key binding with this
--- function will return 2 key objects: one with CapsLock on, and another one
--- with CapsLock off.
+--- Create a new key binding.
--
--- @tparam table mod A list of modifier keys. Valid modifiers are: Any, Mod1,
--- Mod2, Mod3, Mod4, Mod5, Shift, Lock and Control.
--- @tparam string _key The key to trigger an event.
+-- @constructorfct2 awful.key
+-- @tparam table args
+-- @tparam function args.key The key to trigger an event. It can be the character
+-- itself of `#+keycode` (**mandatory**).
+-- @tparam function args.modifiers A list of modifier keys. Valid modifiers are:
+-- `Any`, `Mod1`, Mod2`, `Mod3`, `Mod4`, `Mod5`, `Shift`, `Lock` and `Control`.
+-- This argument is (**mandatory**).
+-- @tparam function args.on_press Callback for when the key is pressed.
+-- @tparam function args.on_release Callback for when the key is released.
+
+--- Create a new key binding (alternate constructor).
+--
+-- @tparam table mod A list of modifier keys. Valid modifiers are: `Any`,
+-- `Mod1`, `Mod2`, `Mod3`, `Mod4`, `Mod5`, `Shift`, `Lock` and `Control`.
+-- @tparam string _key The key to trigger an event. It can be the character
+-- itself of `#+keycode`.
-- @tparam function press Callback for when the key is pressed.
-- @tparam[opt] function release Callback for when the key is released.
--- @tparam table data User data for key,
+-- @tparam[opt] table data User data for key,
-- for example {description="select next tag", group="tag"}.
-- @treturn table A table with one or several key objects.
-- @constructorfct awful.key
-function key.new(mod, _key, press, release, data)
+local function new_common(mod, _key, press, release, data)
if type(release)=='table' then
data=release
release=nil
end
+
local ret = {}
local subsets = gmath.subsets(key.ignore_modifiers)
for _, set in ipairs(subsets) do
- ret[#ret + 1] = capi.key({ modifiers = gtable.join(mod, set),
- key = _key })
- if press then
- ret[#ret]:connect_signal("press", function(_, ...) press(...) end)
- end
- if release then
- ret[#ret]:connect_signal("release", function(_, ...) release(...) end)
- end
+ local sub_key = capi.key {
+ modifiers = gtable.join(mod, set),
+ key = _key
+ }
+
+ sub_key._private._legacy_convert_to = ret
+
+ sub_key:connect_signal("press", function(_, ...)
+ if ret.on_press then
+ ret.on_press(...)
+ end
+ end)
+
+ sub_key:connect_signal("release", function(_, ...)
+ if ret.on_release then
+ ret.on_release(...)
+ end
+ end)
+
+ ret[#ret + 1] = sub_key
end
-- append custom userdata (like description) to a hotkey
data = data and gtable.clone(data) or {}
data.mod = mod
data.key = _key
- data.press = press
- data.release = release
+ data.on_press = press
+ data.on_release = release
+ data._is_capi_key = false
table.insert(key.hotkeys, data)
data.execute = function(_) key.execute(mod, _key) end
- return ret
+ -- Store the private data
+ reverse_map[ret] = data
+
+ --WARNING this object needs to expose only ordered keys for legacy reasons.
+ -- All other properties needs to be fully handled by the meta table and never
+ -- be stored directly in the object.
+
+ return setmetatable(ret, obj_mt)
+end
+
+function key.new(args, _key, press, release, data)
+ -- Assume this is the new constructor.
+ if not _key then
+ assert(not (press or release or data), "Calling awful.key() requires a key name")
+ return new_common(
+ args.modifiers,
+ args.key,
+ args.on_press,
+ args.on_release,
+ args
+ )
+ else
+ return new_common(args, _key, press, release, data)
+ end
end
--- Compare a key object with modifiers and key.
@@ -172,6 +317,10 @@ function key.mt:__call(...)
return key.new(...)
end
+gobject.properties(capi.key, {
+ auto_emit = true,
+})
+
return setmetatable(key, key.mt)
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
diff --git a/lib/awful/keyboard.lua b/lib/awful/keyboard.lua
new file mode 100644
index 000000000..da549c8b3
--- /dev/null
+++ b/lib/awful/keyboard.lua
@@ -0,0 +1,141 @@
+---------------------------------------------------------------------------
+--- Utilities related to the keyboard and keybindings.
+--
+-- @author Emmanuel Lepage Vallee <elv1313@gmail.com>
+-- @copyright 2018-2019 Emmanuel Lepage Vallee
+-- @inputmodule awful.keyboard
+---------------------------------------------------------------------------
+
+local capi = {root = root, awesome = awesome}
+local module = {}
+
+--- Convert the modifiers into pc105 key names
+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 modifiers A modified table. Valid modifiers are: `Any`, `Mod1`,
+-- `Mod2`, `Mod3`, `Mod4`, `Mod5`, `Shift`, `Lock` and `Control`.
+-- @tparam string key The key.
+-- @staticfct awful.keyboard.emulate_key_combination
+function module.emulate_key_combination(modifiers, key)
+ local modmap = generate_conversion_map()
+ local active = capi.awesome._active_modifiers
+
+ -- Release all modifiers
+ for _, m in ipairs(active) do
+ assert(modmap[m])
+ capi.root.fake_input("key_release", modmap[m])
+ end
+
+ for _, v in ipairs(modifiers) do
+ local m = modmap[v]
+ if m then
+ capi.root.fake_input("key_press", m)
+ end
+ end
+
+ capi.root.fake_input("key_press" , key)
+ capi.root.fake_input("key_release", key)
+
+ for _, v in ipairs(modifiers) do
+ local m = modmap[v]
+ if m then
+ capi.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
+ capi.root.fake_input("key_press", modmap[m])
+ end
+end
+
+--- Add an `awful.key` based keybinding to the global set.
+--
+-- A **global** keybinding is one which is always present, even when there is
+-- no focused client. If your intent is too add a keybinding which acts on
+-- the focused client do **not** use this.
+--
+-- @staticfct awful.keyboard.append_global_keybinding
+-- @tparam awful.key key The key object.
+-- @see awful.key
+-- @see awful.keyboard.append_global_keybindings
+-- @see awful.keyboard.remove_global_keybinding
+
+function module.append_global_keybinding(key)
+ capi.root._append_key(key)
+end
+
+--- Add multiple `awful.key` based keybindings to the global set.
+--
+-- A **global** keybinding is one which is always present, even when there is
+-- no focused client. If your intent is too add a keybinding which acts on
+-- the focused client do **not** use this
+--
+-- @tparam table keys A table of `awful.key` objects. Optionally, it can have
+-- a `group` entry. If set, the `group` property will be set on all `awful.keys`
+-- objects.
+-- @see awful.key
+-- @see awful.keyboard.append_global_keybinding
+-- @see awful.keyboard.remove_global_keybinding
+
+function module.append_global_keybindings(keys)
+ local g = keys.group
+ keys.group = nil
+
+ -- Avoid the boilerplate. If the user is adding multiple keys at once, then
+ -- they are probably related.
+ if g then
+ for _, k in ipairs(keys) do
+ k.group = g
+ end
+ end
+
+ capi.root._append_keys(keys)
+ keys.group = g
+end
+
+--- Remove a keybinding from the global set.
+--
+-- @staticfct awful.keyboard.remove_global_keybinding
+-- @tparam awful.key key The key object.
+-- @see awful.key
+-- @see awful.keyboard.append_global_keybinding
+
+function module.remove_global_keybinding(key)
+ capi.root._remove_key(key)
+end
+
+return module
diff --git a/lib/awful/keygrabber.lua b/lib/awful/keygrabber.lua
index 2d076c672..6d5c45ae9 100644
--- a/lib/awful/keygrabber.lua
+++ b/lib/awful/keygrabber.lua
@@ -71,6 +71,7 @@ local unpack = unpack or table.unpack -- luacheck: globals unpack (compatibility
local gtable = require("gears.table")
local gobject = require("gears.object")
local gtimer = require("gears.timer")
+local akeyboard = require("awful.keyboard")
local glib = require("lgi").GLib
local capi = { keygrabber = keygrabber, root = root, awesome = awesome }
@@ -90,9 +91,6 @@ local conversion = nil
--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 = {}
-
-- Read the modifiers name and map their keysyms to the modkeys
local function generate_conversion_map()
if conversion then return conversion end
@@ -119,60 +117,24 @@ local function add_root_keybindings(self, list)
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.
- table.insert(delay_list, {self, 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 _, obj in ipairs(delay_list) do
- local obj_self, obj_list = obj[1], obj[2]
-
- for _, v in ipairs(obj_list) do
- local mods, key, press, release, data = unpack(v)
-
- if type(release) == 'table' then
- data = release
- release = nil
- end
-
- if press then
- local old_press = press
- press = function(...)
- obj_self:start()
- old_press(...)
- end
- end
-
- if release then
- local old_release = release
- release = function(...)
- obj_self:start()
- old_release(...)
- end
- end
-
- table.insert(ret, akey(mods, key, press, release, data))
- end
+ for _, kb in ipairs(list) do
+ if kb.on_press then
+ local old_press = kb.on_press
+ kb.on_press = function(...)
+ self:start()
+ old_press(...)
end
+ end
- -- Wow...
- capi.root.keys(gtable.join( capi.root.keys(), unpack(ret) ))
+ if kb.on_release then
+ local old_release = kb.on_release
+ kb.on_release = function(...)
+ self:start()
+ old_release(...)
+ end
+ end
- delay_list = {}
- end)
+ akeyboard.append_global_keybinding(kb)
end
end
@@ -249,13 +211,13 @@ local function runner(self, modifiers, key, event)
end
for _,v in ipairs(self._private.keybindings[key]) do
- if #filtered_modifiers == #v[1] then
+ if #filtered_modifiers == #v.modifiers then
local match = true
- for _,v2 in ipairs(v[1]) do
+ for _,v2 in ipairs(v.modifiers) do
match = match and mod[v2]
end
- if match then
- v[3](self)
+ if match and v.on_press then
+ v.on_press(self)
if self.mask_event_callback ~= false then
return
@@ -400,20 +362,13 @@ end
--- 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`.
+-- This property contains a table of `awful.key` objects.
--
-- @property keybindings
-- @param table
-- @see export_keybindings
-- @see root_keybindings
+-- @see awful.key
--- If any key is pressed that is not in this list, the keygrabber is stopped.
--
@@ -538,27 +493,62 @@ end
-- Those keybindings will automatically start the keygrabbing when hit.
--
-- @method add_keybinding
--- @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 awful.key key The key.
-- @tparam string description.group The keybinding group
-function keygrabber:add_keybinding(mods, key, callback, description)
+
+function keygrabber:add_keybinding(key, _keycode, _callback, _description)
+ local mods = not akey._is_awful_key and akey or nil
+
+ if mods then
+ gdebug.deprecate(":add_keybinding now takes `awful.key` objects instead"
+ .. " of multiple parameters",
+ {deprecated_in=5}
+ )
+
+ key = akey {
+ modifiers = mods,
+ key = _keycode,
+ description = _description,
+ on_press = _callback
+ }
+ else
+ _keycode = key.key
+ end
+
self._private.keybindings[key] = self._private.keybindings[key] or {}
- table.insert(self._private.keybindings[key], {
- mods, key, callback, description
- })
+ table.insert(self._private.keybindings[_keycode], key)
if self.export_keybindings then
- add_root_keybindings(self, {{mods, key, callback, description}})
+ add_root_keybindings(self, {key})
end
end
function keygrabber:set_root_keybindings(keys)
- add_root_keybindings(self, keys)
+ local real_keys = {}
+
+ -- Handle the pre-object-oriented input structures.
+ for _, key in ipairs(keys) do
+ if key._is_awful_key then
+ table.insert(real_keys, key)
+ else
+ gdebug.deprecate(":set_root_keybindings now takes `awful.key` "
+ .. " objects instead of tables",
+ {deprecated_in=5}
+ )
+
+ local mods, keycode, press, release, data = unpack(key)
+
+ table.insert(real_keys, akey {
+ modifiers = mods,
+ key = keycode,
+ description = (data or {}).description,
+ on_press = press,
+ on_release = release,
+ })
+ end
+ end
+
+ add_root_keybindings(self, real_keys)
end
-- Turn into a set.
@@ -689,26 +679,41 @@ function keygrab.run_with_keybindings(args)
-- 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 v._is_awful_key then
+ ret._private.keybindings[v.key] = ret._private.keybindings[v.key] or {}
+ table.insert(ret._private.keybindings[v.key], v)
+ elseif #v >= 3 and #v <= 4 then
+ gdebug.deprecate("keybindings now contains `awful.key` objects"
+ .. "instead of multiple tables",
+ {deprecated_in=5}
+ )
+
+ local modifiers, key, callback = unpack(v)
if type(callback) == "function" then
+
+ local k = akey {
+ modifiers = modifiers,
+ key = key,
+ on_press = callback,
+ }
+
ret._private.keybindings[key] = ret._private.keybindings[key] or {}
- table.insert(ret._private.keybindings[key], v)
+ table.insert(ret._private.keybindings[key], k)
else
gdebug.print_warning(
"The hook's 3rd parameter has to be a function. " ..
- gdebug.dump(v)
+ gdebug.dump(v or {})
)
end
else
gdebug.print_warning(
- "The hook has to have at least 3 parameters. ".. gdebug.dump(v)
+ "The keybindings should be awful.key objects".. gdebug.dump(v or {})
)
end
end
if args.export_keybindings then
- add_root_keybindings(ret, args.keybindings)
+ ret:set_root_keybindings(args.keybindings)
end
local mt = getmetatable(ret)
diff --git a/lib/awful/mouse/init.lua b/lib/awful/mouse/init.lua
index b94d63eb3..5e768fe76 100644
--- a/lib/awful/mouse/init.lua
+++ b/lib/awful/mouse/init.lua
@@ -370,6 +370,57 @@ end
-- @property is_middle_mouse_button_pressed
-- @param boolean
+--- Add an `awful.button` based mousebinding to the global set.
+--
+-- A **global** mousebinding is one which is always present, even when there is
+-- no focused client. If your intent is too add a mousebinding which acts on
+-- the focused client do **not** use this.
+--
+-- @staticfct awful.mouse.append_global_mousebinding
+-- @tparam awful.button button The button object.
+-- @see awful.button
+
+function mouse.append_global_mousebinding(button)
+ capi.root._append_button(button)
+end
+
+--- Add multiple `awful.button` based mousebindings to the global set.
+--
+-- A **global** mousebinding is one which is always present, even when there is
+-- no focused client. If your intent is too add a mousebinding which acts on
+-- the focused client do **not** use this
+--
+-- @tparam table buttons A table of `awful.button` objects. Optionally, it can have
+-- a `group` entry. If set, the `group` property will be set on all `awful.buttons`
+-- objects.
+-- @see awful.button
+
+function mouse.append_global_mousebindings(buttons)
+ local g = buttons.group
+ buttons.group = nil
+
+ -- Avoid the boilerplate. If the user is adding multiple buttons at once, then
+ -- they are probably related.
+ if g then
+ for _, k in ipairs(buttons) do
+ k.group = g
+ end
+ end
+
+ capi.root._append_buttons(buttons)
+ buttons.group = g
+end
+
+--- Remove a mousebinding from the global set.
+--
+-- @staticfct awful.mouse.remove_global_mousebinding
+-- @tparam awful.button button The button object.
+-- @see awful.button
+
+function mouse.remove_global_mousebinding(button)
+ capi.root._remove_button(button)
+end
+
for _, b in ipairs {"left", "right", "middle"} do
mouse.object["is_".. b .."_mouse_button_pressed"] = function()
return capi.mouse.coords().buttons[1]
diff --git a/lib/awful/root.lua b/lib/awful/root.lua
new file mode 100644
index 000000000..b423dff3c
--- /dev/null
+++ b/lib/awful/root.lua
@@ -0,0 +1,119 @@
+---------------------------------------------------------------------------
+-- @author Emmanuel Lepage-Vallee <elv1313@gmail.com>
+-- @copyright 2018-2019 Emmanuel Lepage-Vallee
+-- @module root
+---------------------------------------------------------------------------
+
+local capi = { root = root }
+local gtable = require("gears.table")
+local gtimer = require("gears.timer")
+local unpack = unpack or table.unpack -- luacheck: globals unpack (compatibility with Lua 5.1)
+
+for _, type_name in ipairs { "button", "key" } do
+ local prop_name = type_name.."s"
+
+ -- The largest amount of wall clock time when loading Awesome 3.4 rc.lua
+ -- was the awful.util.table.join (now gears.table.join). While the main
+ -- bottleneck in the newer releases moved into LGI, doing all these `join`
+ -- slow startup down quite a lot. On top of that, with the ability to add
+ -- and remove keys and buttons can cause a large overhead of its own. To
+ -- mitigate that, only set the actual content once per main loop iteration.
+ --
+ -- The C code also delay uploading these keys into the X server to prevent
+ -- too many keyboard map changes from freezing Awesome.
+ local has_delayed, added, removed = false, {}, {}
+
+ local function delay(value)
+ if value then
+ table.insert(added, value)
+ end
+
+ if has_delayed then return end
+
+ has_delayed = true
+
+ gtimer.delayed_call(function()
+ local new_values = capi.root["_"..prop_name]()
+
+ -- In theory, because they are inserted ordered, it is safe to assume
+ -- the once found, the capi.key/button will be next to each other.
+ for _, v in ipairs(removed) do
+ local idx = gtable.hasitem(new_values, v[1])
+
+ if idx then
+ for i=1, #v do
+ assert(
+ new_values[idx] == v[i],
+ "The root private "..type_name.." table is corrupted"
+ )
+
+ table.remove(new_values, idx)
+ end
+ end
+
+ idx = gtable.hasitem(added, v)
+
+ if idx then
+ table.remove(added, idx)
+ end
+ end
+
+ local joined = gtable.join(unpack(added))
+ new_values = gtable.merge(new_values, joined)
+
+ capi.root["_"..prop_name](new_values)
+
+ has_delayed, added, removed = false, {}, {}
+ end)
+ end
+
+ capi.root["_append_"..type_name] = function(value)
+ if not value then return end
+
+ local t1 = capi.root._private[prop_name]
+
+ -- Simple case
+ if (not t1) or not next(t1) then
+ capi.root[prop_name] = {value}
+ assert(capi.root._private[prop_name])
+ return
+ end
+
+ delay(value)
+ end
+
+ capi.root["_append_"..prop_name] = function(values)
+ -- It's pointless to use gears.table.merge, in the background it has the
+ -- same loop anyway. Also, this isn't done very often.
+ for _, value in ipairs(values) do
+ capi.root["_append_"..type_name](value)
+ end
+ end
+
+ capi.root["_remove_"..type_name] = function(value)
+ if not capi.root._private[prop_name] then return end
+
+ local k = gtable.hasitem(capi.root._private[prop_name], value)
+
+ if k then
+ table.remove(capi.root._private[prop_name], k)
+ end
+
+ -- Because of the legacy API, it is possible the capi.key/buttons will
+ -- be in the formatted table but not of the awful.key/button one.
+ assert(value[1])
+
+ table.insert(removed, value)
+ end
+
+ capi.root["has_"..type_name] = function(item)
+ if not item["_is_capi_"..type_name] then
+ item = item[1]
+ end
+
+ return gtable.hasitem(capi.root["_"..prop_name](), item) ~= nil
+ end
+
+ assert(root[prop_name])
+
+end
diff --git a/lib/awful/screen.lua b/lib/awful/screen.lua
index 139b4c154..2684a2319 100644
--- a/lib/awful/screen.lua
+++ b/lib/awful/screen.lua
@@ -290,7 +290,7 @@ function screen.object.get_outputs(s)
local ret = {}
local outputs = s._custom_outputs
- or (s.data.viewport and s.data.viewport.outputs or s._outputs)
+ or (s._private.viewport and s._private.viewport.outputs or s._outputs)
-- The reason this exists is because output with name as keys is very
-- convenient for quick name lookup by the users, but inconvenient in
@@ -671,9 +671,9 @@ function screen.object.split(s, ratios, mode, _geo)
table.insert(ret, ns)
if s then
- ns.data.viewport = s.data.viewport
+ ns._private.viewport = s._private.viewport
- if not ns.data.viewport then
+ if not ns._private.viewport then
ns.outputs = s.outputs
end
end
@@ -708,7 +708,7 @@ end
-- @staticfct awful.screen.set_auto_dpi_enabled
function screen.set_auto_dpi_enabled(enabled)
for s in capi.screen do
- s.data.dpi_cache = nil
+ s._private.dpi_cache = nil
end
data.autodpi = enabled
end
diff --git a/lib/awful/screen/dpi.lua b/lib/awful/screen/dpi.lua
index 6d3186d6e..896eb6e7f 100644
--- a/lib/awful/screen/dpi.lua
+++ b/lib/awful/screen/dpi.lua
@@ -150,7 +150,7 @@ end
-- Compute more useful viewport metadata frrom_sparse(add)om the list of output.
-- @treturn table An viewport with more information.
local function update_screen_viewport(s)
- local viewport = s.data.viewport
+ local viewport = s._private.viewport
if #ascreen._viewports == 0 then
ascreen._viewports = update_viewports(false)
@@ -171,7 +171,7 @@ local function update_screen_viewport(s)
end
if big_a then
- viewport, s.data.viewport = big_a, big_a
+ viewport, s._private.viewport = big_a, big_a
end
end
@@ -203,7 +203,7 @@ end
function module.remove_screen_handler(viewport)
for s in capi.screen do
- if s.data.viewport and s.data.viewport.id == viewport.id then
+ if s._private.viewport and s._private.viewport.id == viewport.id then
s:fake_remove()
return
end
@@ -212,12 +212,12 @@ end
function module.resize_screen_handler(old_viewport, new_viewport)
for s in capi.screen do
- if s.data.viewport and s.data.viewport.id == old_viewport.id then
+ if s._private.viewport and s._private.viewport.id == old_viewport.id then
local ngeo = new_viewport.geometry
s:fake_resize(
ngeo.x, ngeo.y, ngeo.width, ngeo.height
)
- s.data.viewport = new_viewport
+ s._private.viewport = new_viewport
return
end
end
@@ -274,32 +274,32 @@ function module._get_viewports()
end
local function get_dpi(s)
- if s.data.dpi or s.data.dpi_cache then
- return s.data.dpi or s.data.dpi_cache
+ if s._private.dpi or s._private.dpi_cache then
+ return s._private.dpi or s._private.dpi_cache
end
- if not s.data.viewport then
+ if not s._private.viewport then
update_screen_viewport(s)
end
-- Pick a DPI (from best to worst).
local dpi = ascreen._get_xft_dpi()
- or (s.data.viewport and s.data.viewport.preferred_dpi or nil)
+ or (s._private.viewport and s._private.viewport.preferred_dpi or nil)
or get_fallback_dpi()
-- Pick the screen DPI depending on the `autodpi` settings.
-- Historically, AwesomeWM size unit was the pixel. This assumption is
-- present in a lot, if not most, user config and is why this cannot be
-- enable by default for existing users.
- s.data.dpi_cache = data.autodpi and dpi
+ s._private.dpi_cache = data.autodpi and dpi
or ascreen._get_xft_dpi()
or get_fallback_dpi()
- return s.data.dpi_cache
+ return s._private.dpi_cache
end
local function set_dpi(s, dpi)
- s.data.dpi = dpi
+ s._private.dpi = dpi
end
screen.connect_signal("request::create", module.create_screen_handler)
@@ -362,7 +362,7 @@ capi.screen.connect_signal("property::_viewports", function(a)
-- Drop the cache.
for s in capi.screen do
- s.data.dpi_cache = nil
+ s._private.dpi_cache = nil
end
capi.screen.emit_signal("property::viewports", ascreen._get_viewports())
@@ -402,11 +402,11 @@ return function(screen, d)
"preferred_dpi" } do
screen.object["get_"..prop] = function(s)
- if not s.data.viewport then
+ if not s._private.viewport then
update_screen_viewport(s)
end
- local a = s.data.viewport or {}
+ local a = s._private.viewport or {}
return a[prop] or nil
end
diff --git a/lib/awful/tag.lua b/lib/awful/tag.lua
index 23f7cfe3d..557fadb9c 100644
--- a/lib/awful/tag.lua
+++ b/lib/awful/tag.lua
@@ -283,7 +283,7 @@ function tag.add(name, props)
local newtag = capi.tag{ name = name }
-- Start with a fresh property table to avoid collisions with unsupported data
- newtag.data.awful_tag_properties = {screen=properties.screen, index=properties.index}
+ newtag._private.awful_tag_properties = {screen=properties.screen, index=properties.index}
newtag.activated = true
@@ -402,7 +402,7 @@ function tag.object.delete(self, fallback_tag, force)
end
-- delete the tag
- self.data.awful_tag_properties.screen = nil
+ self._private.awful_tag_properties.screen = nil
self.activated = false
-- Update all indexes
@@ -1519,7 +1519,7 @@ end
-- @tparam tag _tag The tag.
-- @return The data table.
function tag.getdata(_tag)
- return _tag.data.awful_tag_properties
+ return _tag._private.awful_tag_properties
end
--- Get a tag property.
@@ -1532,8 +1532,9 @@ end
-- @return The property.
function tag.getproperty(_tag, prop)
if not _tag then return end -- FIXME: Turn this into an error?
- if _tag.data.awful_tag_properties then
- return _tag.data.awful_tag_properties[prop]
+
+ if _tag._private.awful_tag_properties then
+ return _tag._private.awful_tag_properties[prop]
end
end
@@ -1548,12 +1549,12 @@ end
-- @param prop The property name.
-- @param value The value.
function tag.setproperty(_tag, prop, value)
- if not _tag.data.awful_tag_properties then
- _tag.data.awful_tag_properties = {}
+ if not _tag._private.awful_tag_properties then
+ _tag._private.awful_tag_properties = {}
end
- if _tag.data.awful_tag_properties[prop] ~= value then
- _tag.data.awful_tag_properties[prop] = value
+ if _tag._private.awful_tag_properties[prop] ~= value then
+ _tag._private.awful_tag_properties[prop] = value
_tag:emit_signal("property::" .. prop)
end
end
@@ -1731,8 +1732,8 @@ capi.screen.connect_signal("removed", function(s)
for _, t in pairs(s.tags) do
t.activated = false
- if t.data.awful_tag_properties then
- t.data.awful_tag_properties.screen = nil
+ if t._private.awful_tag_properties then
+ t._private.awful_tag_properties.screen = nil
end
end
end)
diff --git a/lib/gears/object/properties.lua b/lib/gears/object/properties.lua
index 19ab239bb..f671d98a4 100644
--- a/lib/gears/object/properties.lua
+++ b/lib/gears/object/properties.lua
@@ -54,7 +54,8 @@ function object.capi_index_fallback(class, args)
end
-- Use the fallback property table
- return cobj.data[prop]
+ assert(prop ~= "_private")
+ return cobj._private[prop]
end
local setter = args.setter or function(cobj, prop, value)
@@ -74,7 +75,7 @@ function object.capi_index_fallback(class, args)
end
-- Use the fallback property table
- cobj.data[prop] = value
+ cobj._private[prop] = value
-- Emit the signal
if args.auto_emit then
@@ -87,6 +88,36 @@ function object.capi_index_fallback(class, args)
class.set_newindex_miss_handler(setter)
end
+-- Convert the capi objects back into awful ones.
+local function deprecated_to_current(content)
+ local first = content[1]
+
+ local current = first and first._private and
+ first._private._legacy_convert_to or nil
+
+ if not current then return nil end
+
+ local ret = {current}
+
+ for _, o in ipairs(content) do
+ -- If this is false, someone tried to mix things in a
+ -- way that is definitely not intentional.
+ assert(o._private and o._private._legacy_convert_to)
+
+ if o._private._legacy_convert_to ~= current then
+ -- If this is false, someone tried to mix things in a
+ -- way that is definitely not intentional.
+ assert(o._private._legacy_convert_to)
+
+ table.insert(
+ ret, o._private._legacy_convert_to
+ )
+ current = o._private._legacy_convert_to
+ end
+ end
+
+ return ret
+end
-- (private api)
-- Many legacy Awesome APIs such as `client:tags()`, `root.buttons()`,
@@ -129,7 +160,15 @@ local function copy_object(obj, to_set, name, capi_name, is_object, join_if, set
local result = is_formatted and
new_objs or gtable.join(unpack(new_objs))
- if capi_name and is_object then
+ -- First, when possible, get rid of the legacy format and go
+ -- back to the non-deprecated code path.
+ local current = self["set_"..name]
+ and deprecated_to_current(result) or nil
+
+ if current then
+ self["set_"..name](self, current)
+ return capi_name and self[capi_name](self) or self[name]
+ elseif capi_name and is_object then
return self[capi_name](self, result)
elseif capi_name then
return self[capi_name](result)
@@ -161,18 +200,13 @@ function object._legacy_accessors(obj, name, capi_name, is_object, join_if, set_
magic_obj["get_"..name] = function(self)
self = is_object and self or obj
- --FIXME v5 all objects should use _private instead of getproperty.
- if not self._private then
- self._private = {}
- end
-
self._private[name] = self._private[name] or copy_object(
obj, {}, name, capi_name, is_object, join_if, set_empty
)
+ local current = deprecated_to_current(self._private[name])
- assert(self._private[name])
- return self._private[name]
+ return current or self._private[name]
end
magic_obj["set_"..name] = function(self, objs)
@@ -181,7 +215,25 @@ function object._legacy_accessors(obj, name, capi_name, is_object, join_if, set_
if not is_object then
objs, self = self, obj
end
- assert(objs)
+
+ -- When using lua expressions like `false and true and my_objects`,
+ -- it is possible the code ends up as a boolean rather than `nil`
+ -- the resulting type is sometime counter intuitive and different
+ -- from similar languages such as JavaScript. Be forgiving and correct
+ -- course.
+ if objs == false then
+ objs = nil
+ end
+
+ -- Sometime, setting `nil` might be volontary since the user might
+ -- expect it will act as "clear". The correct thing would be to set
+ -- `{}`, but allow it nevertheless.
+
+ if objs == nil then
+ objs = {}
+ end
+
+ assert(self)
-- When called from a declarative property list, "buttons" will be set
-- using the result of gears.table.join, detect this
diff --git a/lib/wibox/init.lua b/lib/wibox/init.lua
index 1f4c7ff57..b27c88947 100644
--- a/lib/wibox/init.lua
+++ b/lib/wibox/init.lua
@@ -66,7 +66,8 @@ function wibox:find_widgets(x, y)
end
function wibox:_buttons(btns)
- return self.drawin:_buttons(btns)
+ -- The C code uses the argument count, `nil` counts.
+ return btns and self.drawin:_buttons(btns) or self.drawin:_buttons()
end
--- Create a widget that reflects the current state of this wibox.
@@ -277,7 +278,6 @@ local function new(args)
return ret
end
- w._private = {}
ret.drawin = w
ret._drawable = wibox.drawable(w.drawable, { wibox = ret },
"wibox drawable (" .. object.modulename(3) .. ")")
diff --git a/mouse.c b/mouse.c
index c446243a9..42d0b382d 100644
--- a/mouse.c
+++ b/mouse.c
@@ -57,7 +57,7 @@
*
* @author Julien Danjou <julien@danjou.info>
* @copyright 2008-2009 Julien Danjou
- * @coreclassmod mouse
+ * @inputmodule mouse
*/
#include "mouse.h"
diff --git a/spec/awful/keyboardlayout_spec.lua b/spec/awful/keyboardlayout_spec.lua
index e2a5c9660..4cf8739d8 100644
--- a/spec/awful/keyboardlayout_spec.lua
+++ b/spec/awful/keyboardlayout_spec.lua
@@ -3,6 +3,14 @@
-- @copyright 2015 Uli Schlachter and Kazunobu Kuriyama
---------------------------------------------------------------------------
+-- luacheck: globals button
+_G.button = setmetatable({
+ set_index_miss_handler = function() end,
+ set_newindex_miss_handler = function() end
+}, {
+ __call = function() return {} end
+})
+
local kb = require("awful.widget.keyboardlayout")
describe("awful.widget.keyboardlayout get_groups_from_group_names", function()
diff --git a/spec/awful/prompt_spec.lua b/spec/awful/prompt_spec.lua
index 5c7d35274..5d27570bd 100644
--- a/spec/awful/prompt_spec.lua
+++ b/spec/awful/prompt_spec.lua
@@ -1,3 +1,10 @@
+_G.key = setmetatable({
+ set_index_miss_handler = function() end,
+ set_newindex_miss_handler = function() end
+}, {
+ __call = function() return {} end
+})
+
local function assert_markup_format(markup)
if #markup == 0 then
return
diff --git a/tests/examples/sequences/template.lua b/tests/examples/sequences/template.lua
index 990f01e7b..7f4e788dd 100644
--- a/tests/examples/sequences/template.lua
+++ b/tests/examples/sequences/template.lua
@@ -752,7 +752,7 @@ function module.display_tags()
selected = t.selected,
icon = t.icon,
screen = t.screen,
- data = t.data,
+ _private = t._private,
clients = t.clients,
layout = t.layout,
master_width_factor = t.master_width_factor,
diff --git a/tests/examples/shims/awesome.lua b/tests/examples/shims/awesome.lua
index 24272af5b..ac4035725 100644
--- a/tests/examples/shims/awesome.lua
+++ b/tests/examples/shims/awesome.lua
@@ -6,7 +6,10 @@ local gears_obj = require("gears.object")
-- handlers.
local function _shim_fake_class()
local obj = gears_obj()
- obj.data = {}
+ obj._private = {}
+
+ -- Deprecated.
+ obj.data = obj._private
local meta = {
__index = function()end,
diff --git a/tests/examples/shims/button.lua b/tests/examples/shims/button.lua
index 5f41d1e7c..b58c051fe 100644
--- a/tests/examples/shims/button.lua
+++ b/tests/examples/shims/button.lua
@@ -1,7 +1,35 @@
-return function() return {
- data = {},
- _is_capi_button = true,
- connect_signal = function() end
-} end
+local gears_obj = require("gears.object")
+
+local button, meta = awesome._shim_fake_class()
+
+local function new_button(_, args)
+ local ret = gears_obj()
+ ret._private = args or {}
+
+ -- The miss handler wont work for this.
+ for k, v in pairs(ret._private) do
+ rawset(ret, k, v)
+ end
+
+ if not rawget(ret, "modifiers") then
+ rawset(ret, "modifiers", {})
+ end
+
+ --TODO v5: remove this.
+ ret.data = ret._private
+
+ rawset(ret, "_is_capi_button", true)
+
+ local md = setmetatable(ret, {
+ __index = function(...) return meta.__index(...) end,
+ __newindex = function(...) return meta.__newindex(...) end
+ })
+
+ assert((not args) or args.button == md.button)
+
+ return md
+end
+
+return setmetatable(button, { __call = new_button, })
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
diff --git a/tests/examples/shims/client.lua b/tests/examples/shims/client.lua
index 758c293fc..f2067131f 100644
--- a/tests/examples/shims/client.lua
+++ b/tests/examples/shims/client.lua
@@ -28,11 +28,11 @@ local properties = {}
for _, prop in ipairs {
"maximized", "maximized_horizontal", "maximized_vertical", "fullscreen" } do
properties["get_"..prop] = function(self)
- return self.data[prop] or false
+ return self._private[prop] or false
end
properties["set_"..prop] = function(self, value)
- self.data[prop] = value or false
+ self._private[prop] = value or false
if value then
self:emit_signal("request::geometry", prop, nil)
@@ -43,12 +43,12 @@ for _, prop in ipairs {
end
function properties.get_screen(self)
- return self.data.screen or screen[1]
+ return self._private.screen or screen[1]
end
function properties.set_screen(self, s)
s = screen[s]
- self.data.screen = s
+ self._private.screen = s
if self.x < s.geometry.x or self.x > s.geometry.x+s.geometry.width then
self:geometry { x = s.geometry.x }
@@ -66,14 +66,18 @@ function client.gen_fake(args)
local ret = gears_obj()
awesome._forward_class(ret, client)
- ret.data = {}
+ ret._private = {}
ret.type = "normal"
ret.valid = true
ret.size_hints = {}
ret.border_width = 1
ret.icon_sizes = {{16,16}}
ret.name = "Example Client"
- ret.data._struts = { top = 0, right = 0, left = 0, bottom = 0 }
+ ret._private._struts = { top = 0, right = 0, left = 0, bottom = 0 }
+
+ --TODO v5: remove this. This was a private API and thus doesn't need to be
+ -- officially deprecated.
+ ret.data = ret._private
-- This is a hack because there's a `:is_transient_for(c2)` method
-- and a `transient_for` property. It will cause a stack overflow
@@ -194,10 +198,10 @@ function client.gen_fake(args)
function ret:struts(new)
for k, v in pairs(new or {}) do
- ret.data._struts[k] = v
+ ret._private._struts[k] = v
end
- return ret.data._struts
+ return ret._private._struts
end
-- Set a dummy one for now since set_screen will corrupt it.
@@ -243,6 +247,23 @@ function client.gen_fake(args)
ret.above = false
ret.sticky = false
+ -- Declare the deprecated buttons and keys methods.
+ function ret:_keys(new)
+ if new then
+ ret._private.keys = new
+ end
+
+ return ret._private.keys or {}
+ end
+
+ function ret:_buttons(new)
+ if new then
+ ret._private.buttons = new
+ end
+
+ return ret._private.buttons or {}
+ end
+
-- Add to the client list
table.insert(clients, ret)
diff --git a/tests/examples/shims/drawin.lua b/tests/examples/shims/drawin.lua
index 0a9780ff6..61ca495c8 100644
--- a/tests/examples/shims/drawin.lua
+++ b/tests/examples/shims/drawin.lua
@@ -6,7 +6,10 @@ local cairo = require("lgi").cairo
local function new_drawin(_, args)
local ret = gears_obj()
- ret.data = {drawable = gears_obj()}
+ ret._private = {drawable = gears_obj()}
+
+ -- Deprecated.
+ ret.data = ret._private
ret.x=0
ret.y=0
@@ -31,11 +34,11 @@ local function new_drawin(_, args)
}
end
- ret.data.drawable.valid = true
- ret.data.drawable.surface = cairo.ImageSurface(cairo.Format.ARGB32, 0, 0)
- ret.data.drawable.geometry = ret.geometry
- ret.data.drawable.refresh = function() end
- ret.data._struts = { top = 0, right = 0, left = 0, bottom = 0 }
+ ret._private.drawable.valid = true
+ ret._private.drawable.surface = cairo.ImageSurface(cairo.Format.ARGB32, 0, 0)
+ ret._private.drawable.geometry = ret.geometry
+ ret._private.drawable.refresh = function() end
+ ret._private._struts = { top = 0, right = 0, left = 0, bottom = 0 }
for _, k in pairs{ "_buttons", "get_xproperty", "set_xproperty" } do
ret[k] = function() end
@@ -43,10 +46,10 @@ local function new_drawin(_, args)
function ret:struts(new)
for k, v in pairs(new or {}) do
- ret.data._struts[k] = v
+ ret._private._struts[k] = v
end
- return ret.data._struts
+ return ret._private._struts
end
local md = setmetatable(ret, {
diff --git a/tests/examples/shims/key.lua b/tests/examples/shims/key.lua
index ff2866aab..48e8abdb3 100644
--- a/tests/examples/shims/key.lua
+++ b/tests/examples/shims/key.lua
@@ -1,6 +1,31 @@
-local gobject = require("gears.object")
-local gtable = require("gears.table")
+local gears_obj = require("gears.object")
-return setmetatable({_is_capi_key = true}, {__call = function(_, args)
- return gtable.crush(gobject(), args)
-end})
+local key, meta = awesome._shim_fake_class()
+
+local function new_key(_, args)
+ local ret = gears_obj()
+ ret._private = args or {}
+
+ -- The miss handler wont work for this.
+ for k, v in pairs(ret._private) do
+ rawset(ret, k, v)
+ end
+
+ --TODO v5: remove this.
+ ret.data = ret._private
+
+ rawset(ret, "_is_capi_key", true)
+
+ local md = setmetatable(ret, {
+ __index = function(...) return meta.__index(...) end,
+ __newindex = function(...) return meta.__newindex(...) end
+ })
+
+ assert((not args) or args.key == md.key)
+
+ return md
+end
+
+return setmetatable(key, { __call = new_key, })
+
+-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
diff --git a/tests/examples/shims/root.lua b/tests/examples/shims/root.lua
index 47ee71b32..b22c878b8 100644
--- a/tests/examples/shims/root.lua
+++ b/tests/examples/shims/root.lua
@@ -2,8 +2,6 @@ local root = {_tags={}}
local gtable = require("gears.table")
-local hotkeys = nil
-
function root:tags()
return root._tags
end
@@ -86,15 +84,9 @@ local function match_modifiers(mods1, mods2)
end
local function execute_keybinding(key, event)
- -- It *could* be extracted from gears.object private API, but it's equally
- -- ugly as using the list used by the hotkey widget.
- if not hotkeys then
- hotkeys = require("awful.key").hotkeys
- end
-
- for _, v in ipairs(hotkeys) do
- if key == v.key and match_modifiers(v.mod, get_mods()) and v[event] then
- v[event]()
+ for _, v in ipairs(keys) do
+ if key == v.key and match_modifiers(v.modifiers, get_mods()) then
+ v:emit_signal(event)
return
end
end
diff --git a/tests/examples/shims/screen.lua b/tests/examples/shims/screen.lua
index e94ba3a5d..b969fa3b4 100644
--- a/tests/examples/shims/screen.lua
+++ b/tests/examples/shims/screen.lua
@@ -31,9 +31,12 @@ local function create_screen(args)
local s = gears_obj()
awesome._forward_class(s, screen)
- s.data = {}
+ s._private = {}
s.valid = true
+ -- Deprecated.
+ s.data = s._private
+
-- Copy the geo in case the args are mutated
local geo = {
x = args.x ,
diff --git a/tests/examples/shims/tag.lua b/tests/examples/shims/tag.lua
index 5a4f3cf2c..d9c092730 100644
--- a/tests/examples/shims/tag.lua
+++ b/tests/examples/shims/tag.lua
@@ -15,11 +15,14 @@ local function new_tag(_, args)
local ret = gears_obj()
awesome._forward_class(ret, tag)
- ret.data = {}
+ ret._private = {}
ret.name = tostring(args.name) or "test"
ret.activated = true
ret.selected = not has_selected_tag(args.screen)
+ -- Deprecated.
+ ret.data = ret._private
+
function ret:clients(_) --TODO handle new
local list = {}
for _, c in ipairs(client.get()) do
diff --git a/tests/examples/text/awful/keygrabber/alttab.lua b/tests/examples/text/awful/keygrabber/alttab.lua
index b72c572e8..c82d18301 100644
--- a/tests/examples/text/awful/keygrabber/alttab.lua
+++ b/tests/examples/text/awful/keygrabber/alttab.lua
@@ -2,7 +2,8 @@
local was_called = {} --DOC_HIDE
-local awful = {keygrabber = require("awful.keygrabber"), --DOC_HIDE
+local awful = { keygrabber = require("awful.keygrabber"), --DOC_HIDE
+ key = require("awful.key"), --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
@@ -12,8 +13,16 @@ local awful = {keygrabber = require("awful.keygrabber"), --DOC_HIDE
awful.keygrabber {
keybindings = {
- {{"Mod1" }, "Tab", awful.client.focus.history.select_previous},
- {{"Mod1", "Shift"}, "Tab", awful.client.focus.history.select_next },
+ awful.key {
+ modifiers = {"Mod1"},
+ key = "Tab",
+ on_press = awful.client.focus.history.select_previous
+ },
+ awful.key {
+ modifiers = {"Mod1", "Shift"},
+ key = "Tab",
+ on_press = awful.client.focus.history.select_next
+ },
},
-- Note that it is using the key name and not the modifier name.
stop_key = "Mod1",
diff --git a/tests/examples/text/awful/keygrabber/autostart.lua b/tests/examples/text/awful/keygrabber/autostart.lua
index f490b3e92..36120899e 100644
--- a/tests/examples/text/awful/keygrabber/autostart.lua
+++ b/tests/examples/text/awful/keygrabber/autostart.lua
@@ -7,7 +7,7 @@ local autostart_works = false --DOC_HIDE
awful.keygrabber {
autostart = true,
- stop_key = "Return",
+ stop_key = "Return",
stop_callback = function(_, _, _, sequence)
autostart_works = true --DOC_HIDE
assert(sequence == "abc") --DOC_HIDE
diff --git a/tests/examples/text/awful/keygrabber/root_keybindings.lua b/tests/examples/text/awful/keygrabber/root_keybindings.lua
index 86a090b83..5e9eb0312 100644
--- a/tests/examples/text/awful/keygrabber/root_keybindings.lua
+++ b/tests/examples/text/awful/keygrabber/root_keybindings.lua
@@ -1,5 +1,6 @@
--DOC_GEN_OUTPUT --DOC_HIDE
-local awful = { keygrabber = require("awful.keygrabber") } --DOC_HIDE
+local awful = { keygrabber = require("awful.keygrabber"), --DOC_HIDE
+ key = require("awful.key") } --DOC_HIDE
local keybinding_works = {} --DOC_HIDE
@@ -7,17 +8,25 @@ 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},
+ awful.key {
+ modifiers = {"Mod4"},
+ key = "i",
+ on_press = 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},
+ awful.key {
+ modifiers = {"Mod4", "Shift"},
+ key = "i",
+ on_press = 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!")
@@ -52,7 +61,6 @@ 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")
diff --git a/tests/examples/text/awful/keygrabber/vimode.lua b/tests/examples/text/awful/keygrabber/vimode.lua
index 74e9a8756..c87004c18 100644
--- a/tests/examples/text/awful/keygrabber/vimode.lua
+++ b/tests/examples/text/awful/keygrabber/vimode.lua
@@ -2,7 +2,8 @@
local gears = {table = require("gears.table")} --DOC_HIDE
-local awful = { keygrabber = require("awful.keygrabber") } --DOC_HIDE
+local awful = { keygrabber = require("awful.keygrabber"), --DOC_HIDE
+ key = require("awful.key") } --DOC_HIDE
local map, actions = {
verbs = {
@@ -44,6 +45,6 @@ local awful = { keygrabber = require("awful.keygrabber") } --DOC_HIDE
stop_callback = parse,
stop_key = gears.table.keys(map.verbs),
root_keybindings = {
- {{"Mod4"}, "v"}
+ awful.key({"Mod4"}, "v")
},
}
diff --git a/tests/test-awesomerc.lua b/tests/test-awesomerc.lua
index 52748e7cd..1d7ba4f3c 100644
--- a/tests/test-awesomerc.lua
+++ b/tests/test-awesomerc.lua
@@ -48,7 +48,7 @@ local steps = {
local l = old_c.screen.selected_tag.layout
assert(l)
- --awful.key.execute({modkey}, " ")
+ --awful.keyboard.emulate_key_combination({modkey}, " ")
awful.layout.inc(1)
assert(old_c.screen.selected_tag.layout ~= l)
@@ -56,7 +56,7 @@ local steps = {
-- Test ontop
assert(not old_c.ontop)
- awful.key.execute({modkey}, "t")
+ awful.keyboard.emulate_key_combination({modkey}, "t")
awesome.sync()
return true
@@ -74,7 +74,7 @@ local steps = {
-- Now, test the master_width_factor
assert(t.master_width_factor == 0.5)
- awful.key.execute({modkey}, "l")
+ awful.keyboard.emulate_key_combination({modkey}, "l")
awesome.sync()
return true
@@ -89,7 +89,7 @@ local steps = {
-- Now, test the master_count
assert(t.master_count == 1)
- awful.key.execute({modkey, "Shift"}, "h")
+ awful.keyboard.emulate_key_combination({modkey, "Shift"}, "h")
awesome.sync()
return true
@@ -104,8 +104,8 @@ local steps = {
-- Now, test the column_count
assert(t.column_count == 1)
- awful.key.execute({modkey, "Control"}, "h")
- awful.key.execute({modkey, "Shift" }, "l")
+ awful.keyboard.emulate_key_combination({modkey, "Control"}, "h")
+ awful.keyboard.emulate_key_combination({modkey, "Shift" }, "l")
awesome.sync()
return true
@@ -120,7 +120,7 @@ local steps = {
-- Now, test the switching tag
assert(t.index == 1)
- awful.key.execute({modkey, }, "Right")
+ awful.keyboard.emulate_key_combination({modkey, }, "Right")
awesome.sync()
return true
@@ -200,7 +200,7 @@ local steps = {
-- tags[1] and the client history should be kept
assert(client.focus == old_c)
- --awful.key.execute({modkey, "Shift" }, "#"..(9+i)) --FIXME
+ --awful.keyboard.emulate_key_combination({modkey, "Shift" }, "#"..(9+i)) --FIXME
client.focus:move_to_tag(tags[2])
assert(not client.focus)
@@ -235,7 +235,7 @@ local steps = {
if count == 1 then
assert(num_pairs(cached_wiboxes) == 0)
- awful.key.execute({modkey}, "s")
+ awful.keyboard.emulate_key_combination({modkey}, "s")
return nil
elseif count == 2 then
@@ -280,7 +280,7 @@ local steps = {
test_context.hotkeys01_clients_before < #client.get()
) then
-- open hotkeys popup with vim hotkeys:
- awful.key.execute({modkey}, "s")
+ awful.keyboard.emulate_key_combination({modkey}, "s")
test_context.hotkeys01_count_vim = count
end
diff --git a/tests/test-dpi.lua b/tests/test-dpi.lua
index 6b0c28d4b..970bcffa4 100644
--- a/tests/test-dpi.lua
+++ b/tests/test-dpi.lua
@@ -64,27 +64,27 @@ local function fake_replay(viewports)
local s = screen[k]
-- Check if the extra metadata are computed.
- assert(s.data.viewport.maximum_dpi)
- assert(s.data.viewport.minimum_dpi)
- assert(s.data.viewport.preferred_dpi)
+ assert(s._private.viewport.maximum_dpi)
+ assert(s._private.viewport.minimum_dpi)
+ assert(s._private.viewport.preferred_dpi)
assert(s.dpi)
- assert(s.maximum_dpi == s.data.viewport.maximum_dpi )
- assert(s.minimum_dpi == s.data.viewport.minimum_dpi )
- assert(s.preferred_dpi == s.data.viewport.preferred_dpi)
+ assert(s.maximum_dpi == s._private.viewport.maximum_dpi )
+ assert(s.minimum_dpi == s._private.viewport.minimum_dpi )
+ assert(s.preferred_dpi == s._private.viewport.preferred_dpi)
-- Make sure it enters either the main `if` or the fallback one.
- assert(s.data.viewport.minimum_dpi ~= math.huge)
- assert(s.data.viewport.preferred_dpi ~= math.huge)
+ assert(s._private.viewport.minimum_dpi ~= math.huge)
+ assert(s._private.viewport.preferred_dpi ~= math.huge)
-- Check the range.
- assert(s.data.viewport.maximum_dpi >= s.data.viewport.minimum_dpi)
- assert(s.data.viewport.preferred_dpi >= s.data.viewport.minimum_dpi)
- assert(s.data.viewport.preferred_dpi <= s.data.viewport.maximum_dpi)
+ assert(s._private.viewport.maximum_dpi >= s._private.viewport.minimum_dpi)
+ assert(s._private.viewport.preferred_dpi >= s._private.viewport.minimum_dpi)
+ assert(s._private.viewport.preferred_dpi <= s._private.viewport.maximum_dpi)
-- Check if the screen was created using the right viewport
-- (order *is* relevant).
- assert(#s.data.viewport.outputs == #a.outputs)
- assert(s.data.viewport and s.data.viewport.id == a.id)
+ assert(#s._private.viewport.outputs == #a.outputs)
+ assert(s._private.viewport and s._private.viewport.id == a.id)
end
-- The original shall not be modified, the CAPI for this is read-only.
@@ -314,7 +314,7 @@ end,
-- Test adding an output.
function()
- local viewport = screen[1].data.viewport
+ local viewport = screen[1]._private.viewport
add_output(fake_viewports[1], {
name = "foobar",
@@ -328,24 +328,24 @@ function()
})
-- It should have been kept.
- assert(viewport == screen[1].data.viewport)
+ assert(viewport == screen[1]._private.viewport)
-- If this isn't true, then auto-dpi didn't do its job.
assert(screen[1].dpi ~= saved_dpi1)
-- Now that there is multiple DPIs for the same viewport, the number
-- should double.
- assert(#screen[1].data.viewport.outputs == 3)
+ assert(#screen[1]._private.viewport.outputs == 3)
assert(screen[1].maximum_dpi == saved_dpi2*2)
assert(screen[1].minimum_dpi == saved_dpi3/2)
assert(screen[1].dpi == saved_dpi1/2)
remove_output(fake_viewports[1], "leet")
- assert(#screen[1].data.viewport.outputs == 2)
+ assert(#screen[1]._private.viewport.outputs == 2)
remove_output(fake_viewports[1], "foobar")
-- At this point, only 1 DPI is left.
- assert(#screen[1].data.viewport.outputs == 1)
+ assert(#screen[1]._private.viewport.outputs == 1)
assert(screen[1].maximum_dpi == saved_dpi1/2)
assert(screen[1].minimum_dpi == saved_dpi1/2)
assert(screen[1].dpi == saved_dpi1/2)
@@ -368,25 +368,25 @@ function()
}
assert(screen.count() == 2)
- assert(screen[1].data.viewport.id == 10)
- assert(screen[2].data.viewport.id == 11)
- assert(grect.are_equal(screen[1].data.viewport.geometry, screen[1].geometry))
- assert(grect.are_equal(screen[2].data.viewport.geometry, screen[2].geometry))
+ assert(screen[1]._private.viewport.id == 10)
+ assert(screen[2]._private.viewport.id == 11)
+ assert(grect.are_equal(screen[1]._private.viewport.geometry, screen[1].geometry))
+ assert(grect.are_equal(screen[2]._private.viewport.geometry, screen[2].geometry))
assert(#ascreen._viewports == 2)
remove_viewport(10)
assert(#ascreen._viewports == 1)
assert(screen.count() == 1)
- assert(screen[1].data.viewport.id == 11)
- assert(grect.are_equal(screen[1].data.viewport.geometry, screen[1].geometry))
+ assert(screen[1]._private.viewport.id == 11)
+ assert(grect.are_equal(screen[1]._private.viewport.geometry, screen[1].geometry))
return true
end,
-- Test resizing.
function()
- local s, sa = screen[1], screen[1].data.viewport
+ local s, sa = screen[1], screen[1]._private.viewport
assert(screen.count() == 1)
assert(#ascreen._viewports == 1)
@@ -403,8 +403,8 @@ function()
assert(screen.count() == 1)
assert(s == screen[1])
- assert(s.data.viewport ~= sa)
- assert(s.data.viewport.id == 12)
+ assert(s._private.viewport ~= sa)
+ assert(s._private.viewport.id == 12)
-- Now 2 smaller (resolution) screens side by side to make sure it doesn't
-- go haywire with overlapping
@@ -423,7 +423,7 @@ function()
assert(screen.count() == 2)
assert(s == screen[1])
- assert(s.data.viewport.id == 13)
+ assert(s._private.viewport.id == 13)
assert(s.geometry.x == 1337)
assert(s.geometry.width == 680)
@@ -453,8 +453,8 @@ function()
}
assert(screen.count() == 2)
- assert(screen[1].data.viewport.id == 15)
- assert(screen[2].data.viewport.id == 16)
+ assert(screen[1]._private.viewport.id == 15)
+ assert(screen[2]._private.viewport.id == 16)
-- Connect custom handler and see if the internals accidently depend on
-- implementation details.
diff --git a/tests/test-input-binding.lua b/tests/test-input-binding.lua
new file mode 100644
index 000000000..75cde9360
--- /dev/null
+++ b/tests/test-input-binding.lua
@@ -0,0 +1,254 @@
+require("awful._compat")
+
+local runner = require( "_runner" )
+local placement = require( "awful.placement" )
+local gtable = require( "gears.table" )
+local test_client = require( "_client" )
+
+local module = {
+ key = require( "awful.key" ),
+ button = require( "awful.button" )
+}
+
+local steps = {}
+
+local second = {
+ key = { "a", "b", "c", "d" },
+ button = { 1 , 2 , 3 , 4 },
+}
+
+local function send_events(event_type, step)
+ root.fake_input("key_press" , "Alt_L")
+ root.fake_input(event_type.."_press" , second[event_type][step])
+ root.fake_input(event_type.."_release", second[event_type][step])
+ root.fake_input("key_release", "Alt_L")
+end
+
+-- Test the shared API between the keys and buttons
+for _, type_name in ipairs { "key", "button" } do
+ local objects = {}
+
+ local pressed, released = {}, {}
+
+ table.insert(steps, function()
+ objects[1] = module[type_name](
+ {"Mod1"}, second[type_name][1], function() pressed[1] = true end, function() released[1] = true end
+ )
+
+ assert(objects[1].has_root_binding == false)
+ assert(not pressed [1])
+ assert(not released[1])
+
+ root["_append_"..type_name](objects[1])
+
+ -- It is async, the result need to be verified later
+ return true
+ end)
+
+ table.insert(steps, function()
+ assert(objects[1].has_root_binding)
+
+ -- Test "artificial" execution
+ objects[1]:trigger()
+
+ assert(pressed [1])
+ assert(released[1])
+
+ -- Test adding and removing in the same iteration
+ root["_remove_"..type_name](objects[1])
+
+ assert(second[type_name][2])
+
+ -- Use the multiple parameters syntax.
+ objects[2] = module[type_name](
+ {"Mod1"}, second[type_name][2], function() pressed[2] = true end, function() released[2] = true end
+ )
+
+ root["_append_"..type_name](objects[2])
+
+ return true
+ end)
+
+ table.insert(steps, function()
+ assert(objects[2].has_root_binding == true )
+ assert(objects[1].has_root_binding == false)
+
+ -- Make sure the cursor is not over the wibar
+ placement.centered(mouse)
+
+ assert(second[type_name][2])
+ send_events(type_name, 2)
+
+ return true
+ end)
+
+ -- Wait until the events are registered.
+ table.insert(steps, function()
+ if (not pressed[2]) or (not released[2]) then return end
+
+ -- Spawn a client.
+ test_client("myclient")
+
+ return true
+ end)
+
+ --FIXME it works when manually tested, but automated testing is too flacky
+ -- to enable...
+ if type_name == "key" then
+ table.insert(steps, function()
+ if #mouse.screen.clients ~= 1 then return end
+
+ placement.maximize(mouse.screen.clients[1])
+
+ return true
+ end)
+
+ local o1, o2 = nil
+
+ table.insert(steps, function()
+ local c = mouse.screen.clients[1]
+
+ -- This time, use the `args` syntax.
+ local args = {
+ modifiers = {"Mod1"},
+ on_press = function() pressed [3] = true end,
+ on_release = function() released[3] = true end
+ }
+
+ args[type_name] = second[type_name][3]
+
+ -- This time, use the `args` syntax.
+ o1 = module[type_name](args)
+
+ -- Test the old API.
+ c[type_name.."s"](c, gtable.join(o1))
+
+ return true
+ end)
+
+ -- This wont work until the client buttons/keys are updated, there is no
+ -- way to know ahead of time.
+ table.insert(steps, function(count)
+
+ if count < 5 then awesome.sync(); return end
+
+ send_events(type_name, 3)
+
+ return true
+ end)
+
+ table.insert(steps, function()
+ if (not pressed[3]) or (not released[3]) then return end
+
+ local c = mouse.screen.clients[1]
+
+ -- Unset the events to make sure keys can be removed.
+ pressed[3], released[3] = false, false
+
+ o2 = module[type_name](
+ {"Mod1"}, second[type_name][4], function() pressed[4] = true end, function() released[4] = true end
+ )
+
+ -- Test the new API
+ c[type_name.."s"] = {o2}
+
+ return true
+ end)
+
+ table.insert(steps, function(count)
+ if count < 5 then awesome.sync(); return end
+
+ send_events(type_name, 3)
+ send_events(type_name, 4)
+
+ return true
+ end)
+
+ table.insert(steps, function()
+ if (not pressed[4]) or (not released[4]) then return end
+
+ assert(not pressed [3])
+ assert(not released[3])
+
+ local c = mouse.screen.clients[1]
+
+ -- Make sure mixing the 2 syntaxes doesn't create a chimera state
+ pressed[4], released[4] = false, false
+
+ -- This *will* happen with older configs because `awful.rules` will
+ -- loop the properties, find the old capi list and fail to understand
+ -- that the content should use the legacy API.
+
+ local joined = gtable.join(o1)
+
+ assert(#joined == 4)
+
+ c[type_name.."s"] = joined
+
+ -- It should have been converted to the new format.
+ assert(#c[type_name.."s"] == 1)
+
+ return true
+ end)
+
+ table.insert(steps, function(count)
+ if count < 5 then awesome.sync(); return end
+
+ send_events(type_name, 3)
+ send_events(type_name, 4)
+
+ return true
+ end)
+
+ table.insert(steps, function()
+ if (not pressed[3]) or (not released[3]) then return end
+
+ assert(not pressed [4])
+ assert(not released[4])
+
+ local c = mouse.screen.clients[1]
+
+ assert(#c[type_name.."s"] == 1)
+
+ -- Test setting the object to `false` to simulate an inline Lua
+ -- expression gone wrong. This used to work and is rather
+ -- convenient, so lets not break it even if it is technically a bug.
+ c[type_name.."s"] = false
+
+ assert(#c[type_name.."s"] == 0)
+
+ c[type_name.."s"] = {o1}
+
+ assert(#c[type_name.."s"] == 1)
+
+ -- Test removing the objects using `nil`.
+ c[type_name.."s"] = nil
+
+ assert(#c[type_name.."s"] == 0)
+
+ c[type_name.."s"] = {o1}
+
+ assert(#c[type_name.."s"] == 1)
+
+ -- Test removing using `{}`
+ c[type_name.."s"] = {}
+
+ assert(#c[type_name.."s"] == 0)
+
+ c:kill()
+
+ return true
+ end)
+
+ -- Cleanup (otherwise there is a race with the root.buttons tests)
+ table.insert(steps, function()
+ if #mouse.screen.clients ~= 0 then return end
+
+ return true
+ end)
+ end
+end
+
+runner.run_steps(steps)
+
+-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
diff --git a/tests/test-keygrabber.lua b/tests/test-keygrabber.lua
index 4070d22a9..acfdbd568 100644
--- a/tests/test-keygrabber.lua
+++ b/tests/test-keygrabber.lua
@@ -7,11 +7,20 @@ local awful = require("awful")
local keygrabber_a_active = false
local keygrabber_b_active = false
+-- Disable the deprecation to test both the current and legacy APIs.
+local gdebug = require("gears.debug")
+gdebug.deprecate = function() end
+
local steps = {
function()
+
awful.keygrabber {
keybindings = {
- {{}, "a", function() keygrabber_a_active = true end},
+ awful.key {
+ modifiers = {},
+ key = "a",
+ on_press = function() keygrabber_a_active = true end
+ }
},
stop_key = "Escape",
stop_callback = function() keygrabber_a_active = false end,