diff --git a/.luacheckrc b/.luacheckrc index aa7e4f10b..6cbe09f91 100644 --- a/.luacheckrc +++ b/.luacheckrc @@ -26,7 +26,6 @@ read_globals = { "key", "keygrabber", "mousegrabber", - "root", "selection", "tag", "window", @@ -43,6 +42,7 @@ read_globals = { globals = { "screen", "mouse", + "root", "client" } diff --git a/build-utils/check_for_invalid_requires.lua b/build-utils/check_for_invalid_requires.lua index 924c902eb..dd33443b0 100755 --- a/build-utils/check_for_invalid_requires.lua +++ b/build-utils/check_for_invalid_requires.lua @@ -54,7 +54,7 @@ local allowed_deps = { -- Turn "foo.bar.baz" into "foo.bar". Returns nil if there is nothing more to -- remove. local function get_supermodule(module) - return string.match(module, "(.+)%.%a+") + return string.match(module, "(.+)%.[a-z_]+") end -- Check if "module" (or one of its parents) is allowed to depend on diff --git a/docs/common/widget.ldoc b/docs/common/widget.ldoc index bab763514..acd6bc7c8 100644 --- a/docs/common/widget.ldoc +++ b/docs/common/widget.ldoc @@ -40,9 +40,13 @@ -- @property visible -- @param boolean ---- Set/get a widget's buttons. --- @param _buttons The table of buttons that should bind to the widget. --- @method buttons +--- The widget buttons. +-- +-- The table contains a list of `awful.button` objects. +-- +-- @property buttons +-- @param table +-- @see awful.button --- Emit a signal and ensure all parent widgets in the hierarchies also -- forward the signal. This is useful to track signals when there is a dynamic diff --git a/docs/config.ld b/docs/config.ld index 398ef1a24..120ba85c4 100644 --- a/docs/config.ld +++ b/docs/config.ld @@ -118,10 +118,8 @@ file = { '../spawn.c', '../xkb.c', '../common/luaobject.c', - '../objects/button.c', '../objects/client.c', '../objects/drawable.c', - '../objects/key.c', '../objects/screen.c', '../objects/tag.c', '../objects/window.c', @@ -135,6 +133,7 @@ file = { -- documentation '../lib/awful/autofocus.lua', '../lib/awful/dbus.lua', + '../lib/awful/_compat.lua', '../lib/awful/init.lua', '../lib/awful/remote.lua', '../lib/awful/screen/dpi.lua', @@ -155,6 +154,11 @@ file = { '../lib/naughty/list/init.lua', '../lib/naughty/widget/_default.lua', + -- Ignore components that provide no value to users and have confusing + -- names + '../objects/button.c', + '../objects/key.c', + -- Deprecated classes for one years or more don't deserve entries -- in the index '../lib/awful/widget/graph.lua', diff --git a/lib/awful/_compat.lua b/lib/awful/_compat.lua new file mode 100644 index 000000000..b7af2bb2d --- /dev/null +++ b/lib/awful/_compat.lua @@ -0,0 +1,80 @@ +-- This file contains all global backward compatibility workarounds for the +-- Core API changes. +local gtimer = require("gears.timer") +local util = require("awful.util") +local spawn = require("awful.spawn") +local gdebug = require("gears.debug") +local gprop = require("gears.object.properties") + +local capi = {root = root} + +function timer(...) -- luacheck: ignore + gdebug.deprecate("gears.timer", {deprecated_in=4}) + return gtimer(...) +end + +util.spawn = function(...) + gdebug.deprecate("awful.spawn", {deprecated_in=4}) + return spawn.spawn(...) +end + +util.spawn_with_shell = function(...) + gdebug.deprecate("awful.spawn.with_shell", {deprecated_in=4}) + return spawn.with_shell(...) +end + +util.pread = function() + gdebug.deprecate("Use io.popen() directly or look at awful.spawn.easy_async() " + .. "for an asynchronous alternative", {deprecated_in=4}) + return "" +end + +-- Allow properties to be set on the root object. This helps to migrate some +-- capi function to an higher level Lua implementation. +do + local root_props, root_object = {}, {} + + capi.root.set_newindex_miss_handler(function(_,key,value) + if root_object["set_"..key] then + root_object["set_"..key](value) + elseif not root_object["get_"..key] then + root_props[key] = value + else + -- If there is a getter, but no setter, then the property is read-only + error("Cannot set '" .. tostring(key) .. " because it is read-only") + end + end) + + capi.root.set_index_miss_handler(function(_,key) + if root_object["get_"..key] then + return root_object["get_"..key]() + else + return root_props[key] + end + end) + + root._private = {} + root.object = root_object + assert(root.object == root_object) +end + +-- root.bottons() used to be a capi function. However this proved confusing +-- as rc.lua used `awful.button` and `root.buttons()` used capi.button. There +-- was a little documented hack to "flatten" awful.button into a pair of +-- capi.button. A consequence of this, beside being ugly, was that it was +-- de-facto read-only due the confusion related the difference between the +-- capi and "high level" format difference. + +gprop._legacy_accessors(capi.root, "buttons", "_buttons", false, function(new_btns) + return new_btns[1] and ( + type(new_btns[1]) == "button" or new_btns[1]._is_capi_button + ) or false +end, true) + +gprop._legacy_accessors(capi.root, "keys", "_keys", false, function(new_btns) + return new_btns[1] and ( + type(new_btns[1]) == "key" or new_btns[1]._is_capi_button + ) or false +end, true) + +assert(root.keys) diff --git a/lib/awful/button.lua b/lib/awful/button.lua index 39a035653..b59303f05 100644 --- a/lib/awful/button.lua +++ b/lib/awful/button.lua @@ -35,7 +35,6 @@ local ignore_modifiers = { "Lock", "Mod2" } -- will return 2 button objects: one with CapsLock on, and the other one with -- CapsLock off. -- --- @see button -- @treturn table A table with one or several button objects. function button.new(mod, _button, press, release) local ret = {} diff --git a/lib/awful/client.lua b/lib/awful/client.lua index c4ff1f005..ccf28325a 100644 --- a/lib/awful/client.lua +++ b/lib/awful/client.lua @@ -1344,6 +1344,18 @@ function client.object.is_transient_for(self, c2) return nil end +object.properties._legacy_accessors(client, "buttons", "_buttons", true, function(new_btns) + return new_btns[1] and ( + type(new_btns[1]) == "button" or new_btns[1]._is_capi_button + ) or false +end, true) + +object.properties._legacy_accessors(client, "keys", "_keys", true, function(new_btns) + return new_btns[1] and ( + type(new_btns[1]) == "key" or new_btns[1]._is_capi_key + ) or false +end, true) + --- Set the client shape. -- @property shape -- @tparam gears.shape A gears.shape compatible function. diff --git a/lib/awful/init.lua b/lib/awful/init.lua index da63275c4..cf5764273 100644 --- a/lib/awful/init.lua +++ b/lib/awful/init.lua @@ -6,34 +6,7 @@ -- @module awful --------------------------------------------------------------------------- --- TODO: This is a hack for backwards-compatibility with 3.5, remove! -local util = require("awful.util") -local gtimer = require("gears.timer") -local gdebug = require("gears.debug") -function timer(...) -- luacheck: ignore - gdebug.deprecate("gears.timer", {deprecated_in=4}) - return gtimer(...) -end - ---TODO: This is a hack for backwards-compatibility with 3.5, remove! --- Set awful.util.spawn* and awful.util.pread. -local spawn = require("awful.spawn") - -util.spawn = function(...) - gdebug.deprecate("awful.spawn", {deprecated_in=4}) - return spawn.spawn(...) -end - -util.spawn_with_shell = function(...) - gdebug.deprecate("awful.spawn.with_shell", {deprecated_in=4}) - return spawn.with_shell(...) -end - -util.pread = function() - gdebug.deprecate("Use awful.spawn.easy_async() " - .. "for an asynchronous alternative", {deprecated_in=4}) - return "" -end +require("awful._compat") return { @@ -60,7 +33,7 @@ return titlebar = require("awful.titlebar"); rules = require("awful.rules"); popup = require("awful.popup"); - spawn = spawn; + spawn = require("awful.spawn"); } -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/lib/awful/key.lua b/lib/awful/key.lua index 6e0a56919..5826a7c97 100644 --- a/lib/awful/key.lua +++ b/lib/awful/key.lua @@ -103,7 +103,7 @@ end -- 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. --- @see key.key +-- -- @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. @@ -113,6 +113,7 @@ end -- 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) if type(release)=='table' then data=release diff --git a/lib/awful/wibar.lua b/lib/awful/wibar.lua index b29d6d846..4b4e9bb62 100644 --- a/lib/awful/wibar.lua +++ b/lib/awful/wibar.lua @@ -421,6 +421,8 @@ function awfulwibar.new(args) w:connect_signal("property::visible", function() reattach(w) end) + assert(w.buttons) + return w end diff --git a/lib/awful/widget/common.lua b/lib/awful/widget/common.lua index 35dbfb7ce..3b8dc6a6e 100644 --- a/lib/awful/widget/common.lua +++ b/lib/awful/widget/common.lua @@ -21,17 +21,23 @@ local common = {} -- @param object -- @treturn table function common.create_buttons(buttons, object) + local is_formatted = buttons and buttons[1] and ( + type(buttons[1]) == "button" or buttons[1]._is_capi_button) or false + if buttons then local btns = {} - for _, b in ipairs(buttons) do - -- Create a proxy button object: it will receive the real - -- press and release events, and will propagate them to the - -- button object the user provided, but with the object as - -- argument. - local btn = capi.button { modifiers = b.modifiers, button = b.button } - btn:connect_signal("press", function () b:emit_signal("press", object) end) - btn:connect_signal("release", function () b:emit_signal("release", object) end) - btns[#btns + 1] = btn + for _, src in ipairs(buttons) do + --TODO v6 Remove this legacy overhead + for _, b in ipairs(is_formatted and {src} or src) do + -- Create a proxy button object: it will receive the real + -- press and release events, and will propagate them to the + -- button object the user provided, but with the object as + -- argument. + local btn = capi.button { modifiers = b.modifiers, button = b.button } + btn:connect_signal("press", function () b:emit_signal("press", object) end) + btn:connect_signal("release", function () b:emit_signal("release", object) end) + btns[#btns + 1] = btn + end end return btns diff --git a/lib/awful/widget/layoutbox.lua b/lib/awful/widget/layoutbox.lua index 5583e6836..07b12d629 100644 --- a/lib/awful/widget/layoutbox.lua +++ b/lib/awful/widget/layoutbox.lua @@ -1,5 +1,6 @@ --------------------------------------------------------------------------- ---- Layoutbox widget. +--- Display the current client layout (`awful.layout`) icon or name +-- -- -- @author Julien Danjou <julien@danjou.info> -- @copyright 2009 Julien Danjou @@ -13,6 +14,8 @@ local tooltip = require("awful.tooltip") local beautiful = require("beautiful") local wibox = require("wibox") local surface = require("gears.surface") +-- local gdebug = require("gears.debug") +local gtable = require("gears.table") local function get_screen(s) return s and capi.screen[s] @@ -42,10 +45,25 @@ end --- Create a layoutbox widget. It draws a picture with the current layout -- symbol of the current tag. --- @param screen The screen number that the layout will be represented for. --- @return An imagebox widget configured as a layoutbox. --- @constructorfct awful.widget.layoutbox -function layoutbox.new(screen) +-- @tparam table args The arguments. +-- @tparam screen args.screen The screen number that the layout will be represented for. +-- @tparam table args.buttons The `awful.button`s for this layoutbox. +-- @return The layoutbox. +function layoutbox.new(args) + args = args or {} + local screen = nil + + if type(args) == "number" or type(args) == "screen" or args.fake_remove then + screen, args = args, {} +--TODO uncomment +-- gdebug.deprecate( +-- "Use awful.widget.layoutbox{screen=s} instead of awful.widget.layoutbox(screen)", +-- {deprecated_in=5} +-- ) + end + + assert(type(args) == "table") + screen = get_screen(screen or 1) -- Do we already have the update callbacks registered? @@ -80,6 +98,9 @@ function layoutbox.new(screen) w._layoutbox_tooltip = tooltip {objects = {w}, delay_show = 1} + -- Apply the buttons, visible, forced_width and so on + gtable.crush(w, args) + update(w, screen) boxes[screen] = w end @@ -91,6 +112,10 @@ function layoutbox.mt:__call(...) return layoutbox.new(...) end +--@DOC_widget_COMMON@ + +--@DOC_object_COMMON@ + return setmetatable(layoutbox, layoutbox.mt) -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/lib/gears/object/properties.lua b/lib/gears/object/properties.lua index 3b740fd44..8f29ee018 100644 --- a/lib/gears/object/properties.lua +++ b/lib/gears/object/properties.lua @@ -7,7 +7,10 @@ -- @submodule gears.object --------------------------------------------------------------------------- +local gtable = require("gears.table") +-- local gdebug = require("gears.debug") local object = {} +local unpack = unpack or table.unpack -- luacheck: globals unpack (compatibility with Lua 5.1) --- Add the missing properties handler to a CAPI object such as client/tag/screen. @@ -84,6 +87,126 @@ function object.capi_index_fallback(class, args) class.set_newindex_miss_handler(setter) end + +-- (private api) +-- Many legacy Awesome APIs such as `client:tags()`, `root.buttons()`, +-- `client:keys()`, `drawin:geometry()`, etc used functions for both the getter +-- and setter. This contrast with just about everything else that came after +-- it and is an artifact of an earlier time before we had "good" Lua object +-- support. +-- +-- Because both consistency and backward compatibility are important, this +-- table wrapper allows to support both the legacy method based accessors +-- and key/value based accessors. +-- +-- TO BE USED FOR DEPRECATION ONLY. + +local function copy_object(obj, to_set, name, capi_name, is_object, join_if, set_empty) + local ret = gtable.clone(to_set, false) + + -- .buttons used to be a function taking the result of `gears.table.join`. + -- For compatibility, support this, but from now on, it's a property. + return setmetatable(ret, { + __call = function(_, self, new_objs) +--TODO uncomment +-- gdebug.deprecate("`"..name.."` is no longer a function, it is a property. ".. +-- "Remove the `gears.table.join` and use a brace enclosed table", +-- {deprecated_in=5} +-- ) + + new_objs, self = is_object and new_objs or self, is_object and self or obj + + -- Setter + if new_objs and #new_objs > 0 then + if (not set_empty) and not next(new_objs) then return end + + local is_formatted = join_if(new_objs) + + -- Because modules may rely on :buttons taking a list of + -- `capi.buttons`/`capi.key` and now the user passes a list of + -- `awful.button`/`awful.key` convert this. + + local result = is_formatted and + new_objs or gtable.join(unpack(new_objs)) + + if capi_name and is_object then + return self[capi_name](self, result) + elseif capi_name then + return self[capi_name](result) + else + self._private[name.."_formatted"] = result + return self._private[name.."_formatted"] + end + end + + -- Getter + if capi_name and is_object then + return self[capi_name](self) + elseif capi_name then + return self[capi_name]() + else + return self._private[name.."_formatted"] or {} + end + end + }) +end + +function object._legacy_accessors(obj, name, capi_name, is_object, join_if, set_empty) + + -- Some objects have a special "object" property to add more properties, but + -- not all. + + local magic_obj = obj.object and obj.object or obj + + magic_obj["get_"..name] = function(self) + self = is_object and self or obj + + self._private[name] = self._private[name] or copy_object( + obj, {}, name, capi_name, is_object, join_if, set_empty + ) + + assert(self._private[name]) + return self._private[name] + end + + magic_obj["set_"..name] = function(self, objs) + if (not set_empty) and not next(objs) then return end + + if not is_object then + objs, self = self, obj + end + assert(objs) + + -- When called from a declarative property list, "buttons" will be set + -- using the result of gears.table.join, detect this + local is_formatted = join_if(objs) + +-- if is_formatted then +--TODO uncomment +-- gdebug.deprecate("Remove the `gears.table.join` and replace it with braces", +-- {deprecated_in=5} +-- ) +-- end + + + --TODO v6 Use the original directly and drop this legacy copy + local result = is_formatted and objs + or gtable.join(unpack(objs)) + + if is_object and capi_name then + self[capi_name](self, result) + elseif capi_name then + obj[capi_name](result) + else + self._private[name.."_formatted"] = result + end + + self._private[name] = copy_object( + obj, objs, name, capi_name, is_object, join_if, set_empty + ) + end +end + return setmetatable( object, {__call = function(_,...) object.capi_index_fallback(...) end}) -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/lib/wibox/init.lua b/lib/wibox/init.lua index f55a899e3..3f68d5cb4 100644 --- a/lib/wibox/init.lua +++ b/lib/wibox/init.lua @@ -19,6 +19,7 @@ local beautiful = require("beautiful") local base = require("wibox.widget.base") local cairo = require("lgi").cairo + --- This provides widget box windows. Every wibox can also be used as if it were -- a drawin. All drawin functions and properties are also available on wiboxes! -- wibox @@ -63,6 +64,10 @@ function wibox:find_widgets(x, y) return self._drawable:find_widgets(x, y) end +function wibox:_buttons(btns) + return self.drawin:_buttons(btns) +end + --- Create a widget that reflects the current state of this wibox. -- @treturn widget A new widget. -- @method to_widget @@ -207,12 +212,18 @@ function wibox:get_children_by_id(name) return {} end -for _, k in pairs{ "buttons", "struts", "geometry", "get_xproperty", "set_xproperty" } do +for _, k in pairs{ "struts", "geometry", "get_xproperty", "set_xproperty" } do wibox[k] = function(self, ...) return self.drawin[k](self.drawin, ...) end end +object.properties._legacy_accessors(wibox.object, "buttons", "_buttons", true, function(new_btns) + return new_btns[1] and ( + type(new_btns[1]) == "button" or new_btns[1]._is_capi_button + ) or false +end, true) + local function setup_signals(_wibox) local obj local function clone_signal(name) @@ -264,6 +275,7 @@ 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) .. ")") @@ -365,6 +377,8 @@ object.properties(capi.drawin, { auto_emit = true, }) +capi.drawin.object = wibox.object + return setmetatable(wibox, wibox.mt) -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/lib/wibox/widget/base.lua b/lib/wibox/widget/base.lua index d3958b2d8..bf4b87f8c 100644 --- a/lib/wibox/widget/base.lua +++ b/lib/wibox/widget/base.lua @@ -7,6 +7,7 @@ local object = require("gears.object") local cache = require("gears.cache") local matrix = require("gears.matrix") +local gdebug = require("gears.debug") local protected_call = require("gears.protected_call") local gtable = require("gears.table") local setmetatable = setmetatable @@ -21,15 +22,11 @@ local base = {} -- Functions available on all widgets. base.widget = {} ---- Set/get a widget's buttons. --- @tab _buttons The table of buttons that is bound to the widget. --- @method buttons -function base.widget:buttons(_buttons) - if _buttons then - self._private.widget_buttons = _buttons - end - return self._private.widget_buttons -end +object.properties._legacy_accessors(base.widget, "buttons", nil, true, function(new_btns) + return new_btns[1] and ( + type(new_btns[1]) == "button" or new_btns[1]._is_capi_button + ) or false +end, true) --- Set a widget's visibility. -- @tparam boolean b Whether the widget is visible. @@ -367,7 +364,7 @@ function base.handle_button(event, widget, x, y, button, modifiers, geometry) -- Find all matching button objects. local matches = {} - for _, v in pairs(widget._private.widget_buttons) do + for _, v in pairs(widget._private.buttons_formatted or {}) do local match = true -- Is it the right button? if v.button ~= 0 and v.button ~= button then match = false end @@ -609,17 +606,22 @@ end -- -- @param wdg The value. -- @param[opt=nil] ... Arguments passed to the contructor (if any). --- @treturn The new widget. -- @constructorfct wibox.widget.base.make_widget_from_value +-- @treturn widget|nil The new widget or `nil` in case of failure. function base.make_widget_from_value(wdg, ...) + if not wdg then return nil end + local is_function, t = is_callable(wdg) if is_function then wdg = wdg(...) - elseif t == "table" and not wdg.is_widget then - wdg = base.make_widget_declarative(wdg) + elseif t == "table" then + wdg = wdg.is_widget and wdg or base.make_widget_declarative(wdg) else - assert(wdg.is_widget, "The argument is not a function, table, or widget.") + gdebug.print_warning( + "The argument is not a function, table, or widget." + ) + return nil end return wdg @@ -656,9 +658,6 @@ function base.make_widget(proxy, widget_name, args) -- Create a table used to store the widgets internal data. rawset(ret, "_private", {}) - -- No buttons yet. - ret._private.widget_buttons = {} - -- Widget is visible. ret._private.visible = true @@ -713,6 +712,20 @@ function base.make_widget(proxy, widget_name, args) mt.__tostring = function() return string.format("%s (%s)", ret.widget_name, orig_string) end + + -- Even when properties are disabled, buttons is required for backward + -- compatibility. + --TODO v6 Remove this + if not args.enable_properties then + mt.__index = function(_, key) + if key == "buttons" then + return base.widget.get_buttons(ret) + end + + return rawget(ret, key) + end + end + return setmetatable(ret, mt) end diff --git a/luaa.c b/luaa.c index 78e30844c..3e9455f74 100644 --- a/luaa.c +++ b/luaa.c @@ -80,9 +80,10 @@ extern const struct luaL_Reg awesome_dbus_lib[]; #endif extern const struct luaL_Reg awesome_keygrabber_lib[]; extern const struct luaL_Reg awesome_mousegrabber_lib[]; -extern const struct luaL_Reg awesome_root_lib[]; extern const struct luaL_Reg awesome_mouse_methods[]; extern const struct luaL_Reg awesome_mouse_meta[]; +extern const struct luaL_Reg awesome_root_methods[]; +extern const struct luaL_Reg awesome_root_meta[]; /** A call into the Lua code aborted with an error. * @@ -995,8 +996,7 @@ luaA_init(xdgHandle* xdg, string_array_t *searchpath) setup_awesome_signals(L); /* Export root lib */ - luaA_registerlib(L, "root", awesome_root_lib); - lua_pop(L, 1); /* luaA_registerlib() leaves the table on stack */ + luaA_openlib(L, "root", awesome_root_methods, awesome_root_meta); #ifdef WITH_DBUS /* Export D-Bus lib */ diff --git a/objects/client.c b/objects/client.c index 5f5d9de9e..e9b47a15c 100644 --- a/objects/client.c +++ b/objects/client.c @@ -949,9 +949,9 @@ /** Get or set mouse buttons bindings for a client. * - * @param buttons_table An array of mouse button bindings objects, or nothing. - * @return A table with all buttons. - * @method buttons + * @property buttons + * @param table + * @see awful.button */ /** Get the number of instances. @@ -3677,9 +3677,9 @@ luaA_client_set_shape_input(lua_State *L, client_t *c) /** Get or set keys bindings for a client. * - * @param keys_table An array of key bindings objects, or nothing. - * @return A table with all keys. - * @method keys + * @property keys + * @param table + * @see awful.key */ static int luaA_client_keys(lua_State *L) @@ -3813,7 +3813,7 @@ client_class_setup(lua_State *L) { LUA_OBJECT_META(client) LUA_CLASS_META - { "keys", luaA_client_keys }, + { "_keys", luaA_client_keys }, { "isvisible", luaA_client_isvisible }, { "geometry", luaA_client_geometry }, { "apply_size_hints", luaA_client_apply_size_hints }, diff --git a/objects/window.c b/objects/window.c index 3c0432730..623cdbb6e 100644 --- a/objects/window.c +++ b/objects/window.c @@ -515,7 +515,7 @@ window_class_setup(lua_State *L) static const struct luaL_Reg window_meta[] = { { "struts", luaA_window_struts }, - { "buttons", luaA_window_buttons }, + { "_buttons", luaA_window_buttons }, { "set_xproperty", luaA_window_set_xproperty }, { "get_xproperty", luaA_window_get_xproperty }, { NULL, NULL } diff --git a/root.c b/root.c index 4da0f6605..9c1b14655 100644 --- a/root.c +++ b/root.c @@ -31,6 +31,7 @@ #include "common/xcursor.h" #include "common/xutil.h" #include "objects/button.h" +#include "common/luaclass.h" #include "xwindow.h" #include "math.h" @@ -39,6 +40,10 @@ #include #include +static int miss_index_handler = LUA_REFNIL; +static int miss_newindex_handler = LUA_REFNIL; +static int miss_call_handler = LUA_REFNIL; + static void root_set_wallpaper_pixmap(xcb_connection_t *c, xcb_pixmap_t p) { @@ -330,11 +335,12 @@ luaA_root_fake_input(lua_State *L) } /** Get or set global key bindings. - * These bindings will be available when you press keys on the root window. + * These bindings will be available when you press keys on the root window + * (the wallpaper). * - * @tparam table|nil keys_array An array of key binding objects, or nothing. - * @return The array of key bindings objects of this client. - * @staticfct keys + * @property keys + * @param table + * @see awful.key */ static int luaA_root_keys(lua_State *L) @@ -369,13 +375,22 @@ luaA_root_keys(lua_State *L) return 1; } -/** Get or set global mouse bindings. - * This binding will be available when you click on the root window. +/** + * Store the list of mouse buttons to be applied on the wallpaper (also + * known as root window). * - * @param button_table An array of mouse button bindings objects, or nothing. - * @return The array of mouse button bindings objects. - * @staticfct buttons + * @property buttons + * @tparam[opt={}] table buttons The list of buttons. + * @see awful.button + * + * @usage + * root.buttons = { + * awful.button({ }, 3, function () mymainmenu:toggle() end), + * awful.button({ }, 4, awful.tag.viewnext), + * awful.button({ }, 5, awful.tag.viewprev), + * } */ + static int luaA_root_buttons(lua_State *L) { @@ -525,10 +540,65 @@ luaA_root_tags(lua_State *L) return 1; } -const struct luaL_Reg awesome_root_lib[] = +/** +* Add a custom call handler. +*/ +static int +luaA_root_set_call_handler(lua_State *L) { - { "buttons", luaA_root_buttons }, - { "keys", luaA_root_keys }, + return luaA_registerfct(L, 1, &miss_call_handler); +} + +/** +* Add a custom property handler (getter). +*/ +static int +luaA_root_set_index_miss_handler(lua_State *L) +{ + return luaA_registerfct(L, 1, &miss_index_handler); +} + +/** +* Add a custom property handler (setter). +*/ +static int +luaA_root_set_newindex_miss_handler(lua_State *L) +{ + return luaA_registerfct(L, 1, &miss_newindex_handler); +} + +/** Root library. + * \param L The Lua VM state. + * \return The number of elements pushed on stack. + * \luastack + */ +static int +luaA_root_index(lua_State *L) +{ + if (miss_index_handler != LUA_REFNIL) + return luaA_call_handler(L, miss_index_handler); + + return luaA_default_index(L); +} + +/** Newindex for root. + * \param L The Lua VM state. + * \return The number of elements pushed on stack. + */ +static int +luaA_root_newindex(lua_State *L) +{ + /* Call the lua root property handler */ + if (miss_newindex_handler != LUA_REFNIL) + return luaA_call_handler(L, miss_newindex_handler); + + return luaA_default_newindex(L); +} + +const struct luaL_Reg awesome_root_methods[] = +{ + { "_buttons", luaA_root_buttons }, + { "_keys", luaA_root_keys }, { "cursor", luaA_root_cursor }, { "fake_input", luaA_root_fake_input }, { "drawins", luaA_root_drawins }, @@ -536,8 +606,17 @@ const struct luaL_Reg awesome_root_lib[] = { "size", luaA_root_size }, { "size_mm", luaA_root_size_mm }, { "tags", luaA_root_tags }, - { "__index", luaA_default_index }, - { "__newindex", luaA_default_newindex }, + { "__index", luaA_root_index }, + { "__newindex", luaA_root_newindex }, + { "set_index_miss_handler", luaA_root_set_index_miss_handler}, + { "set_call_handler", luaA_root_set_call_handler}, + { "set_newindex_miss_handler", luaA_root_set_newindex_miss_handler}, + + { NULL, NULL } +}; + +const struct luaL_Reg awesome_root_meta[] = +{ { NULL, NULL } }; diff --git a/tests/examples/shims/_common_template.lua b/tests/examples/shims/_common_template.lua index 283978d23..50c73d0c8 100644 --- a/tests/examples/shims/_common_template.lua +++ b/tests/examples/shims/_common_template.lua @@ -33,6 +33,8 @@ return function(_, _) -- backend. local bg = require("wibox.container.background") bg._use_fallback_algorithm() + + require("awful._compat") end -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/tests/examples/shims/button.lua b/tests/examples/shims/button.lua index fb5e995af..5f41d1e7c 100644 --- a/tests/examples/shims/button.lua +++ b/tests/examples/shims/button.lua @@ -1,5 +1,6 @@ return function() return { data = {}, + _is_capi_button = true, connect_signal = function() end } end diff --git a/tests/examples/shims/drawin.lua b/tests/examples/shims/drawin.lua index 2147b6232..0a9780ff6 100644 --- a/tests/examples/shims/drawin.lua +++ b/tests/examples/shims/drawin.lua @@ -37,7 +37,7 @@ local function new_drawin(_, args) ret.data.drawable.refresh = function() end ret.data._struts = { top = 0, right = 0, left = 0, bottom = 0 } - for _, k in pairs{ "buttons", "get_xproperty", "set_xproperty" } do + for _, k in pairs{ "_buttons", "get_xproperty", "set_xproperty" } do ret[k] = function() end end diff --git a/tests/examples/shims/key.lua b/tests/examples/shims/key.lua index 2936189dc..ff2866aab 100644 --- a/tests/examples/shims/key.lua +++ b/tests/examples/shims/key.lua @@ -1,6 +1,6 @@ local gobject = require("gears.object") local gtable = require("gears.table") -return setmetatable({}, {__call = function(_, args) +return setmetatable({_is_capi_key = true}, {__call = function(_, args) return gtable.crush(gobject(), args) end}) diff --git a/tests/examples/shims/root.lua b/tests/examples/shims/root.lua index e79e9652e..47ee71b32 100644 --- a/tests/examples/shims/root.lua +++ b/tests/examples/shims/root.lua @@ -32,7 +32,7 @@ function root.cursor() end local keys = {} -function root.keys(k) +function root._keys(k) keys = k or keys return keys end @@ -128,6 +128,9 @@ function root.fake_input(event_type, detail, x, y) fake_input_handlers[event_type](detail, x, y) end +function root._buttons() + return {} +end -- Send an artificial set of key events to trigger a key combination. -- It only works in the shims and should not be used with UTF-8 chars. @@ -174,6 +177,31 @@ function root._write_string(string, c) end end -return root + +function root.set_newindex_miss_handler(h) + rawset(root, "_ni_handler", h) +end + +function root.set_index_miss_handler(h) + rawset(root, "_i_handler", h) +end + +return setmetatable(root, { + __index = function(self, key) + if key == "screen" then + return screen[1] + end + local h = rawget(root,"_i_handler") + if h then + return h(self, key) + end + end, + __newindex = function(...) + local h = rawget(root,"_ni_handler") + if h then + h(...) + end + end, +}) -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/tests/test-miss-handlers.lua b/tests/test-miss-handlers.lua index c6b2baffa..7bbfea256 100644 --- a/tests/test-miss-handlers.lua +++ b/tests/test-miss-handlers.lua @@ -4,7 +4,10 @@ local mouse = mouse local class = tag local obj = class({}) local handler = require("gears.object.properties") +local abutton = require("awful.button") +local gtable = require("gears.table") local wibox = require("wibox") +local runner = require("_runner") awesome.connect_signal("debug::index::miss", error) awesome.connect_signal("debug::newindex::miss", error) @@ -68,6 +71,51 @@ assert(w2:get_children_by_id("main_textbox")[1]) assert(w2.main_background.main_textbox) assert(w2.main_background == w2:get_children_by_id("main_background")[1]) -require("_runner").run_steps({ function() return true end }) +-- You can't retry this, so if the assert above were false, assume it never +-- reaches this. + +local steps = { function() return true end } + +-- Try each combinations of current and legacy style accessors. +table.insert(steps, function()local b1, b2 = button({}, 1, function() end), button({}, 2, function() end) + root.buttons = {b1} + + assert(#root.buttons == 1 ) + assert(#root.buttons() == 1 ) + assert(root.buttons[1] == b1) + + root.buttons{b2} + + assert(#root.buttons == 1 ) + assert(#root.buttons() == 1 ) + assert(root.buttons()[1] == b2) + + local ab1, ab2 = abutton({}, 1, function() end), abutton({}, 2, function() end) + + root.buttons = {ab1} + assert(#root.buttons == 1) + assert(#root.buttons() == 4) + for i=1, 4 do + assert(root.buttons()[i] == ab1[i]) + end + + root.buttons(gtable.join(ab2)) + assert(#root.buttons == 1) + assert(#root.buttons() == 4) + for i=1, 4 do + assert(root.buttons()[i] == ab2[i]) + end + + root.buttons{ab1} + assert(#root.buttons == 1) + assert(#root.buttons() == 4) + for i=1, 4 do + assert(root.buttons()[i] == ab1[i]) + end + + return true +end) + +runner.run_steps(steps) -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80