Merge pull request #2894 from Elv13/xmas_2k18_2_merge_p3

API standardization part 2
This commit is contained in:
Emmanuel Lepage Vallée 2019-11-09 17:13:19 -05:00 committed by GitHub
commit 9bc6d4aa40
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 1627 additions and 433 deletions

View File

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

View File

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

View File

@ -69,4 +69,6 @@
-- <tr><td><a href='../core_components/client.html#startup_id'>startup\_id</a></td><td>The FreeDesktop StartId</td></tr>
-- <tr><td><a href='../core_components/client.html#valid'>valid</a></td><td>If the client that this object refers to is still managed by awesome</td></tr>
-- <tr><td><a href='../core_components/client.html#first_tag'>first\_tag</a></td><td>The first tag of the client</td></tr>
-- <tr><td><a href='../core_components/client.html#buttons'>buttons</a></td><td>Get or set mouse buttons bindings for a client</td></tr>
-- <tr><td><a href='../core_components/client.html#keys'>keys</a></td><td>Get or set keys bindings for a client</td></tr>
-- </table>

View File

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

View File

@ -49,22 +49,28 @@
<h2>Contents</h2>
<ul>
# for kind,items in module.kinds() do
# if not kind:match("^ldoc_skip") then
<li><a href="#$(no_spaces(kind))">$(kind)</a></li>
# end
# end
</ul>
# 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
<h2>$(kind)</h2>
# if not kind:match("^ldoc_skip") then
<h2>$(kind)dasdasd</h2>
<ul class="nowrap">
# end
# for item in items() do
<li><a href="#$(item.name)">$(display_name(item))</a></li>
# end
# if not kind:match("^ldoc_skip") then
</ul>
# 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
</table>
# end
<h2><a href="#$(no_spaces(kind))">$(kind)</a></h2>
<table class="function_list">
# 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 @@
<td class="summary">$(M(item.summary,item))</td>
</tr>
# end -- for items
</table>
# last_kind = kind
#end -- for kinds
</table>
<br/>
<br/>
@ -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
</dl>
# end
<h2 class="section-header $(has_description and 'has-description')"><a name="$(no_spaces(kind))"></a>$(kind)</h2>
# end
$(M(module.kinds:get_section_description(kind),nil))
# if kitem then
# if has_description then
@ -169,7 +187,9 @@
<pre class="example">$(ldoc.prettify(kitem.usage[1]))</pre>
# end
# end
# if not kind:match("^ldoc_skip") then
<dl class="function">
# end
# for item in items() do
<dt>
<a name = "$(item.name)"></a>
@ -289,8 +309,9 @@
</dd>
# end -- for items
</dl>
# last_kind = kind
# end -- for kinds
</dl>
# else -- if module; project-level contents

View File

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

View File

@ -2,19 +2,29 @@
--- Create easily new buttons objects ignoring certain modifiers.
--
-- @author Julien Danjou &lt;julien@danjou.info&gt;
-- @author Emmanuel Lepage Vallee &lt;elv1313@gmail.com&gt;
-- @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:
--
-- <table class='widget_list' border=1>
-- <tr style='font-weight: bold;'>
-- <th align='center'>Name</th>
-- <th align='center'>Description</th>
-- </tr>
-- <tr><td>Mod1</td><td>Usually called Alt on PCs and Option on Macs</td></tr>
-- <tr><td>Mod4</td><td>Also called Super, Windows and Command ⌘</td></tr>
-- <tr><td>Mod5</td><td>Also called AltGr or ISO Level 3</td></tr>
-- <tr><td>Shift</td><td>Both left and right shift keys</td></tr>
-- <tr><td>Control</td><td>Also called CTRL on some keyboards</td></tr>
-- </table>
--
-- 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

View File

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

View File

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

View File

@ -2,8 +2,9 @@
--- Create easily new key objects ignoring certain modifiers.
--
-- @author Julien Danjou &lt;julien@danjou.info&gt;
-- @copyright 2009 Julien Danjou
-- @classmod awful.key
-- @author Emmanuel Lepage Vallee &lt;elv1313@gmail.com&gt;
-- @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:
--
-- <table class='widget_list' border=1>
-- <tr style='font-weight: bold;'>
-- <th align='center'>Name</th>
-- <th align='center'>Description</th>
-- </tr>
-- <tr><td>Mod1</td><td>Usually called Alt on PCs and Option on Macs</td></tr>
-- <tr><td>Mod4</td><td>Also called Super, Windows and Command ⌘</td></tr>
-- <tr><td>Mod5</td><td>Also called AltGr or ISO Level 3</td></tr>
-- <tr><td>Shift</td><td>Both left and right shift keys</td></tr>
-- <tr><td>Control</td><td>Also called CTRL on some keyboards</td></tr>
-- </table>
--
-- 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

141
lib/awful/keyboard.lua Normal file
View File

@ -0,0 +1,141 @@
---------------------------------------------------------------------------
--- Utilities related to the keyboard and keybindings.
--
-- @author Emmanuel Lepage Vallee &lt;elv1313@gmail.com&gt;
-- @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

View File

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

View File

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

119
lib/awful/root.lua Normal file
View File

@ -0,0 +1,119 @@
---------------------------------------------------------------------------
-- @author Emmanuel Lepage-Vallee &lt;elv1313@gmail.com&gt;
-- @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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -57,7 +57,7 @@
*
* @author Julien Danjou &lt;julien@danjou.info&gt;
* @copyright 2008-2009 Julien Danjou
* @coreclassmod mouse
* @inputmodule mouse
*/
#include "mouse.h"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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")
},
}

View File

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

View File

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

View File

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

View File

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