diff --git a/awesomerc.lua b/awesomerc.lua
index a40e977b5..578a9a37f 100644
--- a/awesomerc.lua
+++ b/awesomerc.lua
@@ -49,27 +49,6 @@ editor_cmd = terminal .. " -e " .. editor
-- I suggest you to remap Mod4 to another key using xmodmap or other tools.
-- However, you can use another modifier like Mod1, but it may interact with others.
modkey = "Mod4"
-
--- @DOC_LAYOUT@
--- Table of layouts to cover with awful.layout.inc, order matters.
-awful.layout.layouts = {
- awful.layout.suit.floating,
- awful.layout.suit.tile,
- awful.layout.suit.tile.left,
- awful.layout.suit.tile.bottom,
- awful.layout.suit.tile.top,
- awful.layout.suit.fair,
- awful.layout.suit.fair.horizontal,
- awful.layout.suit.spiral,
- awful.layout.suit.spiral.dwindle,
- awful.layout.suit.max,
- awful.layout.suit.max.fullscreen,
- awful.layout.suit.magnifier,
- awful.layout.suit.corner.nw,
- -- awful.layout.suit.corner.ne,
- -- awful.layout.suit.corner.sw,
- -- awful.layout.suit.corner.se,
-}
-- }}}
-- {{{ Menu
@@ -95,10 +74,33 @@ mylauncher = awful.widget.launcher({ image = beautiful.awesome_icon,
menubar.utils.terminal = terminal -- Set the terminal for applications that require it
-- }}}
+-- {{{ Tag
+-- @DOC_LAYOUT@
+-- Table of layouts to cover with awful.layout.inc, order matters.
+tag.connect_signal("request::default_layouts", function()
+ awful.layout.append_default_layouts({
+ awful.layout.suit.floating,
+ awful.layout.suit.tile,
+ awful.layout.suit.tile.left,
+ awful.layout.suit.tile.bottom,
+ awful.layout.suit.tile.top,
+ awful.layout.suit.fair,
+ awful.layout.suit.fair.horizontal,
+ awful.layout.suit.spiral,
+ awful.layout.suit.spiral.dwindle,
+ awful.layout.suit.max,
+ awful.layout.suit.max.fullscreen,
+ awful.layout.suit.magnifier,
+ awful.layout.suit.corner.nw,
+ })
+end)
+-- }}}
+
+-- {{{ Wibar
+
-- Keyboard map indicator and switcher
mykeyboardlayout = awful.widget.keyboardlayout()
--- {{{ Wibar
-- Create a textclock widget
mytextclock = wibox.widget.textclock()
@@ -157,22 +159,15 @@ screen.connect_signal("request::desktop_decoration", function(s)
}
}
+ -- @TASKLIST_BUTTON@
-- Create a tasklist widget
s.mytasklist = awful.widget.tasklist {
screen = s,
filter = awful.widget.tasklist.filter.currenttags,
buttons = {
awful.button({ }, 1, function (c)
- if c == client.focus then
- c.minimized = true
- else
- c:emit_signal(
- "request::activate",
- "tasklist",
- {raise = true}
- )
- end
- end),
+ c:activate { context = "tasklist", action = "toggle_minimization" }
+ end),
awful.button({ }, 3, function() awful.menu.client_list { theme = { width = 250 } } end),
awful.button({ }, 4, function() awful.client.focus.byidx( 1) end),
awful.button({ }, 5, function() awful.client.focus.byidx(-1) end),
@@ -286,9 +281,7 @@ awful.keyboard.append_global_keybindings({
local c = awful.client.restore()
-- Focus restored client
if c then
- c:emit_signal(
- "request::activate", "key.unminimize", {raise = true}
- )
+ c:activate { raise = true, context = "key.unminimize" }
end
end,
{description = "restore minimized", group = "client"}),
@@ -383,15 +376,13 @@ awful.keyboard.append_global_keybindings({
client.connect_signal("request::default_mousebindings", function()
awful.mouse.append_client_mousebindings({
awful.button({ }, 1, function (c)
- c:emit_signal("request::activate", "mouse_click", {raise = true})
+ c:activate { context = "mouse_click" }
end),
awful.button({ modkey }, 1, function (c)
- c:emit_signal("request::activate", "mouse_click", {raise = true})
- awful.mouse.client.move(c)
+ c:activate { context = "mouse_click", action = "mouse_move" }
end),
awful.button({ modkey }, 3, function (c)
- c:emit_signal("request::activate", "mouse_click", {raise = true})
- awful.mouse.client.resize(c)
+ c:activate { context = "mouse_click", action = "mouse_resize"}
end),
})
end)
@@ -446,15 +437,13 @@ end)
-- }}}
-- {{{ Rules
--- Rules to apply to new clients (through the "manage" signal).
+-- Rules to apply to new clients.
-- @DOC_RULES@
awful.rules.rules = {
-- @DOC_GLOBAL_RULE@
-- All clients will match this rule.
{ rule = { },
- properties = { border_width = beautiful.border_width,
- border_color = beautiful.border_normal,
- focus = awful.client.focus.filter,
+ properties = { focus = awful.client.focus.filter,
raise = true,
screen = awful.screen.preferred,
placement = awful.placement.no_overlap+awful.placement.no_offscreen
@@ -506,34 +495,17 @@ awful.rules.rules = {
}
-- }}}
--- {{{ Signals
--- Signal function to execute when a new client appears.
--- @DOC_MANAGE_HOOK@
-client.connect_signal("manage", function (c)
- -- Set the windows at the slave,
- -- i.e. put it at the end of others instead of setting it master.
- -- if not awesome.startup then awful.client.setslave(c) end
-
- if awesome.startup
- and not c.size_hints.user_position
- and not c.size_hints.program_position then
- -- Prevent clients from being unreachable after screen count changes.
- awful.placement.no_offscreen(c)
- end
-end)
-
+-- {{{ Titlebars
-- @DOC_TITLEBARS@
-- Add a titlebar if titlebars_enabled is set to true in the rules.
client.connect_signal("request::titlebars", function(c)
-- buttons for the titlebar
local buttons = {
awful.button({ }, 1, function()
- c:emit_signal("request::activate", "titlebar", {raise = true})
- awful.mouse.client.move(c)
+ c:activate { context = "titlebar", action = "mouse_move" }
end),
awful.button({ }, 3, function()
- c:emit_signal("request::activate", "titlebar", {raise = true})
- awful.mouse.client.resize(c)
+ c:activate { context = "titlebar", action = "mouse_resize"}
end),
}
@@ -562,13 +534,4 @@ client.connect_signal("request::titlebars", function(c)
layout = wibox.layout.align.horizontal
}
end)
-
--- Enable sloppy focus, so that focus follows mouse.
-client.connect_signal("mouse::enter", function(c)
- c:emit_signal("request::activate", "mouse_enter", {raise = false})
-end)
-
--- @DOC_BORDER@
-client.connect_signal("focus", function(c) c.border_color = beautiful.border_focus end)
-client.connect_signal("unfocus", function(c) c.border_color = beautiful.border_normal end)
-- }}}
diff --git a/build-utils/check_for_invalid_requires.lua b/build-utils/check_for_invalid_requires.lua
index dd33443b0..741d1ad37 100755
--- a/build-utils/check_for_invalid_requires.lua
+++ b/build-utils/check_for_invalid_requires.lua
@@ -32,6 +32,9 @@ local allowed_deps = {
gears = true,
lgi = true,
wibox = true,
+
+ -- Necessary to lazy-load the deprecated modules.
+ ["awful.*"] = true
},
naughty = {
awful = true,
diff --git a/docs/05-awesomerc.md.lua b/docs/05-awesomerc.md.lua
index ffd0dbad0..dee0ab161 100644
--- a/docs/05-awesomerc.md.lua
+++ b/docs/05-awesomerc.md.lua
@@ -208,12 +208,6 @@ sections.DOC_DIALOG_RULE = [[
]]
-
-sections.DOC_MANAGE_HOOK = [[
-
-]]
-
-
sections.DOC_TITLEBARS = [[
]]
@@ -227,11 +221,6 @@ sections.DOC_CSD_TITLEBARS = [[
See `client.requests_no_titlebar` for more details.
]]
-
-sections.DOC_BORDER = [[
-
-]]
-
-- Ask ldoc to generate links
local function add_links(line)
diff --git a/docs/89-NEWS.md b/docs/89-NEWS.md
index 73595bf79..7089f99fa 100644
--- a/docs/89-NEWS.md
+++ b/docs/89-NEWS.md
@@ -62,6 +62,14 @@ This document was last updated at commit v4.3-197-g9085ed631.
objects. If you used these low level APIs to add keys and buttons dynamically,
please migrate your code to the corresponding `:append_` and `:remove_`
client methods.
+ * `beautiful.border_width` and `beautiful.border_color` are now honored even
+ when the part related to borders is removed from `rc.lua`. Set them
+ appropriately in your theme or disconnect the default `request::border`
+ handler.
+ * The order by which the client rules compute the geometry have changed
+ slightly. The border is now applied before the titlebar offset. This should
+ not affect most users unless you had mitigated the bug it fixes by adding
+ the titlebar offset in your rules.
# Awesome window manager framework version 4.3 changes
diff --git a/docs/90-FAQ.md b/docs/90-FAQ.md
index 7e7d1c2b2..9257632df 100644
--- a/docs/90-FAQ.md
+++ b/docs/90-FAQ.md
@@ -315,17 +315,17 @@ You can call the `awful.layout.set()` function, here's an example:
### Why are new clients urgent by default?
-You can change this by redefining `awful.ewmh.activate(c)` in your rc.lua. If
+You can change this by redefining `awful.permissions.activate(c)` in your rc.lua. If
you don't want new clients to be urgent by default put this in your rc.lua:
- client.disconnect_signal("request::activate", awful.ewmh.activate)
- function awful.ewmh.activate(c)
+ client.disconnect_signal("request::activate", awful.permissions.activate)
+ function awful.permissions.activate(c)
if c:isvisible() then
client.focus = c
c:raise()
end
end
- client.connect_signal("request::activate", awful.ewmh.activate)
+ client.connect_signal("request::activate", awful.permissions.activate)
## Usage
diff --git a/docs/common/client_theme.ldoc b/docs/common/client_theme.ldoc
new file mode 100644
index 000000000..762138577
--- /dev/null
+++ b/docs/common/client_theme.ldoc
@@ -0,0 +1,492 @@
+*/
+
+
+/**
+ * The fallback border color when the client is floating.
+ *
+ * @beautiful beautiful.border_color_floating
+ * @param color
+ * @see request::border
+ * @see beautiful.border_color_floating_active
+ * @see beautiful.border_color_floating_normal
+ * @see beautiful.border_color_floating_urgent
+ * @see beautiful.border_color_floating_new
+ */
+
+/**
+ * The fallback border color when the client is mazimized.
+ *
+ * @beautiful beautiful.border_color_mazimized
+ * @param color
+ * @see request::border
+ * @see beautiful.border_color_maximized_active
+ * @see beautiful.border_color_maximized_normal
+ * @see beautiful.border_color_maximized_urgent
+ * @see beautiful.border_color_maximized_new
+ */
+
+/**
+ * The border color when the client is active.
+ *
+ * @beautiful beautiful.border_color_active
+ * @param color
+ * @see request::border
+ */
+
+/**
+ * The border color when the client is not active.
+ *
+ * @beautiful beautiful.border_color_normal
+ * @param color
+ * @see request::border
+ */
+
+/**
+ * The border color when the client has the urgent property set.
+ *
+ * @beautiful beautiful.border_color_urgent
+ * @param color
+ * @see request::border
+ */
+
+/**
+ * The border color when the client is not active and new.
+ *
+ * @beautiful beautiful.border_color_new
+ * @param color
+ * @see request::border
+ */
+
+/**
+ * The border color when the (floating) client is active.
+ *
+ * @beautiful beautiful.border_color_floating_active
+ * @param color
+ * @see request::border
+ */
+
+/**
+ * The border color when the (floating) client is not active.
+ *
+ * @beautiful beautiful.border_color_floating_normal
+ * @param color
+ * @see request::border
+ */
+
+/**
+ * The border color when the (floating) client has the urgent property set.
+ *
+ * @beautiful beautiful.border_color_floating_urgent
+ * @param color
+ * @see request::border
+ */
+
+/**
+ * The border color when the (floating) client is not active and new.
+ *
+ * @beautiful beautiful.border_color_floating_new
+ * @param color
+ * @see request::border
+ */
+
+/**
+ * The border color when the (maximized) client is active.
+ *
+ * @beautiful beautiful.border_color_maximized_active
+ * @param color
+ * @see request::border
+ */
+
+/**
+ * The border color when the (maximized) client is not active.
+ *
+ * @beautiful beautiful.border_color_maximized_normal
+ * @param color
+ * @see request::border
+ */
+
+/**
+ * The border color when the (maximized) client has the urgent property set.
+ *
+ * @beautiful beautiful.border_color_maximized_urgent
+ * @param color
+ * @see request::border
+ */
+
+/**
+ * The border color when the (maximized) client is not active and new.
+ *
+ * @beautiful beautiful.border_color_maximized_new
+ * @param color
+ * @see request::border
+ */
+
+/**
+ * The border color when the (fullscreen) client is active.
+ *
+ * @beautiful beautiful.border_color_fullscreen_active
+ * @param color
+ * @see request::border
+ */
+
+/**
+ * The border color when the (fullscreen) client is not active.
+ *
+ * @beautiful beautiful.border_color_fullscreen_normal
+ * @param color
+ * @see request::border
+ */
+
+/**
+ * The border color when the (fullscreen) client has the urgent property set.
+ *
+ * @beautiful beautiful.border_color_fullscreen_urgent
+ * @param color
+ * @see request::border
+ */
+
+/**
+ * The border color when the (fullscreen) client is not active and new.
+ *
+ * @beautiful beautiful.border_color_fullscreen_new
+ * @param color
+ * @see request::border
+ */
+
+/**
+ * The fallback border width when nothing else is set.
+ *
+ * @beautiful beautiful.border_width
+ * @param integer
+ * @see request::border
+ * @see beautiful.border_width_floating
+ * @see beautiful.border_width_mazimized
+ * @see beautiful.border_width_floating_active
+ * @see beautiful.border_width_floating_normal
+ * @see beautiful.border_width_floating_urgent
+ * @see beautiful.border_width_floating_new
+ * @see beautiful.border_width_maximized_active
+ * @see beautiful.border_width_maximized_normal
+ * @see beautiful.border_width_maximized_urgent
+ * @see beautiful.border_width_maximized_new
+ */
+
+/**
+ * The fallback border width when the client is floating.
+ *
+ * @beautiful beautiful.border_width_floating
+ * @param integer
+ * @see request::border
+ * @see beautiful.border_width_floating_active
+ * @see beautiful.border_width_floating_normal
+ * @see beautiful.border_width_floating_urgent
+ * @see beautiful.border_width_floating_new
+ */
+
+/**
+ * The fallback border width when the client is mazimized.
+ *
+ * @beautiful beautiful.border_width_mazimized
+ * @param integer
+ * @see request::border
+ * @see beautiful.border_width_maximized_active
+ * @see beautiful.border_width_maximized_normal
+ * @see beautiful.border_width_maximized_urgent
+ * @see beautiful.border_width_maximized_new
+ */
+
+/**
+ * The client border width for the normal clients.
+ *
+ * @beautiful beautiful.border_width_normal
+ * @param integer
+ * @see request::border
+ */
+
+/**
+ * The client border width for the active client.
+ *
+ * @beautiful beautiful.border_width_active
+ * @param integer
+ * @see request::border
+ */
+
+/**
+ * The client border width for the urgent clients.
+ *
+ * @beautiful beautiful.border_width_urgent
+ * @param integer
+ * @see request::border
+ */
+
+/**
+ * The client border width for the new clients.
+ *
+ * @beautiful beautiful.border_width_new
+ * @param integer
+ * @see request::border
+ */
+
+/**
+ * The client border width for the normal floating clients.
+ *
+ * @beautiful beautiful.border_width_floating_normal
+ * @param integer
+ * @see request::border
+ */
+
+/**
+ * The client border width for the active floating client.
+ *
+ * @beautiful beautiful.border_width_floating_active
+ * @param integer
+ * @see request::border
+ */
+
+/**
+ * The client border width for the urgent floating clients.
+ *
+ * @beautiful beautiful.border_width_floating_urgent
+ * @param integer
+ * @see request::border
+ */
+
+/**
+ * The client border width for the new floating clients.
+ *
+ * @beautiful beautiful.border_width_floating_new
+ * @param integer
+ * @see request::border
+ */
+
+/**
+ * The client border width for the normal maximized clients.
+ *
+ * @beautiful beautiful.border_width_maximized_normal
+ * @param integer
+ * @see request::border
+ */
+
+/**
+ * The client border width for the active maximized client.
+ *
+ * @beautiful beautiful.border_width_maximized_active
+ * @param integer
+ * @see request::border
+ */
+
+/**
+ * The client border width for the urgent maximized clients.
+ *
+ * @beautiful beautiful.border_width_maximized_urgent
+ * @param integer
+ * @see request::border
+ */
+
+/**
+ * The client border width for the new maximized clients.
+ *
+ * @beautiful beautiful.border_width_maximized_new
+ * @param integer
+ * @see request::border
+ */
+
+/**
+ * The client border width for the normal fullscreen clients.
+ *
+ * @beautiful beautiful.border_width_fullscreen_normal
+ * @param integer
+ * @see request::border
+ */
+
+/**
+ * The client border width for the active fullscreen client.
+ *
+ * @beautiful beautiful.border_width_fullscreen_active
+ * @param integer
+ * @see request::border
+ */
+
+/**
+ * The client border width for the urgent fullscreen clients.
+ *
+ * @beautiful beautiful.border_width_fullscreen_urgent
+ * @param integer
+ * @see request::border
+ */
+
+/**
+ * The client border width for the new fullscreen clients.
+ *
+ * @beautiful beautiful.border_width_fullscreen_new
+ * @param integer
+ * @see request::border
+ */
+
+/**
+ * The client opacity for the normal clients.
+ *
+ * A number between 0 and 1.
+ *
+ * @beautiful beautiful.opacity_normal
+ * @param[opt=1] number
+ * @see request::border
+ */
+
+/**
+ * The client opacity for the active client.
+ *
+ * A number between 0 and 1.
+ *
+ * @beautiful beautiful.opacity_active
+ * @param[opt=1] number
+ * @see request::border
+ */
+
+/**
+ * The client opacity for the urgent clients.
+ *
+ * A number between 0 and 1.
+ *
+ * @beautiful beautiful.opacity_urgent
+ * @param[opt=1] number
+ * @see request::border
+ */
+
+/**
+ * The client opacity for the new clients.
+ *
+ * A number between 0 and 1.
+ *
+ * @beautiful beautiful.opacity_new
+ * @param[opt=1] number
+ * @see request::border
+ */
+
+/**
+ * The client opacity for the normal floating clients.
+ *
+ * A number between 0 and 1.
+ *
+ * @beautiful beautiful.opacity_floating_normal
+ * @param[opt=1] number
+ * @see request::border
+ */
+
+/**
+ * The client opacity for the active floating client.
+ *
+ * A number between 0 and 1.
+ *
+ * @beautiful beautiful.opacity_floating_active
+ * @param[opt=1] number
+ * @see request::border
+ */
+
+/**
+ * The client opacity for the urgent floating clients.
+ *
+ * A number between 0 and 1.
+ *
+ * @beautiful beautiful.opacity_floating_urgent
+ * @param[opt=1] number
+ * @see request::border
+ */
+
+/**
+ * The client opacity for the new floating clients.
+ *
+ * A number between 0 and 1.
+ *
+ * @beautiful beautiful.opacity_floating_new
+ * @param[opt=1] number
+ * @see request::border
+ */
+
+/**
+ * The client opacity for the normal maximized clients.
+ *
+ * A number between 0 and 1.
+ *
+ * @beautiful beautiful.opacity_maximized_normal
+ * @param[opt=1] number
+ * @see request::border
+ */
+
+/**
+ * The client opacity for the active maximized client.
+ *
+ * A number between 0 and 1.
+ *
+ * @beautiful beautiful.opacity_maximized_active
+ * @param[opt=1] number
+ * @see request::border
+ */
+
+/**
+ * The client opacity for the urgent maximized clients.
+ *
+ * A number between 0 and 1.
+ *
+ * @beautiful beautiful.opacity_maximized_urgent
+ * @param[opt=1] number
+ * @see request::border
+ */
+
+/**
+ * The client opacity for the new maximized clients.
+ *
+ * A number between 0 and 1.
+ *
+ * @beautiful beautiful.opacity_maximized_new
+ * @param[opt=1] number
+ * @see request::border
+ */
+/**
+ * The client opacity for the normal fullscreen clients.
+ *
+ * A number between 0 and 1.
+ *
+ * @beautiful beautiful.opacity_fullscreen_normal
+ * @param[opt=1] number
+ * @see request::border
+ */
+
+/**
+ * The client opacity for the active fullscreen client.
+ *
+ * A number between 0 and 1.
+ *
+ * @beautiful beautiful.opacity_fullscreen_active
+ * @param[opt=1] number
+ * @see request::border
+ */
+
+/**
+ * The client opacity for the urgent fullscreen clients.
+ *
+ * A number between 0 and 1.
+ *
+ * @beautiful beautiful.opacity_fullscreen_urgent
+ * @param[opt=1] number
+ * @see request::border
+ */
+
+/**
+ * The client opacity for the new fullscreen clients.
+ *
+ * A number between 0 and 1.
+ *
+ * @beautiful beautiful.opacity_fullscreen_new
+ * @param[opt=1] number
+ * @see request::border
+ */
+
+/**
+ * The marked clients border color.
+ * Note that only solid colors are supported.
+ * @beautiful beautiful.border_marked
+ * @param color
+ */
+
+/*
diff --git a/docs/config.ld b/docs/config.ld
index 3a906f563..35faea832 100644
--- a/docs/config.ld
+++ b/docs/config.ld
@@ -70,6 +70,8 @@ new_type("deprecatedproperty", "Deprecated object properties", false, "Type cons
new_type("method", "Object methods ", false, "Parameters")
-- New type for signals
new_type("signal", "Signals", false, "Arguments")
+-- Deprecated signals.
+new_type("deprecatedsignal", "Deprecated signals", false, "Arguments")
-- New type for signals connections
new_type("signalhandler", "Request handlers", false, "Arguments")
-- Allow objects to define a set of beautiful properties affecting them
@@ -351,6 +353,12 @@ add_custom_tag {
hidden = true
}
+-- Define when a signal is only emitted on a class rather than on objects.
+add_custom_tag {
+ name = "classsignal",
+ hidden = true,
+}
+
-- Specify when this an item was deprecated.
-- @deprecatedin 4.4 Optional message.
add_custom_tag {
@@ -394,6 +402,20 @@ add_custom_tag {
},
}
+add_custom_tag {
+ name = "request",
+ title = "Requested actions or permissions",
+ params = {
+ { name = "class" },
+ { name = "type" },
+ { name = "context" },
+ { name = "default" },
+ },
+ table = {
+ "Class", "Permission", "Context", "Default", "Description"
+ },
+}
+
-- More fitting section names
kind_names={topic='Documentation', module='Libraries', script='Sample files'}
@@ -442,6 +464,7 @@ file = {
'../lib/awful/screen/dpi.lua',
'../lib/awful/startup_notification.lua',
'../lib/awful/mouse/drag_to_tag.lua',
+ '../lib/awful/permissions/_common.lua',
'../lib/gears/init.lua',
'../lib/wibox/layout/init.lua',
'../lib/wibox/container/init.lua',
@@ -468,6 +491,7 @@ file = {
'../lib/awful/widget/progressbar.lua',
'../lib/awful/widget/textclock.lua',
'../lib/awful/wibox.lua',
+ '../lib/awful/ewmh.lua',
'../lib/wibox/layout/constraint.lua',
'../lib/wibox/layout/margin.lua',
'../lib/wibox/layout/mirror.lua',
@@ -497,12 +521,14 @@ local summarize = {
emits = {index = 1, title = "signals" },
propemits = {index = 2, title = "signals" },
usebeautiful = {index = 3, title = "theme variables"},
- propbeautiful = {index = 4, title = "theme variables"}
+ propbeautiful = {index = 4, title = "theme variables"},
+ request = {index = 5, title = "permissions" },
}
local delimiter_for_tag = {
usebeautiful = { "table class='widget_list' border=1", "table", "tr", "tr", {"Theme variable", "Usage"}},
propbeautiful = { "table class='widget_list' border=1", "table", "tr", "tr", {"Theme variable", "Usage"}},
+ request = { "table class='widget_list' border=1", "table", "tr", "tr", {"Class", "Permission", "Context", "Default", "Description"}},
}
-- Use the first word of the subtag content to map it to its tag.
@@ -736,6 +762,13 @@ local show_return = {
deprecated = true,
}
+-- The different type of deprecation.
+local is_deprecated = {
+ deprecated = true,
+ deprecatedproperty = true,
+ deprecatedsignal = true,
+}
+
custom_display_name_handler = function(item, default_handler)
init_custom_types(item)
@@ -776,7 +809,7 @@ custom_display_name_handler = function(item, default_handler)
end
end
- if item.type == "deprecated" or item.type == "deprecatedproperty" then
+ if is_deprecated[item.type] then
return ret .. " [deprecated]"
end
diff --git a/event.c b/event.c
index 989c0110c..030f86166 100644
--- a/event.c
+++ b/event.c
@@ -478,7 +478,7 @@ event_handle_destroynotify(xcb_destroy_notify_event_t *ev)
client_t *c;
if((c = client_getbywin(ev->window)))
- client_unmanage(c, false);
+ client_unmanage(c, CLIENT_UNMANAGE_DESTROYED);
else
for(int i = 0; i < globalconf.embedded.len; i++)
if(globalconf.embedded.tab[i].win == ev->window)
@@ -856,7 +856,7 @@ event_handle_unmapnotify(xcb_unmap_notify_event_t *ev)
client_t *c;
if((c = client_getbywin(ev->window)))
- client_unmanage(c, true);
+ client_unmanage(c, CLIENT_UNMANAGE_UNMAP);
}
/** The randr screen change notify event handler.
@@ -993,7 +993,7 @@ event_handle_reparentnotify(xcb_reparent_notify_event_t *ev)
/* Ignore reparents to the root window, they *might* be caused by
* ourselves if a client quickly unmaps and maps itself again. */
if (ev->parent != globalconf.screen->root)
- client_unmanage(c, true);
+ client_unmanage(c, CLIENT_UNMANAGE_REPARENT);
}
else if (ev->parent != globalconf.systray.window) {
/* Embedded window moved elsewhere, end of embedding */
diff --git a/ewmh.c b/ewmh.c
index d720a3043..ad7f51078 100644
--- a/ewmh.c
+++ b/ewmh.c
@@ -238,8 +238,8 @@ ewmh_init_lua(void)
luaA_class_connect_signal(L, &client_class, "focus", ewmh_update_net_active_window);
luaA_class_connect_signal(L, &client_class, "unfocus", ewmh_update_net_active_window);
- luaA_class_connect_signal(L, &client_class, "manage", ewmh_update_net_client_list);
- luaA_class_connect_signal(L, &client_class, "unmanage", ewmh_update_net_client_list);
+ luaA_class_connect_signal(L, &client_class, "request::manage", ewmh_update_net_client_list);
+ luaA_class_connect_signal(L, &client_class, "request::unmanage", ewmh_update_net_client_list);
luaA_class_connect_signal(L, &client_class, "property::modal" , ewmh_client_update_hints);
luaA_class_connect_signal(L, &client_class, "property::fullscreen" , ewmh_client_update_hints);
luaA_class_connect_signal(L, &client_class, "property::maximized_horizontal" , ewmh_client_update_hints);
@@ -256,7 +256,7 @@ ewmh_init_lua(void)
luaA_class_connect_signal(L, &client_class, "property::titlebar_right" , ewmh_client_update_frame_extents);
luaA_class_connect_signal(L, &client_class, "property::titlebar_left" , ewmh_client_update_frame_extents);
luaA_class_connect_signal(L, &client_class, "property::border_width" , ewmh_client_update_frame_extents);
- luaA_class_connect_signal(L, &client_class, "manage", ewmh_client_update_frame_extents);
+ luaA_class_connect_signal(L, &client_class, "request::manage", ewmh_client_update_frame_extents);
/* NET_CURRENT_DESKTOP handling */
luaA_class_connect_signal(L, &client_class, "focus", ewmh_update_net_current_desktop);
luaA_class_connect_signal(L, &client_class, "unfocus", ewmh_update_net_current_desktop);
@@ -412,14 +412,17 @@ ewmh_process_state_atom(client_t *c, xcb_atom_t state, int set)
{
if(set == _NET_WM_STATE_REMOVE) {
lua_pushboolean(L, false);
+ /*TODO v5: Add a context */
luaA_object_emit_signal(L, -2, "request::urgent", 1);
}
else if(set == _NET_WM_STATE_ADD) {
lua_pushboolean(L, true);
+ /*TODO v5: Add a context */
luaA_object_emit_signal(L, -2, "request::urgent", 1);
}
else if(set == _NET_WM_STATE_TOGGLE) {
lua_pushboolean(L, !c->urgent);
+ /*TODO v5: Add a context */
luaA_object_emit_signal(L, -2, "request::urgent", 1);
}
}
@@ -436,6 +439,7 @@ ewmh_process_desktop(client_t *c, uint32_t desktop)
{
luaA_object_push(L, c);
lua_pushboolean(L, true);
+ /*TODO v5: Move the context argument to arg1 */
luaA_object_emit_signal(L, -2, "request::tag", 1);
/* Pop the client, arguments are already popped */
lua_pop(L, 1);
@@ -444,6 +448,7 @@ ewmh_process_desktop(client_t *c, uint32_t desktop)
{
luaA_object_push(L, c);
luaA_object_push(L, globalconf.tags.tab[idx]);
+ /*TODO v5: Move the context argument to arg1 */
luaA_object_emit_signal(L, -2, "request::tag", 1);
/* Pop the client, arguments are already popped */
lua_pop(L, 1);
@@ -462,7 +467,8 @@ ewmh_process_client_message(xcb_client_message_event_t *ev)
{
lua_State *L = globalconf_get_lua_State();
luaA_object_push(L, globalconf.tags.tab[idx]);
- luaA_object_emit_signal(L, -1, "request::select", 0);
+ lua_pushstring(L, "ewmh");
+ luaA_object_emit_signal(L, -1, "request::select", 1);
lua_pop(L, 1);
}
}
diff --git a/lib/awful/autofocus.lua b/lib/awful/autofocus.lua
index 5647a9651..c11d4831e 100644
--- a/lib/awful/autofocus.lua
+++ b/lib/awful/autofocus.lua
@@ -1,74 +1,15 @@
---------------------------------------------------------------------------
---- Autofocus functions.
---
--- When loaded, this module makes sure that there's always a client that will
--- have focus on events such as tag switching, client unmanaging, etc.
---
--- @author Julien Danjou <julien@danjou.info>
--- @copyright 2009 Julien Danjou
--- @module awful.autofocus
+-- This module used to be a "require only" module which, when explicitly
+-- required, would allow handle focus when switching tags and other useful
+-- corner cases. This code has been migrated to a more standard request::
+-- API. The content itself is now in `awful.permissions`. This was required
+-- to preserve backward compatibility since this module may or may not have
+-- been loaded.
---------------------------------------------------------------------------
+require("awful.permissions._common")._deprecated_autofocus_in_use()
-local client = client
-local aclient = require("awful.client")
-local timer = require("gears.timer")
-
-local function filter_sticky(c)
- return not c.sticky and aclient.focus.filter(c)
-end
-
---- Give focus when clients appear/disappear.
---
--- @param obj An object that should have a .screen property.
-local function check_focus(obj)
- if (not obj.screen) or not obj.screen.valid then return end
- -- When no visible client has the focus...
- if not client.focus or not client.focus:isvisible() then
- local c = aclient.focus.history.get(screen[obj.screen], 0, filter_sticky)
- if not c then
- c = aclient.focus.history.get(screen[obj.screen], 0, aclient.focus.filter)
- end
- if c then
- c:emit_signal("request::activate", "autofocus.check_focus",
- {raise=false})
- end
- end
-end
-
---- Check client focus (delayed).
--- @param obj An object that should have a .screen property.
-local function check_focus_delayed(obj)
- timer.delayed_call(check_focus, {screen = obj.screen})
-end
-
---- Give focus on tag selection change.
---
--- @tparam tag t A tag object
-local function check_focus_tag(t)
- local s = t.screen
- if (not s) or (not s.valid) then return end
- s = screen[s]
- check_focus({ screen = s })
- if client.focus and screen[client.focus.screen] ~= s then
- local c = aclient.focus.history.get(s, 0, filter_sticky)
- if not c then
- c = aclient.focus.history.get(s, 0, aclient.focus.filter)
- end
- if c then
- c:emit_signal("request::activate", "autofocus.check_focus_tag",
- {raise=false})
- end
- end
-end
-
-tag.connect_signal("property::selected", function (t)
- timer.delayed_call(check_focus_tag, t)
-end)
-client.connect_signal("unmanage", check_focus_delayed)
-client.connect_signal("tagged", check_focus_delayed)
-client.connect_signal("untagged", check_focus_delayed)
-client.connect_signal("property::hidden", check_focus_delayed)
-client.connect_signal("property::minimized", check_focus_delayed)
-client.connect_signal("property::sticky", check_focus_delayed)
-
--- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
+--require("gears.debug").deprecate(
+-- "The `awful.autofocus` module is deprecated, remove the require() and "..
+-- "look at the new `rc.lua` granted permission section in the client rules",
+-- {deprecated_in=5}
+--)
diff --git a/lib/awful/client.lua b/lib/awful/client.lua
index 2a7c978d6..b142a3aa8 100644
--- a/lib/awful/client.lua
+++ b/lib/awful/client.lua
@@ -14,6 +14,8 @@ local object = require("gears.object")
local grect = require("gears.geometry").rectangle
local gmath = require("gears.math")
local gtable = require("gears.table")
+local amousec = require("awful.mouse.client")
+local pcommon = require("awful.permissions._common")
local pairs = pairs
local type = type
local ipairs = ipairs
@@ -169,6 +171,8 @@ end
-- first tag additionally) when the client is not visible.
-- If it is a function, it will be called with the client and its first
-- tag as arguments.
+-- @request client activate client.jumpto granted When a client is activated
+-- because `c:jump_to()` is called.
function client.object.jump_to(self, merge)
local s = get_screen(screen.focused())
-- focus the screen
@@ -300,6 +304,8 @@ end
-- @staticfct awful.client.swap.global_bydirection
-- @param dir The direction, can be either "up", "down", "left" or "right".
-- @client[opt] sel The client.
+-- @request client activate client.swap.global_bydirection granted When a client
+-- could be activated because `awful.client.swap.global_bydirection` was called.
function client.swap.global_bydirection(dir, sel)
sel = sel or capi.client.focus
local scr = get_screen(sel and sel.screen or screen.focused())
@@ -461,6 +467,8 @@ end
--- Move a client to a tag.
-- @method move_to_tag
-- @tparam tag target The tag to move the client to.
+-- @request client activate client.movetotag granted When a client could be
+-- activated because `c:move_to_tag()` was called.
function client.object.move_to_tag(self, target)
local s = target.screen
if self and s then
@@ -524,6 +532,8 @@ end
-- @tparam[opt=c.screen.index+1] screen s The screen, default to current + 1.
-- @see screen
-- @see request::activate
+-- @request client activate client.movetoscreen granted When a client could be
+-- activated because `c:move_to_screen()` was called.
function client.object.move_to_screen(self, s)
if self then
local sc = capi.screen.count()
@@ -535,7 +545,7 @@ function client.object.move_to_screen(self, s)
end
s = get_screen(s)
if get_screen(self.screen) ~= s then
- local sel_is_focused = self == capi.client.focus
+ local sel_is_focused = self.active
self.screen = s
screen.focus(s)
@@ -687,6 +697,12 @@ function client.object.set_floating(c, s)
c:geometry(client.property.get(c, "floating_geometry"))
end
c.screen = scr
+
+ if s then
+ c:emit_signal("request::border", "floating", {})
+ else
+ c:emit_signal("request::border", (c.active and "" or "in").."active", {})
+ end
end
end
@@ -790,6 +806,7 @@ function client.floating.get(c)
end
--- The client floating state.
+--
-- If the client is part of the tiled layout or free floating.
--
-- Note that some windows might be floating even if you
@@ -801,7 +818,13 @@ end
-- * *property::floating*
--
-- @property floating
--- @param boolean The floating state
+-- @tparam boolean floating The floating state.
+-- @request client border floating granted When a border update is required
+-- because the client focus status changed.
+-- @request client border active granted When a client becomes active and is not
+-- floating.
+-- @request client border inactive granted When a client stop being active and
+-- is not floating.
function client.object.get_floating(c)
c = c or capi.client.focus
@@ -833,6 +856,17 @@ local function update_implicitly_floating(c)
if cur ~= new then
client.property.set(c, "_implicitly_floating", new)
c:emit_signal("property::floating")
+
+ -- Don't send the border signals as they would be sent twice (with this
+ -- one having the wrong context). There is some `property::` signal
+ -- sent before `request::manage`.
+ if client.property.get(c, "_border_init") then
+ if cur then
+ c:emit_signal("request::border", "floating", {})
+ else
+ c:emit_signal("request::border", (c.active and "" or "in").."active", {})
+ end
+ end
end
end
@@ -842,7 +876,7 @@ capi.client.connect_signal("property::maximized_vertical", update_implicitly_flo
capi.client.connect_signal("property::maximized_horizontal", update_implicitly_floating)
capi.client.connect_signal("property::maximized", update_implicitly_floating)
capi.client.connect_signal("property::size_hints", update_implicitly_floating)
-capi.client.connect_signal("manage", update_implicitly_floating)
+capi.client.connect_signal("request::manage", update_implicitly_floating)
--- Toggle the floating state of a client between 'auto' and 'true'.
-- Use `c.floating = not c.floating`
@@ -1388,7 +1422,182 @@ function client.object.set_shape(self, shape)
set_shape(self)
end
--- Register standards signals
+-- Proxy those properties to decorate their accessors with an extra flag to
+-- define when they are set by the user. This allows to "manage" the value of
+-- those properties internally until they are manually overridden.
+for _, prop in ipairs { "border_width", "border_color", "opacity" } do
+ client.object["get_"..prop] = function(self)
+ return self["_"..prop]
+ end
+ client.object["set_"..prop] = function(self, value)
+ self._private["_user_"..prop] = true
+ self["_"..prop] = value
+ end
+end
+
+--- Activate (focus) a client.
+--
+-- This method is the correct way to focus a client. While
+-- `client.focus = my_client` works and is commonly used in older code, it has
+-- some drawbacks. The most obvious one is that it bypasses the activate
+-- filters. It also doesn't handle minimized clients well and requires a lot
+-- of boilerplate code to make work properly.
+--
+-- The valid `args.actions` are:
+--
+-- * **mouse_move**: Move the client when the mouse cursor moves until the
+-- mouse buttons are release.
+-- * **mouse_resize**: Resize the client when the mouse cursor moves until the
+-- mouse buttons are release.
+-- * **mouse_center**: Move the mouse cursor to the center of the client if it
+-- isn't already within its geometry,
+-- * **toggle_minimization**: If the client is already active, minimize it.
+--
+-- @method activate
+-- @tparam table args
+-- @tparam[opt=other] string args.context Why was this activate called?
+-- @tparam[opt=true] boolean args.raise Raise the client to the top of its layer.
+-- @tparam[opt=false] boolean args.force Force the activation even for unfocusable
+-- clients.
+-- @tparam[opt=false] boolean args.switch_to_tags
+-- @tparam[opt=false] boolean args.switch_to_tag
+-- @tparam[opt=false] boolean args.action Once activated, perform an action.
+-- @tparam[opt=false] boolean args.toggle_minimization
+-- @see awful.permissions.add_activate_filter
+-- @see request::activate
+-- @see active
+function client.object.activate(c, args)
+ local new_args = setmetatable({}, {__index = args or {}})
+
+ -- Set the default arguments.
+ new_args.raise = new_args.raise == nil and true or args.raise
+
+ if c == capi.client.focus and new_args.action == "toggle_minimization" then
+ c.minimized = true
+ else
+ c:emit_signal(
+ "request::activate",
+ new_args.context or "other",
+ new_args
+ )
+ end
+
+ if new_args.action and new_args.action == "mouse_move" then
+ amousec.move(c)
+ elseif new_args.action and new_args.action == "mouse_resize" then
+ amousec.resize(c)
+ elseif new_args.action and new_args.action == "mouse_center" then
+ local coords, geo = mouse.mouse.coords(), c:geometry()
+ coords.width, coords.height = 1,1
+
+ if not grect.area_intersect_area(geo, coords) then
+ -- Do not use `awful.placement` to avoid an useless circular
+ -- dependency. Centering is *very* simple.
+ mouse.mouse.coords {
+ x = geo.x + math.ceil(geo.width /2),
+ y = geo.y + math.ceil(geo.height/2)
+ }
+ end
+ end
+end
+
+--- Grant a permission for a client.
+--
+-- @method grant
+-- @tparam string permission The permission name (just the name, no `request::`).
+-- @tparam string context The reason why this permission is requested.
+-- @see awful.permissions
+
+--- Deny a permission for a client.
+--
+-- @method deny
+-- @tparam string permission The permission name (just the name, no `request::`).
+-- @tparam string context The reason why this permission is requested.
+-- @see awful.permissions
+
+pcommon.setup_grant(client.object, "client")
+
+--- Return true if the client is active (has focus).
+--
+-- This property is **READ ONLY**. Use `c:activate { context = "myreason" }`
+-- to change the focus.
+--
+-- The reason for this is that directly setting the focus
+-- (which can also be done using `client.focus = c`) will bypass the focus
+-- stealing filters. This is easy at first, but as this gets called from more
+-- and more places, it quickly become unmanageable. This coding style is
+-- recommended for maintainable code:
+--
+-- -- Check if a client has focus:
+-- if c.active then
+-- -- do something
+-- end
+--
+-- -- Check if there is a active (focused) client:
+-- if client.focus ~= nil then
+-- -- do something
+-- end
+--
+-- -- Get the active (focused) client:
+-- local c = client.focus
+--
+-- -- Set the focus:
+-- c:activate {
+-- context = "myreason",
+-- switch_to_tag = true,
+-- }
+--
+-- -- Get notified when a client gets or loses the focus:
+-- c:connect_signal("property::active", function(c, is_active)
+-- -- do something
+-- end)
+--
+-- -- Get notified when any client gets or loses the focus:
+-- client.connect_signal("property::active", function(c, is_active)
+-- -- do something
+-- end)
+--
+-- -- Get notified when any client gets the focus:
+-- client.connect_signal("focus", function(c)
+-- -- do something
+-- end)
+--
+-- -- Get notified when any client loses the focus:
+-- client.connect_signal("unfocus", function(c)
+-- -- do something
+-- end)
+--
+-- @property active
+-- @tparam boolean active
+-- @request client border active granted When a client becomes active.
+-- @request client border inactive granted When a client stop being active.
+-- @see activate
+-- @see request::activate
+-- @see awful.permissions.add_activate_filter
+
+function client.object.get_active(c)
+ return capi.client.focus == c
+end
+
+function client.object.set_active(c, value)
+ if value then
+ -- Do it, but print an error popup. Setting the focus without a context
+ -- breaks the filters. This seems a good idea at first, then cause
+ -- endless pain. QtWidgets also enforces this.
+ c:activate()
+ assert(false, "You cannot set `active` directly, use `c:activate()`.")
+ else
+ -- Be permissive given the alternative is a bit convoluted.
+ capi.client.focus = nil
+ gdebug.print_warning(
+ "Consider using `client.focus = nil` instead of `c.active = false"
+ )
+ end
+end
+
+capi.client.connect_signal("property::active", function(c)
+ c:emit_signal("request::border", (c.active and "" or "in").."active", {})
+end)
--- The last geometry when client was floating.
-- @signal property::floating_geometry
@@ -1398,23 +1607,62 @@ end
-- @tparam[opt=nil] string content The context (like "rules")
-- @tparam[opt=nil] table hints Some hints.
---- The client marked signal (deprecated).
--- @signal marked
+--- The client marked signal.
+-- @deprecatedsignal marked
---- The client unmarked signal (deprecated).
--- @signal unmarked
+--- The client unmarked signal.
+-- @deprecatedsignal unmarked
+
+--- Emited when the border client might need to be update.
+--
+-- The context are:
+--
+-- * **added**: When a new client is created.
+-- * **active**: When client gains the focus (or stop being urgent/floating
+-- but is active).
+-- * **inactive**: When client loses the focus (or stop being urgent/floating
+-- and is not active.
+-- * **urgent**: When a client becomes urgent.
+-- * **floating**: When the floating or maximization state changes.
+--
+-- @signal request::border
+-- @see awful.permissions.update_border
-- Add clients during startup to focus history.
--- This used to happen through ewmh.activate, but that only handles visible
+-- This used to happen through permissions.activate, but that only handles visible
-- clients now.
-capi.client.connect_signal("manage", function (c)
+capi.client.connect_signal("request::manage", function (c)
+ if capi.awesome.startup
+ and not c.size_hints.user_position
+ and not c.size_hints.program_position then
+ -- Prevent clients from being unreachable after screen count changes.
+ require("awful.placement").no_offscreen(c)
+ end
+
if awesome.startup then
client.focus.history.add(c)
end
-end)
-capi.client.connect_signal("unmanage", client.focus.history.delete)
-capi.client.connect_signal("unmanage", client.floating.delete)
+ client.property.set(c, "_border_init", true)
+ c:emit_signal("request::border", "added", {})
+end)
+capi.client.connect_signal("request::unmanage", client.focus.history.delete)
+
+capi.client.connect_signal("request::unmanage", client.floating.delete)
+
+-- Print a warning when the old `manage` signal is used.
+capi.client.connect_signal("manage::connected", function()
+ gdebug.deprecate(
+ "Use `request::manage` rather than `manage`",
+ {deprecated_in=5}
+ )
+end)
+capi.client.connect_signal("unmanage::connected", function()
+ gdebug.deprecate(
+ "Use `request::unmanage` rather than `unmanage`",
+ {deprecated_in=5}
+ )
+end)
-- Connect to "focus" signal, and allow to disable tracking.
do
diff --git a/lib/awful/client/focus.lua b/lib/awful/client/focus.lua
index a8b04f2ee..4ba75221a 100644
--- a/lib/awful/client/focus.lua
+++ b/lib/awful/client/focus.lua
@@ -60,6 +60,8 @@ end
-- @function awful.client.focus.byidx
-- @param i The index.
-- @client[opt] c The client.
+-- @request client activate client.focus.byidx granted When `awful.focus.byidx`
+-- is called.
function focus.byidx(i, c)
local target = client.next(i, c)
if target then
@@ -141,6 +143,8 @@ end
--- Focus the previous client in history.
-- @function awful.client.focus.history.previous
+-- @request client activate client.focus.history.previous granted When
+-- `awful.focus.history.previous` is called.
function focus.history.previous()
local sel = capi.client.focus
local s = sel and sel.screen or screen.focused()
@@ -158,6 +162,8 @@ end
-- @client[opt] c The client.
-- @tparam[opt=false] boolean stacked Use stacking order? (top to bottom)
-- @function awful.client.focus.bydirection
+-- @request client activate client.focus.bydirection granted When
+-- `awful.focus.bydirection` is called.
function focus.bydirection(dir, c, stacked)
local sel = c or capi.client.focus
if sel then
@@ -183,6 +189,8 @@ end
-- @client[opt] c The client.
-- @tparam[opt=false] boolean stacked Use stacking order? (top to bottom)
-- @function awful.client.focus.global_bydirection
+-- @request client activate client.focus.global_bydirection granted When
+-- `awful.client.focus.global_bydirection` is called.
function focus.global_bydirection(dir, c, stacked)
local sel = c or capi.client.focus
local scr = get_screen(sel and sel.screen or screen.focused())
diff --git a/lib/awful/client/urgent.lua b/lib/awful/client/urgent.lua
index 8cdcc2cba..dd1602d7a 100644
--- a/lib/awful/client/urgent.lua
+++ b/lib/awful/client/urgent.lua
@@ -62,10 +62,30 @@ end
-- @function awful.urgent.add
-- @client c The client object.
-- @param prop The property which is updated.
+-- @request client border active granted When a client becomes active and is no
+-- longer urgent.
+-- @request client border inactive granted When a client stop being active and
+-- is no longer urgent.
+-- @request client border urgent granted When a client stop becomes urgent.
function urgent.add(c, prop)
- if type(c) == "client" and prop == "urgent" and c.urgent then
+ assert(
+ c.urgent ~= nil,
+ "`awful.client.urgent.add()` takes a client as first parameter"
+ )
+
+ if prop == "urgent" and c.urgent then
table.insert(data, c)
end
+
+ if c.urgent then
+ c:emit_signal("request::border", "urgent", {})
+ else
+ c:emit_signal(
+ "request::border",
+ (c.active and "" or "in").."active",
+ {}
+ )
+ end
end
--- Remove client from urgent stack.
@@ -83,7 +103,7 @@ end
capi.client.connect_signal("property::urgent", urgent.add)
capi.client.connect_signal("focus", urgent.delete)
-capi.client.connect_signal("unmanage", urgent.delete)
+capi.client.connect_signal("request::unmanage", urgent.delete)
return urgent
diff --git a/lib/awful/ewmh.lua b/lib/awful/ewmh.lua
index 842a05508..5cd71a368 100644
--- a/lib/awful/ewmh.lua
+++ b/lib/awful/ewmh.lua
@@ -1,472 +1,9 @@
----------------------------------------------------------------------------
---- Implements EWMH requests handling.
---
--- @author Julien Danjou <julien@danjou.info>
--- @copyright 2009 Julien Danjou
--- @module awful.ewmh
----------------------------------------------------------------------------
+local gdebug = require("gears.debug")
-local client = client
-local screen = screen
-local ipairs = ipairs
-local timer = require("gears.timer")
-local gtable = require("gears.table")
-local aclient = require("awful.client")
-local aplace = require("awful.placement")
-local asuit = require("awful.layout.suit")
-local beautiful = require("beautiful")
-local alayout = require("awful.layout")
-local atag = require("awful.tag")
-
-local ewmh = {
- generic_activate_filters = {},
- contextual_activate_filters = {},
-}
-
---- Honor the screen padding when maximizing.
--- @beautiful beautiful.maximized_honor_padding
--- @tparam[opt=true] boolean maximized_honor_padding
-
---- Hide the border on fullscreen clients.
--- @beautiful beautiful.fullscreen_hide_border
--- @tparam[opt=true] boolean fullscreen_hide_border
-
---- Hide the border on maximized clients.
--- @beautiful beautiful.maximized_hide_border
--- @tparam[opt=false] boolean maximized_hide_border
-
---- The list of all registered generic request::activate (focus stealing)
--- filters. If a filter is added to only one context, it will be in
--- `ewmh.contextual_activate_filters`["context_name"].
--- @table[opt={}] generic_activate_filters
--- @see ewmh.activate
--- @see ewmh.add_activate_filter
--- @see ewmh.remove_activate_filter
-
---- The list of all registered contextual request::activate (focus stealing)
--- filters. If a filter is added to only one context, it will be in
--- `ewmh.generic_activate_filters`.
--- @table[opt={}] contextual_activate_filters
--- @see ewmh.activate
--- @see ewmh.add_activate_filter
--- @see ewmh.remove_activate_filter
-
---- Update a client's settings when its geometry changes, skipping signals
--- resulting from calls within.
-local repair_geometry_lock = false
-
-local function repair_geometry(window)
- if repair_geometry_lock then return end
- repair_geometry_lock = true
-
- -- Re-apply the geometry locking properties to what they should be.
- for _, prop in ipairs {
- "fullscreen", "maximized", "maximized_vertical", "maximized_horizontal"
- } do
- if window[prop] then
- window:emit_signal("request::geometry", prop, {
- store_geometry = false
- })
- break
- end
- end
-
- repair_geometry_lock = false
-end
-
---- Activate a window.
---
--- This sets the focus only if the client is visible.
---
--- It is the default signal handler for `request::activate` on a `client`.
---
--- @signalhandler awful.ewmh.activate
--- @client c A client to use
--- @tparam string context The context where this signal was used.
--- @tparam[opt] table hints A table with additional hints:
--- @tparam[opt=false] boolean hints.raise should the client be raised?
--- @tparam[opt=false] boolean hints.switch_to_tag should the client's first tag
--- be selected if none of the client's tags are selected?
--- @tparam[opt=false] boolean hints.switch_to_tags Select all tags associated
--- with the client.
-function ewmh.activate(c, context, hints) -- luacheck: no unused args
- hints = hints or {}
-
- if c.focusable == false and not hints.force then
- if hints.raise then
- c:raise()
- end
-
- return
- end
-
- local found, ret = false
-
- -- Execute the filters until something handle the request
- for _, tab in ipairs {
- ewmh.contextual_activate_filters[context] or {},
- ewmh.generic_activate_filters
- } do
- for i=#tab, 1, -1 do
- ret = tab[i](c, context, hints)
- if ret ~= nil then found=true; break end
- end
-
- if found then break end
- end
-
- -- Minimized clients can be requested to have focus by, for example, 3rd
- -- party toolbars and they might not try to unminimize it first.
- if ret ~= false and hints.raise then
- c.minimized = false
- end
-
- if ret ~= false and c:isvisible() then
- client.focus = c
- elseif ret == false and not hints.force then
- return
- end
-
- if hints.raise then
- c:raise()
- if not awesome.startup and not c:isvisible() then
- c.urgent = true
- end
- end
-
- -- The rules use `switchtotag`. For consistency and code re-use, support it,
- -- but keep undocumented. --TODO v5 remove switchtotag
- if hints.switchtotag or hints.switch_to_tag or hints.switch_to_tags then
- atag.viewmore(c:tags(), c.screen, (not hints.switch_to_tags) and 0 or nil)
- end
-end
-
---- Add an activate (focus stealing) filter function.
---
--- The callback takes the following parameters:
---
--- * **c** (*client*) The client requesting the activation
--- * **context** (*string*) The activation context.
--- * **hints** (*table*) Some additional hints (depending on the context)
---
--- If the callback returns `true`, the client will be activated. If the callback
--- returns `false`, the activation request is cancelled unless the `force` hint is
--- set. If the callback returns `nil`, the previous callback will be executed.
--- This will continue until either a callback handles the request or when it runs
--- out of callbacks. In that case, the request will be granted if the client is
--- visible.
---
--- For example, to block Firefox from stealing the focus, use:
---
--- awful.ewmh.add_activate_filter(function(c)
--- if c.class == "Firefox" then return false end
--- end, "ewmh")
---
--- @tparam function f The callback
--- @tparam[opt] string context The `request::activate` context
--- @see generic_activate_filters
--- @see contextual_activate_filters
--- @see remove_activate_filter
--- @staticfct awful.ewmh.add_activate_filter
-function ewmh.add_activate_filter(f, context)
- if not context then
- table.insert(ewmh.generic_activate_filters, f)
- else
- ewmh.contextual_activate_filters[context] =
- ewmh.contextual_activate_filters[context] or {}
-
- table.insert(ewmh.contextual_activate_filters[context], f)
- end
-end
-
---- Remove an activate (focus stealing) filter function.
--- This is an helper to avoid dealing with `ewmh.add_activate_filter` directly.
--- @tparam function f The callback
--- @tparam[opt] string context The `request::activate` context
--- @treturn boolean If the callback existed
--- @see generic_activate_filters
--- @see contextual_activate_filters
--- @see add_activate_filter
--- @staticfct awful.ewmh.remove_activate_filter
-function ewmh.remove_activate_filter(f, context)
- local tab = context and (ewmh.contextual_activate_filters[context] or {})
- or ewmh.generic_activate_filters
-
- for k, v in ipairs(tab) do
- if v == f then
- table.remove(tab, k)
-
- -- In case the callback is there multiple time.
- ewmh.remove_activate_filter(f, context)
-
- return true
- end
- end
-
- return false
-end
-
--- Get tags that are on the same screen as the client. This should _almost_
--- always return the same content as c:tags().
-local function get_valid_tags(c, s)
- local tags, new_tags = c:tags(), {}
-
- for _, t in ipairs(tags) do
- if s == t.screen then
- table.insert(new_tags, t)
- end
- end
-
- return new_tags
-end
-
---- Tag a window with its requested tag.
---
--- It is the default signal handler for `request::tag` on a `client`.
---
--- @signalhandler awful.ewmh.tag
--- @client c A client to tag
--- @tparam[opt] tag|boolean t A tag to use. If true, then the client is made sticky.
--- @tparam[opt={}] table hints Extra information
-function ewmh.tag(c, t, hints) --luacheck: no unused
- -- There is nothing to do
- if not t and #get_valid_tags(c, c.screen) > 0 then return end
-
- if not t then
- if c.transient_for and not (hints and hints.reason == "screen") then
- c.screen = c.transient_for.screen
- if not c.sticky then
- local tags = c.transient_for:tags()
- c:tags(#tags > 0 and tags or c.transient_for.screen.selected_tags)
- end
- else
- c:to_selected_tags()
- end
- elseif type(t) == "boolean" and t then
- c.sticky = true
- else
- c.screen = t.screen
- c:tags({ t })
- end
-end
-
---- Handle client urgent request
--- @signalhandler awful.ewmh.urgent
--- @client c A client
--- @tparam boolean urgent If the client should be urgent
-function ewmh.urgent(c, urgent)
- if c ~= client.focus and not aclient.property.get(c,"ignore_urgent") then
- c.urgent = urgent
- end
-end
-
--- Map the state to the action name
-local context_mapper = {
- maximized_vertical = "maximize_vertically",
- maximized_horizontal = "maximize_horizontally",
- maximized = "maximize",
- fullscreen = "maximize"
-}
-
---- Move and resize the client.
---
--- This is the default geometry request handler.
---
--- @signalhandler awful.ewmh.geometry
--- @tparam client c The client
--- @tparam string context The context
--- @tparam[opt={}] table hints The hints to pass to the handler
-function ewmh.geometry(c, context, hints)
- local layout = c.screen.selected_tag and c.screen.selected_tag.layout or nil
-
- -- Setting the geometry will not work unless the client is floating.
- if (not c.floating) and (not layout == asuit.floating) then
- return
- end
-
- context = context or ""
-
- local original_context = context
-
- -- Now, map it to something useful
- context = context_mapper[context] or context
-
- local props = gtable.clone(hints or {}, false)
- props.store_geometry = props.store_geometry==nil and true or props.store_geometry
-
- -- If it is a known placement function, then apply it, otherwise let
- -- other potential handler resize the client (like in-layout resize or
- -- floating client resize)
- if aplace[context] then
-
- -- Check if it corresponds to a boolean property.
- local state = c[original_context]
-
- -- If the property is boolean and it corresponds to the undo operation,
- -- restore the stored geometry.
- if state == false then
- local original = repair_geometry_lock
- repair_geometry_lock = true
- aplace.restore(c, {context=context})
- repair_geometry_lock = original
- return
- end
-
- local honor_default = original_context ~= "fullscreen"
-
- if props.honor_workarea == nil then
- props.honor_workarea = honor_default
- end
-
- if props.honor_padding == nil and props.honor_workarea and context:match("maximize") then
- props.honor_padding = beautiful.maximized_honor_padding ~= false
- end
-
- if (original_context == "fullscreen" and beautiful.fullscreen_hide_border ~= false) or
- (original_context == "maximized" and beautiful.maximized_hide_border == true) then
- props.ignore_border_width = true
- props.zap_border_width = true
- end
-
- local original = repair_geometry_lock
- repair_geometry_lock = true
- aplace[context](c, props)
- repair_geometry_lock = original
- end
-end
-
---- Merge the 2 requests sent by clients wanting to be maximized.
---
--- The X clients set 2 flags (atoms) when they want to be maximized. This caused
--- 2 request::geometry to be sent. This code gives some time for them to arrive
--- and send a new `request::geometry` (through the property change) with the
--- combined state.
---
--- @signalhandler awful.ewmh.merge_maximization
--- @tparam client c The client
--- @tparam string context The context
--- @tparam[opt={}] table hints The hints to pass to the handler
-function ewmh.merge_maximization(c, context, hints)
- if context ~= "client_maximize_horizontal" and context ~= "client_maximize_vertical" then
- return
- end
-
- if not c._delay_maximization then
- c._delay_maximization = function()
- -- Computes the actual X11 atoms before/after
- local before_max_h = c.maximized or c.maximized_horizontal
- local before_max_v = c.maximized or c.maximized_vertical
- local after_max_h, after_max_v
- if c._delayed_max_h ~= nil then
- after_max_h = c._delayed_max_h
- else
- after_max_h = before_max_h
- end
- if c._delayed_max_v ~= nil then
- after_max_v = c._delayed_max_v
- else
- after_max_v = before_max_v
- end
- -- Interprets the client's intention based on the client's view
- if after_max_h and after_max_v then
- c.maximized = true
- elseif before_max_h and before_max_v then
- -- At this point, c.maximized must be true, and the client is
- -- trying to unmaximize the window, and potentially partial
- -- maximized the window
- c.maximized = false
- if after_max_h ~= after_max_v then
- c.maximized_horizontal = after_max_h
- c.maximized_vertical = after_max_v
- end
- else
- -- At this point, c.maximized must be false, and the client is
- -- not trying to fully maximize the window
- c.maximized_horizontal = after_max_h
- c.maximized_vertical = after_max_v
- end
- end
-
- timer {
- timeout = 1/60,
- autostart = true,
- single_shot = true,
- callback = function()
- if not c.valid then return end
-
- c._delay_maximization(c)
- c._delay_maximization = nil
- c._delayed_max_h = nil
- c._delayed_max_v = nil
- end
- }
- end
-
- local function get_value(suffix, long_suffix)
- if hints.toggle and c["_delayed_max_"..suffix] ~= nil then
- return not c["_delayed_max_"..suffix]
- elseif hints.toggle then
- return not (c["maximized"] or c["maximized_"..long_suffix])
- else
- return hints.status
- end
- end
-
- if context == "client_maximize_horizontal" then
- c._delayed_max_h = get_value("h", "horizontal")
- elseif context == "client_maximize_vertical" then
- c._delayed_max_v = get_value("v", "vertical")
- end
-end
-
---- Allow the client to move itself.
---
--- This is the default geometry request handler when the context is `ewmh`.
---
--- @signalhandler awful.ewmh.client_geometry_requests
--- @tparam client c The client
--- @tparam string context The context
--- @tparam[opt={}] table hints The hints to pass to the handler
-function ewmh.client_geometry_requests(c, context, hints)
- if context == "ewmh" and hints then
- if c.immobilized_horizontal then
- hints = gtable.clone(hints)
- hints.x = nil
- hints.width = nil
- end
- if c.immobilized_vertical then
- hints = gtable.clone(hints)
- hints.y = nil
- hints.height = nil
- end
- c:geometry(hints)
- end
-end
-
--- The magnifier layout doesn't work with focus follow mouse.
-ewmh.add_activate_filter(function(c)
- if alayout.get(c.screen) ~= alayout.suit.magnifier
- and aclient.focus.filter(c) then
- return nil
- else
- return false
- end
-end, "mouse_enter")
-
-client.connect_signal("request::activate", ewmh.activate)
-client.connect_signal("request::tag", ewmh.tag)
-client.connect_signal("request::urgent", ewmh.urgent)
-client.connect_signal("request::geometry", ewmh.geometry)
-client.connect_signal("request::geometry", ewmh.merge_maximization)
-client.connect_signal("request::geometry", ewmh.client_geometry_requests)
-client.connect_signal("property::border_width", repair_geometry)
-client.connect_signal("property::screen", repair_geometry)
-screen.connect_signal("property::workarea", function(s)
- for _, c in pairs(client.get(s)) do
- repair_geometry(c)
- end
-end)
-
-return ewmh
-
--- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
+-- It diverged over time to the point where it had nothing to do with EWMH.
+return gdebug.deprecate_class(
+ require("awful.permissions"),
+ "awful.ewmh",
+ "awful.permissions",
+ { deprecated_in = 5}
+)
diff --git a/lib/awful/init.lua b/lib/awful/init.lua
index 992590324..cff95db16 100644
--- a/lib/awful/init.lua
+++ b/lib/awful/init.lua
@@ -8,8 +8,11 @@
require("awful._compat")
-return
-{
+local deprecated = {
+ ewmh = true
+}
+
+local ret = {
client = require("awful.client");
completion = require("awful.completion");
layout = require("awful.layout");
@@ -30,11 +33,21 @@ return
wibox = require("awful.wibox");
startup_notification = require("awful.startup_notification");
tooltip = require("awful.tooltip");
- ewmh = require("awful.ewmh");
+ permissions = require("awful.permissions");
titlebar = require("awful.titlebar");
rules = require("awful.rules");
popup = require("awful.popup");
spawn = require("awful.spawn");
}
+-- Lazy load deprecated modules to reduce the numbers of loop dependencies.
+return setmetatable(ret,{
+ __index = function(_, key)
+ if deprecated[key] then
+ rawset(ret, key, require("awful."..key))
+ end
+ return rawget(ret, key)
+ end
+})
+
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
diff --git a/lib/awful/layout/init.lua b/lib/awful/layout/init.lua
index e9aeb0097..fb5e9fd5c 100644
--- a/lib/awful/layout/init.lua
+++ b/lib/awful/layout/init.lua
@@ -31,26 +31,17 @@ end
local layout = {}
-layout.suit = require("awful.layout.suit")
+-- Support `table.insert()` to avoid breaking old code.
+local default_layouts = setmetatable({}, {
+ __newindex = function(self, key, value)
+ assert(key <= #self+1 and key > 0)
-layout.layouts = {
- layout.suit.floating,
- layout.suit.tile,
- layout.suit.tile.left,
- layout.suit.tile.bottom,
- layout.suit.tile.top,
- layout.suit.fair,
- layout.suit.fair.horizontal,
- layout.suit.spiral,
- layout.suit.spiral.dwindle,
- layout.suit.max,
- layout.suit.max.fullscreen,
- layout.suit.magnifier,
- layout.suit.corner.nw,
- layout.suit.corner.ne,
- layout.suit.corner.sw,
- layout.suit.corner.se,
-}
+ layout.append_default_layout(value)
+ end
+})
+
+
+layout.suit = require("awful.layout.suit")
--- The default list of layouts.
--
@@ -256,6 +247,53 @@ function layout.arrange(screen)
end)
end
+--- Append a layout to the list of default tag layouts.
+--
+-- @staticfct awful.layout.append_default_layout
+-- @tparam layout to_add A valid tag layout.
+-- @see awful.layout.layouts
+function layout.append_default_layout(to_add)
+ rawset(default_layouts, #default_layouts+1, to_add)
+ capi.tag.emit_signal("property::layouts")
+end
+
+--- Remove a layout from the list of default layouts.
+--
+-- @DOC_text_awful_layout_remove_EXAMPLE@
+--
+-- @staticfct awful.layout.remove_default_layout
+-- @tparam layout to_remove A valid tag layout.
+-- @treturn boolean True if the layout was found and removed.
+-- @see awful.layout.layouts
+function layout.remove_default_layout(to_remove)
+ local ret, found = false, true
+
+ -- Remove all instances, just in case.
+ while found do
+ found = false
+ for k, l in ipairs(default_layouts) do
+ if l == to_remove then
+ table.remove(default_layouts, k)
+ ret, found = true, true
+ break
+ end
+ end
+ end
+
+ return ret
+end
+
+--- Append many layouts to the list of default tag layouts.
+--
+-- @staticfct awful.layout.append_default_layouts
+-- @tparam table layouts A table of valid tag layout.
+-- @see awful.layout.layouts
+function layout.append_default_layouts(layouts)
+ for _, l in ipairs(layouts) do
+ rawset(default_layouts, #default_layouts+1, l)
+ end
+end
+
--- Get the current layout name.
-- @param _layout The layout.
-- @return The layout name.
@@ -365,6 +403,87 @@ capi.screen.connect_signal("property::geometry", function(s, old_geom)
end
end)
-return layout
+local init_layouts
+init_layouts = function()
+ capi.tag.emit_signal("request::default_layouts", "startup")
+ capi.tag.disconnect_signal("new", init_layouts)
+
+ -- Fallback.
+ if #default_layouts == 0 then
+ layout.append_default_layouts({
+ layout.suit.floating,
+ layout.suit.tile,
+ layout.suit.tile.left,
+ layout.suit.tile.bottom,
+ layout.suit.tile.top,
+ layout.suit.fair,
+ layout.suit.fair.horizontal,
+ layout.suit.spiral,
+ layout.suit.spiral.dwindle,
+ layout.suit.max,
+ layout.suit.max.fullscreen,
+ layout.suit.magnifier,
+ layout.suit.corner.nw,
+ layout.suit.corner.ne,
+ layout.suit.corner.sw,
+ layout.suit.corner.se,
+ })
+ end
+
+ init_layouts = nil
+end
+
+-- "new" is emited before "activate", do it should be the very last opportunity
+-- generate the list of default layout. With dynamic tag, this can happen later
+-- than the first event loop iteration.
+capi.tag.connect_signal("new", init_layouts)
+
+-- Intercept the calls to `layouts` to both lazyload then and emit the proper
+-- signals.
+local mt = {
+ __index = function(_, key)
+ if key == "layouts" then
+ -- Lazy initialization to *at least* attempt to give modules a
+ -- chance to load before calling `request::default_layouts`. Note
+ -- that the old `rc.lua` called `awful.layout.layouts` in the global
+ -- context. If there was some module `require()` later in the code,
+ -- they will not get the signal.
+ if init_layouts then
+ init_layouts()
+ end
+
+ return default_layouts
+ end
+ end,
+ __newindex = function(_, key, value)
+ if key == "layouts" then
+ assert(type(value) == "table", "`awful.layout.layouts` needs a table.")
+
+ -- Do not ask for layouts if they were already provided.
+ if init_layouts then
+ gdebug.print_warning(
+ "`awful.layout.layouts` was set before `request::default_layouts` could "..
+ "be called. Please use `awful.layout.append_default_layouts` to "..
+ " avoid this problem"
+ )
+
+ capi.tag.disconnect_signal("new", init_layouts)
+ init_layouts = nil
+ elseif #default_layouts > 0 then
+ gdebug.print_warning(
+ "`awful.layout.layouts` was set after `request::default_layouts` was "..
+ "used to get the layouts. This is probably an accident. Use "..
+ "`awful.layout.remove_default_layout` to get rid of this warning."
+ )
+ end
+
+ default_layouts = value
+ else
+ rawset(layout, key, value)
+ end
+ end
+}
+
+return setmetatable(layout, mt)
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
diff --git a/lib/awful/menu.lua b/lib/awful/menu.lua
index b17964a4d..9cfe6b10c 100644
--- a/lib/awful/menu.lua
+++ b/lib/awful/menu.lua
@@ -119,8 +119,8 @@ local function load_theme(a, b)
local fallback = beautiful.get()
if a.reset then b = fallback end
if a == "reset" then a = fallback end
- ret.border = a.border_color or b.menu_border_color or b.border_normal or
- fallback.menu_border_color or fallback.border_normal
+ ret.border = a.border_color or b.menu_border_color or b.border_color_normal or
+ fallback.menu_border_color or fallback.border_color_normal
ret.border_width= a.border_width or b.menu_border_width or b.border_width or
fallback.menu_border_width or fallback.border_width or dpi(0)
ret.fg_focus = a.fg_focus or b.menu_fg_focus or b.fg_focus or
@@ -521,6 +521,8 @@ end
-- included in the menu.
-- @return The menu.
-- @constructorfct awful.menu.clients
+-- @request client activate menu.clients granted When clicking on a clients menu
+-- element.
function menu.clients(args, item_args, filter)
local cls_t = {}
for c in client_iterate(filter or function() return true end) do
diff --git a/lib/awful/mouse/client.lua b/lib/awful/mouse/client.lua
index 0b1536d98..d6191b723 100644
--- a/lib/awful/mouse/client.lua
+++ b/lib/awful/mouse/client.lua
@@ -15,7 +15,9 @@ local module = {}
-- @staticfct awful.mouse.client.move
-- @param c The client to move, or the focused one if nil.
-- @param snap The pixel to snap clients.
--- @param finished_cb Deprecated, do not use
+-- @param finished_cb Deprecated, do not use.
+-- @request client geometry mouse.move granted When `awful.mouse.client.move` is
+-- called.
function module.move(c, snap, finished_cb) --luacheck: no unused args
if finished_cb then
gdebug.deprecate("The mouse.client.move `finished_cb` argument is no longer"..
@@ -99,6 +101,8 @@ end
-- @tparam string corner The corner to grab on resize. Auto detected by default.
-- @tparam[opt={}] table args A set of `awful.placement` arguments
-- @treturn string The corner (or side) name
+-- @request client geometry mouse.resize granted When `awful.mouse.client.resize`
+-- is called.
function module.resize(c, corner, args)
c = c or capi.client.focus
diff --git a/lib/awful/mouse/resize.lua b/lib/awful/mouse/resize.lua
index 16542e4e1..98981d33f 100644
--- a/lib/awful/mouse/resize.lua
+++ b/lib/awful/mouse/resize.lua
@@ -103,7 +103,6 @@ end
-- @tparam client client A client.
-- @tparam[default=mouse.resize] string context The resizing context.
-- @tparam[opt={}] table args A set of `awful.placement` arguments.
-
local function handler(_, client, context, args) --luacheck: no unused_args
args = args or {}
context = context or "mouse.resize"
diff --git a/lib/awful/permissions/_common.lua b/lib/awful/permissions/_common.lua
new file mode 100644
index 000000000..368c95da6
--- /dev/null
+++ b/lib/awful/permissions/_common.lua
@@ -0,0 +1,82 @@
+-- Common code for the permission framework.
+-- It is in its own file to avoid circular dependencies.
+--
+-- It is **NOT** a public API.
+
+local module = {}
+
+-- The default permissions for all requests.
+-- If something is not in this list, then it is assumed it should be granted.
+local default_permissions = {
+ client = {
+ autoactivate = {
+ -- To preserve the default from AwesomeWM 3.3-4.3, do not steal
+ -- focus by default for these contexts:
+ mouse_enter = false,
+ switch_tag = false,
+ history = false,
+ }
+ }
+}
+
+function module.check(object, class, request, context)
+ if not default_permissions[class] then return true end
+ if not default_permissions[class][request] then return true end
+ if default_permissions[class][request][context] == nil then return true end
+
+ local ret = nil
+
+ if object._private.permissions and object._private.permissions[request] then
+ ret = object._private.permissions[request][context]
+ end
+
+ if ret ~= nil then return ret end
+
+ return default_permissions[class][request][context]
+end
+
+function module.set(class, request, context, granted)
+ assert(type(granted) == "boolean")
+
+ if not default_permissions[class] then
+ default_permissions[class] = {}
+ end
+
+ if not default_permissions[class][request] then
+ default_permissions[class][request] = {}
+ end
+
+ default_permissions[class][request][context] = granted
+end
+
+-- Awesome 3.3-4.3 had an `awful.autofocus` module. That module had no APIs, but
+-- was simply "enabled" when you `require()`d it for the first time. This was
+-- non-standard and was the only module in `awful` to only do things when
+-- explicitly `require()`d.
+--
+-- It is now a dummy module which will set the property to `true`.
+function module._deprecated_autofocus_in_use()
+ module.set("client", "autoactivate", "mouse_enter", true)
+ module.set("client", "autoactivate", "switch_tag" , true)
+ module.set("client", "autoactivate", "history" , true)
+end
+
+local function set_object_permission_common(self, request, context, v)
+ self._private.permissions = self._private.permissions or {}
+ if not self._private.permissions[request] then
+ self._private.permissions[request] = {}
+ end
+ self._private.permissions[request][context] = v
+end
+
+-- Add the grant and deny methods to the objects.
+function module.setup_grant(class, classname) -- luacheck: no unused
+ function class.grant(self, request, context)
+ set_object_permission_common(self, request, context, true)
+ end
+ function class.deny(self, request, context)
+ set_object_permission_common(self, request, context, false)
+ end
+end
+
+return module
diff --git a/lib/awful/permissions/init.lua b/lib/awful/permissions/init.lua
new file mode 100644
index 000000000..cf4fcab65
--- /dev/null
+++ b/lib/awful/permissions/init.lua
@@ -0,0 +1,732 @@
+---------------------------------------------------------------------------
+--- Implements EWMH requests handling.
+--
+-- @author Julien Danjou <julien@danjou.info>
+-- @copyright 2009 Julien Danjou
+-- @module awful.permissions
+---------------------------------------------------------------------------
+
+local client = client
+local screen = screen
+local ipairs = ipairs
+local timer = require("gears.timer")
+local gtable = require("gears.table")
+local aclient = require("awful.client")
+local aplace = require("awful.placement")
+local asuit = require("awful.layout.suit")
+local beautiful = require("beautiful")
+local alayout = require("awful.layout")
+local atag = require("awful.tag")
+local gdebug = require("gears.debug")
+local pcommon = require("awful.permissions._common")
+
+local permissions = {
+ generic_activate_filters = {},
+ contextual_activate_filters = {},
+}
+
+--- Honor the screen padding when maximizing.
+-- @beautiful beautiful.maximized_honor_padding
+-- @tparam[opt=true] boolean maximized_honor_padding
+
+--- Hide the border on fullscreen clients.
+-- @beautiful beautiful.fullscreen_hide_border
+-- @tparam[opt=true] boolean fullscreen_hide_border
+
+--- Hide the border on maximized clients.
+-- @beautiful beautiful.maximized_hide_border
+-- @tparam[opt=false] boolean maximized_hide_border
+
+--- The list of all registered generic request::activate (focus stealing)
+-- filters. If a filter is added to only one context, it will be in
+-- `permissions.contextual_activate_filters`["context_name"].
+-- @table[opt={}] generic_activate_filters
+-- @see permissions.activate
+-- @see permissions.add_activate_filter
+-- @see permissions.remove_activate_filter
+
+--- The list of all registered contextual request::activate (focus stealing)
+-- filters. If a filter is added to only one context, it will be in
+-- `permissions.generic_activate_filters`.
+-- @table[opt={}] contextual_activate_filters
+-- @see permissions.activate
+-- @see permissions.add_activate_filter
+-- @see permissions.remove_activate_filter
+
+--- Update a client's settings when its geometry changes, skipping signals
+-- resulting from calls within.
+local repair_geometry_lock = false
+
+local function repair_geometry(window)
+ if repair_geometry_lock then return end
+ repair_geometry_lock = true
+
+ -- Re-apply the geometry locking properties to what they should be.
+ for _, prop in ipairs {
+ "fullscreen", "maximized", "maximized_vertical", "maximized_horizontal"
+ } do
+ if window[prop] then
+ window:emit_signal("request::geometry", prop, {
+ store_geometry = false
+ })
+ break
+ end
+ end
+
+ repair_geometry_lock = false
+end
+
+local function filter_sticky(c)
+ return not c.sticky and aclient.focus.filter(c)
+end
+
+--- Give focus when clients appear/disappear.
+--
+-- @param obj An object that should have a .screen property.
+local function check_focus(obj)
+ if (not obj.screen) or not obj.screen.valid then return end
+ -- When no visible client has the focus...
+ if not client.focus or not client.focus:isvisible() then
+ local c = aclient.focus.history.get(screen[obj.screen], 0, filter_sticky)
+ if not c then
+ c = aclient.focus.history.get(screen[obj.screen], 0, aclient.focus.filter)
+ end
+ if c then
+ c:emit_signal(
+ "request::autoactivate",
+ "history",
+ {raise=false}
+ )
+ end
+ end
+end
+
+--- Check client focus (delayed).
+-- @param obj An object that should have a .screen property.
+local function check_focus_delayed(obj)
+ timer.delayed_call(check_focus, {screen = obj.screen})
+end
+
+--- Give focus on tag selection change.
+--
+-- @tparam tag t A tag object
+local function check_focus_tag(t)
+ local s = t.screen
+ if (not s) or (not s.valid) then return end
+ s = screen[s]
+ check_focus({ screen = s })
+ if client.focus and screen[client.focus.screen] ~= s then
+ local c = aclient.focus.history.get(s, 0, filter_sticky)
+ if not c then
+ c = aclient.focus.history.get(s, 0, aclient.focus.filter)
+ end
+ if c then
+ c:emit_signal(
+ "request::autoactivate",
+ "switch_tag",
+ {raise=false}
+ )
+ end
+ end
+end
+
+--- Activate a window.
+--
+-- This sets the focus only if the client is visible.
+--
+-- It is the default signal handler for `request::activate` on a `client`.
+--
+-- @signalhandler awful.permissions.activate
+-- @client c A client to use
+-- @tparam string context The context where this signal was used.
+-- @tparam[opt] table hints A table with additional hints:
+-- @tparam[opt=false] boolean hints.raise should the client be raised?
+-- @tparam[opt=false] boolean hints.switch_to_tag should the client's first tag
+-- be selected if none of the client's tags are selected?
+-- @tparam[opt=false] boolean hints.switch_to_tags Select all tags associated
+-- with the client.
+function permissions.activate(c, context, hints) -- luacheck: no unused args
+ if not pcommon.check(c, "client", "activate", context) then return end
+
+ hints = hints or {}
+
+ if c.focusable == false and not hints.force then
+ if hints.raise then
+ c:raise()
+ end
+
+ return
+ end
+
+ local found, ret = false
+
+ -- Execute the filters until something handle the request
+ for _, tab in ipairs {
+ permissions.contextual_activate_filters[context] or {},
+ permissions.generic_activate_filters
+ } do
+ for i=#tab, 1, -1 do
+ ret = tab[i](c, context, hints)
+ if ret ~= nil then found=true; break end
+ end
+
+ if found then break end
+ end
+
+ -- Minimized clients can be requested to have focus by, for example, 3rd
+ -- party toolbars and they might not try to unminimize it first.
+ if ret ~= false and hints.raise then
+ c.minimized = false
+ end
+
+ if ret ~= false and c:isvisible() then
+ client.focus = c
+ elseif ret == false and not hints.force then
+ return
+ end
+
+ if hints.raise then
+ c:raise()
+ if not awesome.startup and not c:isvisible() then
+ c.urgent = true
+ end
+ end
+
+ -- The rules use `switchtotag`. For consistency and code re-use, support it,
+ -- but keep undocumented. --TODO v5 remove switchtotag
+ if hints.switchtotag or hints.switch_to_tag or hints.switch_to_tags then
+ atag.viewmore(c:tags(), c.screen, (not hints.switch_to_tags) and 0 or nil)
+ end
+end
+
+--- Add an activate (focus stealing) filter function.
+--
+-- The callback takes the following parameters:
+--
+-- * **c** (*client*) The client requesting the activation
+-- * **context** (*string*) The activation context.
+-- * **hints** (*table*) Some additional hints (depending on the context)
+--
+-- If the callback returns `true`, the client will be activated. If the callback
+-- returns `false`, the activation request is cancelled unless the `force` hint is
+-- set. If the callback returns `nil`, the previous callback will be executed.
+-- This will continue until either a callback handles the request or when it runs
+-- out of callbacks. In that case, the request will be granted if the client is
+-- visible.
+--
+-- For example, to block Firefox from stealing the focus, use:
+--
+-- awful.permissions.add_activate_filter(function(c)
+-- if c.class == "Firefox" then return false end
+-- end, "permissions")
+--
+-- @tparam function f The callback
+-- @tparam[opt] string context The `request::activate` context
+-- @see generic_activate_filters
+-- @see contextual_activate_filters
+-- @see remove_activate_filter
+-- @staticfct awful.permissions.add_activate_filter
+function permissions.add_activate_filter(f, context)
+ if not context then
+ table.insert(permissions.generic_activate_filters, f)
+ else
+ permissions.contextual_activate_filters[context] =
+ permissions.contextual_activate_filters[context] or {}
+
+ table.insert(permissions.contextual_activate_filters[context], f)
+ end
+end
+
+--- Remove an activate (focus stealing) filter function.
+-- This is an helper to avoid dealing with `permissions.add_activate_filter` directly.
+-- @tparam function f The callback
+-- @tparam[opt] string context The `request::activate` context
+-- @treturn boolean If the callback existed
+-- @see generic_activate_filters
+-- @see contextual_activate_filters
+-- @see add_activate_filter
+-- @staticfct awful.permissions.remove_activate_filter
+function permissions.remove_activate_filter(f, context)
+ local tab = context and (permissions.contextual_activate_filters[context] or {})
+ or permissions.generic_activate_filters
+
+ for k, v in ipairs(tab) do
+ if v == f then
+ table.remove(tab, k)
+
+ -- In case the callback is there multiple time.
+ permissions.remove_activate_filter(f, context)
+
+ return true
+ end
+ end
+
+ return false
+end
+
+-- Get tags that are on the same screen as the client. This should _almost_
+-- always return the same content as c:tags().
+local function get_valid_tags(c, s)
+ local tags, new_tags = c:tags(), {}
+
+ for _, t in ipairs(tags) do
+ if s == t.screen then
+ table.insert(new_tags, t)
+ end
+ end
+
+ return new_tags
+end
+
+--- Tag a window with its requested tag.
+--
+-- It is the default signal handler for `request::tag` on a `client`.
+--
+-- @signalhandler awful.permissions.tag
+-- @client c A client to tag
+-- @tparam[opt] tag|boolean t A tag to use. If true, then the client is made sticky.
+-- @tparam[opt={}] table hints Extra information
+function permissions.tag(c, t, hints) --luacheck: no unused
+ -- There is nothing to do
+ if not t and #get_valid_tags(c, c.screen) > 0 then return end
+
+ if not t then
+ if c.transient_for and not (hints and hints.reason == "screen") then
+ c.screen = c.transient_for.screen
+ if not c.sticky then
+ local tags = c.transient_for:tags()
+ c:tags(#tags > 0 and tags or c.transient_for.screen.selected_tags)
+ end
+ else
+ c:to_selected_tags()
+ end
+ elseif type(t) == "boolean" and t then
+ c.sticky = true
+ else
+ c.screen = t.screen
+ c:tags({ t })
+ end
+end
+
+--- Handle client urgent request
+-- @signalhandler awful.permissions.urgent
+-- @client c A client
+-- @tparam boolean urgent If the client should be urgent
+function permissions.urgent(c, urgent)
+ if c ~= client.focus and not aclient.property.get(c,"ignore_urgent") then
+ c.urgent = urgent
+ end
+end
+
+-- Map the state to the action name
+local context_mapper = {
+ maximized_vertical = "maximize_vertically",
+ maximized_horizontal = "maximize_horizontally",
+ maximized = "maximize",
+ fullscreen = "maximize"
+}
+
+--- Move and resize the client.
+--
+-- This is the default geometry request handler.
+--
+-- @signalhandler awful.permissions.geometry
+-- @tparam client c The client
+-- @tparam string context The context
+-- @tparam[opt={}] table hints The hints to pass to the handler
+function permissions.geometry(c, context, hints)
+ if not pcommon.check(c, "client", "geometry", context) then return end
+
+ local layout = c.screen.selected_tag and c.screen.selected_tag.layout or nil
+
+ -- Setting the geometry will not work unless the client is floating.
+ if (not c.floating) and (not layout == asuit.floating) then
+ return
+ end
+
+ context = context or ""
+
+ local original_context = context
+
+ -- Now, map it to something useful
+ context = context_mapper[context] or context
+
+ local props = gtable.clone(hints or {}, false)
+ props.store_geometry = props.store_geometry==nil and true or props.store_geometry
+
+ -- If it is a known placement function, then apply it, otherwise let
+ -- other potential handler resize the client (like in-layout resize or
+ -- floating client resize)
+ if aplace[context] then
+
+ -- Check if it corresponds to a boolean property.
+ local state = c[original_context]
+
+ -- If the property is boolean and it corresponds to the undo operation,
+ -- restore the stored geometry.
+ if state == false then
+ local original = repair_geometry_lock
+ repair_geometry_lock = true
+ aplace.restore(c, {context=context})
+ repair_geometry_lock = original
+ return
+ end
+
+ local honor_default = original_context ~= "fullscreen"
+
+ if props.honor_workarea == nil then
+ props.honor_workarea = honor_default
+ end
+
+ if props.honor_padding == nil and props.honor_workarea and context:match("maximize") then
+ props.honor_padding = beautiful.maximized_honor_padding ~= false
+ end
+
+ if (original_context == "fullscreen" and beautiful.fullscreen_hide_border ~= false) or
+ (original_context == "maximized" and beautiful.maximized_hide_border == true) then
+ props.ignore_border_width = true
+ props.zap_border_width = true
+ end
+
+ local original = repair_geometry_lock
+ repair_geometry_lock = true
+ aplace[context](c, props)
+ repair_geometry_lock = original
+ end
+end
+
+--- Merge the 2 requests sent by clients wanting to be maximized.
+--
+-- The X clients set 2 flags (atoms) when they want to be maximized. This caused
+-- 2 request::geometry to be sent. This code gives some time for them to arrive
+-- and send a new `request::geometry` (through the property change) with the
+-- combined state.
+--
+-- @signalhandler awful.permissions.merge_maximization
+-- @tparam client c The client
+-- @tparam string context The context
+-- @tparam[opt={}] table hints The hints to pass to the handler
+function permissions.merge_maximization(c, context, hints)
+ if not pcommon.check(c, "client", "geometry", context) then return end
+
+ if context ~= "client_maximize_horizontal" and context ~= "client_maximize_vertical" then
+ return
+ end
+
+ if not c._delay_maximization then
+ c._delay_maximization = function()
+ -- Computes the actual X11 atoms before/after
+ local before_max_h = c.maximized or c.maximized_horizontal
+ local before_max_v = c.maximized or c.maximized_vertical
+ local after_max_h, after_max_v
+ if c._delayed_max_h ~= nil then
+ after_max_h = c._delayed_max_h
+ else
+ after_max_h = before_max_h
+ end
+ if c._delayed_max_v ~= nil then
+ after_max_v = c._delayed_max_v
+ else
+ after_max_v = before_max_v
+ end
+ -- Interprets the client's intention based on the client's view
+ if after_max_h and after_max_v then
+ c.maximized = true
+ elseif before_max_h and before_max_v then
+ -- At this point, c.maximized must be true, and the client is
+ -- trying to unmaximize the window, and potentially partial
+ -- maximized the window
+ c.maximized = false
+ if after_max_h ~= after_max_v then
+ c.maximized_horizontal = after_max_h
+ c.maximized_vertical = after_max_v
+ end
+ else
+ -- At this point, c.maximized must be false, and the client is
+ -- not trying to fully maximize the window
+ c.maximized_horizontal = after_max_h
+ c.maximized_vertical = after_max_v
+ end
+ end
+
+ timer {
+ timeout = 1/60,
+ autostart = true,
+ single_shot = true,
+ callback = function()
+ if not c.valid then return end
+
+ c._delay_maximization(c)
+ c._delay_maximization = nil
+ c._delayed_max_h = nil
+ c._delayed_max_v = nil
+ end
+ }
+ end
+
+ local function get_value(suffix, long_suffix)
+ if hints.toggle and c["_delayed_max_"..suffix] ~= nil then
+ return not c["_delayed_max_"..suffix]
+ elseif hints.toggle then
+ return not (c["maximized"] or c["maximized_"..long_suffix])
+ else
+ return hints.status
+ end
+ end
+
+ if context == "client_maximize_horizontal" then
+ c._delayed_max_h = get_value("h", "horizontal")
+ elseif context == "client_maximize_vertical" then
+ c._delayed_max_v = get_value("v", "vertical")
+ end
+end
+
+--- Allow the client to move itself.
+--
+-- This is the default geometry request handler when the context is `permissions`.
+--
+-- @signalhandler awful.permissions.client_geometry_requests
+-- @tparam client c The client
+-- @tparam string context The context
+-- @tparam[opt={}] table hints The hints to pass to the handler
+function permissions.client_geometry_requests(c, context, hints)
+ if not pcommon.check(c, "client", "geometry", context) then return end
+
+ if context == "ewmh" and hints then
+ if c.immobilized_horizontal then
+ hints = gtable.clone(hints)
+ hints.x = nil
+ hints.width = nil
+ end
+ if c.immobilized_vertical then
+ hints = gtable.clone(hints)
+ hints.y = nil
+ hints.height = nil
+ end
+ c:geometry(hints)
+ end
+end
+
+-- The magnifier layout doesn't work with focus follow mouse.
+permissions.add_activate_filter(function(c)
+ if alayout.get(c.screen) ~= alayout.suit.magnifier
+ and aclient.focus.filter(c) then
+ return nil
+ else
+ return false
+ end
+end, "mouse_enter")
+
+--- The default client `request::border` handler.
+--
+-- To replace this handler with your own, use:
+--
+-- client.disconnect_signal("request::border", awful.ewmh.update_border)
+--
+-- The default implementation chooses from dozens beautiful theme variables
+-- depending if the client is tiled, floating, maximized and then from its state
+-- (urgent, new, active, normal)
+--
+-- @signalhandler awful.ewmh.update_border
+-- @usebeautiful beautiful.border_color_active
+-- @usebeautiful beautiful.border_color_normal
+-- @usebeautiful beautiful.border_color_new
+-- @usebeautiful beautiful.border_color_urgent
+-- @usebeautiful beautiful.border_color_floating
+-- @usebeautiful beautiful.border_color_floating_active
+-- @usebeautiful beautiful.border_color_floating_normal
+-- @usebeautiful beautiful.border_color_floating_new
+-- @usebeautiful beautiful.border_color_floating_urgent
+-- @usebeautiful beautiful.border_color_maximized
+-- @usebeautiful beautiful.border_color_maximized_active
+-- @usebeautiful beautiful.border_color_maximized_normal
+-- @usebeautiful beautiful.border_color_maximized_new
+-- @usebeautiful beautiful.border_color_maximized_urgent
+-- @usebeautiful beautiful.border_color_fullscreen
+-- @usebeautiful beautiful.border_color_fullscreen_active
+-- @usebeautiful beautiful.border_color_fullscreen_normal
+-- @usebeautiful beautiful.border_color_fullscreen_new
+-- @usebeautiful beautiful.border_color_fullscreen_urgent
+-- @usebeautiful beautiful.border_width_active
+-- @usebeautiful beautiful.border_width_normal
+-- @usebeautiful beautiful.border_width_new
+-- @usebeautiful beautiful.border_width_urgent
+-- @usebeautiful beautiful.border_width_floating
+-- @usebeautiful beautiful.border_width_floating_active
+-- @usebeautiful beautiful.border_width_floating_normal
+-- @usebeautiful beautiful.border_width_floating_new
+-- @usebeautiful beautiful.border_width_floating_urgent
+-- @usebeautiful beautiful.border_width_maximized
+-- @usebeautiful beautiful.border_width_maximized_active
+-- @usebeautiful beautiful.border_width_maximized_normal
+-- @usebeautiful beautiful.border_width_maximized_new
+-- @usebeautiful beautiful.border_width_maximized_urgent
+-- @usebeautiful beautiful.border_width_fullscreen
+-- @usebeautiful beautiful.border_width_fullscreen_active
+-- @usebeautiful beautiful.border_width_fullscreen_normal
+-- @usebeautiful beautiful.border_width_fullscreen_new
+-- @usebeautiful beautiful.border_width_fullscreen_urgent
+-- @usebeautiful beautiful.opacity_floating
+-- @usebeautiful beautiful.opacity_floating_active
+-- @usebeautiful beautiful.opacity_floating_normal
+-- @usebeautiful beautiful.opacity_floating_new
+-- @usebeautiful beautiful.opacity_floating_urgent
+-- @usebeautiful beautiful.opacity_maximized
+-- @usebeautiful beautiful.opacity_maximized_active
+-- @usebeautiful beautiful.opacity_maximized_normal
+-- @usebeautiful beautiful.opacity_maximized_new
+-- @usebeautiful beautiful.opacity_maximized_urgent
+-- @usebeautiful beautiful.opacity_fullscreen
+-- @usebeautiful beautiful.opacity_fullscreen_active
+-- @usebeautiful beautiful.opacity_fullscreen_normal
+-- @usebeautiful beautiful.opacity_fullscreen_new
+-- @usebeautiful beautiful.opacity_fullscreen_urgent
+-- @usebeautiful beautiful.opacity_active
+-- @usebeautiful beautiful.opacity_normal
+-- @usebeautiful beautiful.opacity_new
+-- @usebeautiful beautiful.opacity_urgent
+
+function permissions.update_border(c, context)
+ if not pcommon.check(c, "client", "border", context) then return end
+
+ local suffix, fallback = "", ""
+
+ -- Add the sub-namespace.
+ if c.fullscreen then
+ suffix, fallback = "_fullscreen", "_fullscreen"
+ elseif c.maximized then
+ suffix, fallback = "_maximized", "_maximized"
+ elseif c.floating then
+ suffix, fallback = "_floating", "_floating"
+ end
+
+ -- Add the state suffix.
+ if c.urgent then
+ suffix = suffix .. "_urgent"
+ elseif c.active then
+ suffix = suffix .. "_active"
+ elseif context == "added" then
+ suffix = suffix .. "_new"
+ else
+ suffix = suffix .. "_normal"
+ end
+
+ if not c._private._user_border_width then
+ c._border_width = beautiful["border_width"..suffix]
+ or beautiful["border_width"..fallback]
+ or beautiful.border_width
+ end
+
+ if not c._private._user_border_color then
+ -- First, check marked clients. This is a concept that should probably
+ -- never have been added to the core. The documentation claims it works,
+ -- even if it has been broken for 90% of AwesomeWM releases ever since
+ -- it was added.
+ if c.marked and beautiful.border_marked then
+ c._border_color = beautiful.border_marked
+ return
+ end
+
+ local tv = beautiful["border_color"..suffix]
+
+ if fallback ~= "" and not tv then
+ tv = beautiful["border_color"..fallback]
+ end
+
+ -- The old theme variable did not have "color" in its name.
+ if (not tv) and beautiful.border_normal and (not c.active) then
+ gdebug.deprecate(
+ "Use `beautiful.border_color_normal` instead of `beautiful.border_normal`",
+ {deprecated_in=5}
+ )
+ tv = beautiful.border_normal
+ elseif (not tv) and beautiful.border_focus then
+ gdebug.deprecate(
+ "Use `beautiful.border_color_active` instead of `beautiful.border_focus`",
+ {deprecated_in=5}
+ )
+ tv = beautiful.border_focus
+ end
+
+ if not tv then
+ tv = beautiful.border_color
+ end
+
+ if tv then
+ c._border_color = tv
+ end
+ end
+
+ if not c._private._user_opacity then
+ local tv = beautiful["opacity"..suffix]
+
+ if fallback ~= "" and not tv then
+ tv = beautiful["opacity"..fallback]
+ end
+
+ if tv then
+ c._opacity = tv
+ end
+ end
+end
+
+local activate_context_map = {
+ mouse_enter = "mouse.enter",
+ switch_tag = "autofocus.check_focus_tag",
+ history = "autofocus.check_focus"
+}
+
+--- Default handler for the `request::autoactivate` signal.
+--
+-- All it does is to emit `request::activate` with the following context
+-- mapping:
+--
+-- * mouse_enter: *mouse.enter*
+-- * switch_tag : *autofocus.check_focus_tag*
+-- * history : *autofocus.check_focus*
+--
+-- @signalhandler awful.permissions.autoactivate
+function permissions.autoactivate(c, context, args)
+ if not pcommon.check(c, "client", "autoactivate", context) then return end
+
+ local ctx = activate_context_map[context] and
+ activate_context_map[context] or context
+
+ c:emit_signal("request::activate", ctx, args)
+end
+
+client.connect_signal("request::autoactivate" , permissions.autoactivate)
+client.connect_signal("request::border" , permissions.update_border)
+client.connect_signal("request::activate" , permissions.activate)
+client.connect_signal("request::tag" , permissions.tag)
+client.connect_signal("request::urgent" , permissions.urgent)
+client.connect_signal("request::geometry" , permissions.geometry)
+client.connect_signal("request::geometry" , permissions.merge_maximization)
+client.connect_signal("request::geometry" , permissions.client_geometry_requests)
+client.connect_signal("property::border_width" , repair_geometry)
+client.connect_signal("property::screen" , repair_geometry)
+client.connect_signal("request::unmanage" , check_focus_delayed)
+client.connect_signal("tagged" , check_focus_delayed)
+client.connect_signal("untagged" , check_focus_delayed)
+client.connect_signal("property::hidden" , check_focus_delayed)
+client.connect_signal("property::minimized" , check_focus_delayed)
+client.connect_signal("property::sticky" , check_focus_delayed)
+
+tag.connect_signal("property::selected", function (t)
+ timer.delayed_call(check_focus_tag, t)
+end)
+
+screen.connect_signal("property::workarea", function(s)
+ for _, c in pairs(client.get(s)) do
+ repair_geometry(c)
+ end
+end)
+
+-- Enable sloppy focus, so that focus follows mouse.
+client.connect_signal("mouse::enter", function(c)
+ c:emit_signal("request::autoactivate", "mouse_enter", {raise=false})
+end)
+
+return permissions
+
+-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
diff --git a/lib/awful/placement.lua b/lib/awful/placement.lua
index 18f204b7d..354c81a41 100644
--- a/lib/awful/placement.lua
+++ b/lib/awful/placement.lua
@@ -708,7 +708,7 @@ local function get_relative_regions(geo, mode, is_absolute)
-- Detect various types of geometry table and (try) to get rid of the
-- differences so the code below don't have to care anymore.
if geo.drawin then
- bw, dgeo = geo.drawin.border_width, geo.drawin:geometry()
+ bw, dgeo = geo.drawin._border_width, geo.drawin:geometry()
elseif geo.drawable and geo.drawable.get_wibox then
bw = geo.drawable.get_wibox().border_width
dgeo = geo.drawable.get_wibox():geometry()
diff --git a/lib/awful/rules.lua b/lib/awful/rules.lua
index 9f4a6c401..2fe0ad249 100644
--- a/lib/awful/rules.lua
+++ b/lib/awful/rules.lua
@@ -529,6 +529,8 @@ end
-- @tab props Properties to apply.
-- @tab[opt] callbacks Callbacks to apply.
-- @staticfct awful.rules.execute
+-- @request client titlebars rules granted The `titlebars_enabled` is set in the
+-- rules.
crules._execute = function(_, c, props, callbacks)
@@ -538,6 +540,12 @@ crules._execute = function(_, c, props, callbacks)
props.keys = props.keys or keys
props.buttons = props.buttons or btns
+ -- Border width will also cause geometry related properties to fail
+ if props.border_width then
+ c.border_width = type(props.border_width) == "function" and
+ props.border_width(c, props) or props.border_width
+ end
+
-- This has to be done first, as it will impact geometry related props.
if props.titlebars_enabled and (type(props.titlebars_enabled) ~= "function"
or props.titlebars_enabled(c,props)) then
@@ -545,12 +553,6 @@ crules._execute = function(_, c, props, callbacks)
c._request_titlebars_called = true
end
- -- Border width will also cause geometry related properties to fail
- if props.border_width then
- c.border_width = type(props.border_width) == "function" and
- props.border_width(c, props) or props.border_width
- end
-
-- Size hints will be re-applied when setting width/height unless it is
-- disabled first
if props.size_hints_honor ~= nil then
@@ -657,7 +659,7 @@ function rules.completed_with_payload_callback(c, props, callbacks)
rules.execute(c, props, callbacks)
end
-client.connect_signal("manage", rules.apply)
+client.connect_signal("request::manage", rules.apply)
--@DOC_rule_COMMON@
diff --git a/lib/awful/screen.lua b/lib/awful/screen.lua
index 5c6a2c0f1..a1dfda78c 100644
--- a/lib/awful/screen.lua
+++ b/lib/awful/screen.lua
@@ -89,6 +89,8 @@ end
-- or keeps its position relative to the current focused screen.
-- @staticfct awful.screen.focus
-- @screen _screen Screen number (defaults / falls back to mouse.screen).
+-- @request client activate screen.focus granted The most recent focused client
+-- for this screen should be re-activated.
function screen.focus(_screen)
client = client or require("awful.client")
if type(_screen) == "number" and _screen > capi.screen.count() then _screen = screen.focused() end
@@ -771,7 +773,9 @@ end
-- The only default implementation is the one provided by `rc.lua`.
--
-- @signal request::desktop_decoration
--- @tparam screen s The screen object.
+-- @tparam string context The context.
+-- @request screen wallpaper added granted When the decorations needs to be
+-- added to a new screen.
--- Emitted when a new screen needs a wallpaper.
--
@@ -782,7 +786,13 @@ end
-- The only default implementation is the one provided by `rc.lua`.
--
-- @signal request::wallpaper
--- @tparam screen s The screen object.
+-- @tparam string context The context.
+-- @request screen wallpaper added granted When the wallpaper needs to be
+-- added to a new screen.
+-- @request screen wallpaper geometry granted When the wallpaper needs to be
+-- updated because the resolution changed.
+-- @request screen wallpaper dpi granted When the wallpaper needs to be
+-- updated because the DPI changed.
--- When a new (physical) screen area has been added.
--
@@ -965,15 +975,15 @@ capi.screen.connect_signal("_added", function(s)
-- metadata. Thus, the DPI may be wrong when setting the wallpaper.
if s._managed ~= "Lua" then
s:emit_signal("added")
- s:emit_signal("request::desktop_decoration")
- s:emit_signal("request::wallpaper")
+ s:emit_signal("request::desktop_decoration", "added")
+ s:emit_signal("request::wallpaper", "added")
end
end)
-- Resize the wallpaper(s)
for _, prop in ipairs {"geometry", "dpi" } do
capi.screen.connect_signal("property::"..prop, function(s)
- s:emit_signal("request::wallpaper")
+ s:emit_signal("request::wallpaper", prop)
end)
end
diff --git a/lib/awful/spawn.lua b/lib/awful/spawn.lua
index 8b54b4d89..2899636f3 100644
--- a/lib/awful/spawn.lua
+++ b/lib/awful/spawn.lua
@@ -712,6 +712,8 @@ local raise_rules = {focus = true, switch_to_tags = true, raise = true}
-- @see awful.rules
-- @treturn client The client if it already exists.
-- @staticfct awful.spawn.raise_or_spawn
+-- @request client activate spawn.raise_or_spawn granted Activate a client when
+-- `awful.spawn.raise_or_spawn` is called and the client exists.
function spawn.raise_or_spawn(cmd, rules, matcher, unique_id, callback)
local hash = unique_id or hash_command(cmd, rules)
@@ -736,7 +738,7 @@ end
capi.awesome.connect_signal("spawn::canceled" , spawn.on_snid_cancel )
capi.awesome.connect_signal("spawn::timeout" , spawn.on_snid_cancel )
-capi.client.connect_signal ("manage" , spawn.on_snid_callback )
+capi.client.connect_signal ("request::manage" , spawn.on_snid_callback )
return setmetatable(spawn, { __call = function(_, ...) return spawn.spawn(...) end })
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
diff --git a/lib/awful/tag.lua b/lib/awful/tag.lua
index 0d8867046..fe831453c 100644
--- a/lib/awful/tag.lua
+++ b/lib/awful/tag.lua
@@ -815,6 +815,8 @@ end
--
-- @property layouts
-- @param table
+-- @request tag layouts awful granted When the `layouts` property is first called
+-- and there is no layouts, then that signal is called.
-- @see awful.layout.layouts
-- @see layout
@@ -867,6 +869,22 @@ function tag.object.get_layouts(self)
local cls = custom_layouts(self)
+ -- Request some layouts. Maybe a new module was added?
+ if #cls == 0 and not tag.getproperty(self, "_layouts_requested") then
+ tag.setproperty(self, "_layouts_requested", true)
+ local old_count = #cls
+ self:emit_signal("request::layouts", "awful", {})
+
+ -- When request::layouts is used, assume it takes precedence over
+ -- the fallback.
+ if #cls > old_count then
+ tag.setproperty(self, "_layouts", gtable.clone(cls, false))
+ return tag.getproperty(self, "_layouts")
+ end
+
+ return tag.object.get_layouts(self)
+ end
+
-- Without the clone, the custom_layouts would grow
return #cls > 0 and gtable.merge(gtable.clone(cls, false), alayout.layouts) or
alayout.layouts
@@ -882,6 +900,60 @@ function tag.object.set_layouts(self, layouts)
self:emit_signal("property::layouts")
end
+function tag.object.append_layout(self, layout)
+ -- If the layouts are manually modified, don't request more.
+ tag.setproperty(self, "_layouts_requested", true)
+
+ local cls = tag.getproperty(self, "_layouts")
+
+ if not cls then
+ cls = custom_layouts(self)
+ end
+
+ table.insert(cls, layout)
+ self:emit_signal("property::layouts")
+end
+
+function tag.object.append_layouts(self, layouts)
+ -- If the layouts are manually modified, don't request more.
+ tag.setproperty(self, "_layouts_requested", true)
+
+ local cls = tag.getproperty(self, "_layouts")
+
+ if not cls then
+ cls = custom_layouts(self)
+ end
+
+ for _, l in ipairs(layouts) do
+ table.insert(cls, l)
+ end
+ self:emit_signal("property::layouts")
+end
+
+function tag.object.remove_layout(self, layout)
+ local cls = tag.getproperty(self, "_layouts")
+
+ if not cls then
+ cls = custom_layouts(self)
+ end
+
+ local pos = {}
+ for k, l in ipairs(cls) do
+ if l == layout then
+ table.insert(pos, k)
+ end
+ end
+
+ if #pos > 0 then
+ for i=#pos, 1, -1 do
+ table.remove(cls, i)
+ end
+ self:emit_signal("property::layouts")
+ end
+
+ return #pos > 0
+end
+
function tag.object.get_layout(t)
local l = tag.getproperty(t, "layout")
if l then return l end
@@ -1627,6 +1699,7 @@ capi.client.connect_signal("property::screen", function(c)
end
if #new_tags == 0 then
+ --TODO v5: Add a context as first param
c:emit_signal("request::tag", nil, {reason="screen"})
elseif #new_tags < #tags then
c:tags(new_tags)
@@ -1687,6 +1760,7 @@ capi.tag.connect_signal("request::select", tag.object.view_only)
-- this, an handler for this request must simply set a new screen
-- for the tag.
-- @signal request::screen
+-- @tparam string context Why it was called.
--- Emitted after `request::screen` if no new screen has been set.
-- The tag will be deleted, this is a last chance to move its clients
@@ -1706,7 +1780,7 @@ end)
capi.screen.connect_signal("removed", function(s)
-- First give other code a chance to move the tag to another screen
for _, t in pairs(s.tags) do
- t:emit_signal("request::screen")
+ t:emit_signal("request::screen", "removed")
end
-- Everything that's left: Tell everyone that these tags go away (other code
-- could e.g. save clients)
@@ -1715,6 +1789,7 @@ capi.screen.connect_signal("removed", function(s)
end
-- Give other code yet another change to save clients
for _, c in pairs(capi.client.get(s)) do
+ --TODO v5: Add a context as first param
c:emit_signal("request::tag", nil, { reason = "screen-removed" })
end
-- Then force all clients left to go somewhere random
diff --git a/lib/awful/titlebar.lua b/lib/awful/titlebar.lua
index 57bc3b011..734aecb27 100644
--- a/lib/awful/titlebar.lua
+++ b/lib/awful/titlebar.lua
@@ -443,7 +443,7 @@ local all_titlebars = setmetatable({}, { __mode = 'k' })
-- Get a color for a titlebar, this tests many values from the array and the theme
local function get_color(name, c, args)
local suffix = "_normal"
- if capi.client.focus == c then
+ if c.active then
suffix = "_focus"
end
local function get(array)
@@ -470,12 +470,13 @@ end
-- when `titlebars_enabled` is not set in the rules.
-- @tparam client c The client.
-- @tparam[opt=false] boolean hide_all Hide all titlebars except `keep`
--- @tparam string keep Keep the titlebar at this position
+-- @tparam string keep Keep the titlebar at this position.
+-- @tparam string context The reason why this was called.
-- @treturn boolean If the titlebars were loaded
-local function load_titlebars(c, hide_all, keep)
+local function load_titlebars(c, hide_all, keep, context)
if c._request_titlebars_called then return false end
- c:emit_signal("request::titlebars", "awful.titlebar", {})
+ c:emit_signal("request::titlebars", context, {})
if hide_all then
-- Don't bother checking if it has been created, `.hide` don't works
@@ -553,11 +554,12 @@ local function new(c, args)
}
-- Update the colors when focus changes
- c:connect_signal("focus", update_colors)
- c:connect_signal("unfocus", update_colors)
+ c:connect_signal("property::active", update_colors)
-- Inform the drawable when it becomes invisible
- c:connect_signal("unmanage", function() ret:_inform_visible(false) end)
+ c:connect_signal("request::unmanage", function()
+ ret:_inform_visible(false)
+ end)
else
bars[position].args = args
ret = bars[position].drawable
@@ -581,9 +583,11 @@ end
-- @param[opt] position The position of the titlebar. Must be one of "left",
-- "right", "top", "bottom". Default is "top".
-- @staticfct awful.titlebar.show
+-- @request client titlebars show granted Called when `awful.titlebar.show` is
+-- called.
function titlebar.show(c, position)
position = position or "top"
- if load_titlebars(c, true, position) then return end
+ if load_titlebars(c, true, position, "show") then return end
local bars = all_titlebars[c]
local data = bars and bars[position]
local args = data and data.args
@@ -605,9 +609,11 @@ end
-- @param[opt] position The position of the titlebar. Must be one of "left",
-- "right", "top", "bottom". Default is "top".
-- @staticfct awful.titlebar.toggle
+-- @request client titlebars toggle granted Called when `awful.titlebar.toggle` is
+-- called.
function titlebar.toggle(c, position)
position = position or "top"
- if load_titlebars(c, true, position) then return end
+ if load_titlebars(c, true, position, "toggle") then return end
local _, size = get_titlebar_function(c, position)(c)
if size == 0 then
titlebar.show(c, position)
@@ -705,7 +711,7 @@ function titlebar.widget.button(c, name, selector, action)
end
end
local prefix = "normal"
- if capi.client.focus == c then
+ if c.active then
prefix = "focus"
end
if img ~= "" then
@@ -832,7 +838,7 @@ function titlebar.widget.stickybutton(c)
return widget
end
-client.connect_signal("unmanage", function(c)
+client.connect_signal("request::unmanage", function(c)
all_titlebars[c] = nil
end)
diff --git a/lib/awful/tooltip.lua b/lib/awful/tooltip.lua
index 81674a3bc..b75a758be 100644
--- a/lib/awful/tooltip.lua
+++ b/lib/awful/tooltip.lua
@@ -694,7 +694,7 @@ function tooltip.new(args)
or beautiful.bg_focus or "#ffcb60"
local border_width = args.border_width or beautiful.tooltip_border_width or 0
local border_color = args.border_color or beautiful.tooltip_border_color
- or beautiful.border_normal or "#ffcb60"
+ or beautiful.border_color_normal or "#ffcb60"
-- Set wibox default properties
self.wibox_properties = {
diff --git a/lib/awful/widget/calendar_popup.lua b/lib/awful/widget/calendar_popup.lua
index 3e3479d8d..3c9fa3fb0 100644
--- a/lib/awful/widget/calendar_popup.lua
+++ b/lib/awful/widget/calendar_popup.lua
@@ -131,7 +131,7 @@ local function parse_cell_options(cell, args)
elseif prop == 'border_width' then
default = beautiful.border_width or 0
elseif prop == 'border_color' then
- default = beautiful.border_normal or beautiful.fg_normal
+ default = beautiful.border_color_normal or beautiful.fg_normal
end
-- Get default
diff --git a/lib/awful/widget/taglist.lua b/lib/awful/widget/taglist.lua
index 7e8b117c9..5c35f2004 100644
--- a/lib/awful/widget/taglist.lua
+++ b/lib/awful/widget/taglist.lua
@@ -539,8 +539,7 @@ function taglist.new(args, filter, buttons, style, update_function, base_widget)
end
local uc = function (c) return u(c.screen) end
local ut = function (t) return u(t.screen) end
- capi.client.connect_signal("focus", uc)
- capi.client.connect_signal("unfocus", uc)
+ capi.client.connect_signal("property::active", uc)
tag.attached_connect_signal(nil, "property::selected", ut)
tag.attached_connect_signal(nil, "property::icon", ut)
tag.attached_connect_signal(nil, "property::hide", ut)
@@ -555,7 +554,7 @@ function taglist.new(args, filter, buttons, style, update_function, base_widget)
end)
capi.client.connect_signal("tagged", uc)
capi.client.connect_signal("untagged", uc)
- capi.client.connect_signal("unmanage", uc)
+ capi.client.connect_signal("request::unmanage", uc)
capi.screen.connect_signal("removed", function(s)
instances[get_screen(s)] = nil
end)
diff --git a/lib/awful/widget/tasklist.lua b/lib/awful/widget/tasklist.lua
index d2fe7cfee..8b49b6afe 100644
--- a/lib/awful/widget/tasklist.lua
+++ b/lib/awful/widget/tasklist.lua
@@ -352,7 +352,7 @@ local function tasklist_label(c, args, tb)
end
end
- local focused = capi.client.focus == c
+ local focused = c.active
-- Handle transient_for: the first parent that does not skip the taskbar
-- is considered to be focused, if the real client has skip_taskbar.
if not focused and capi.client.focus and capi.client.focus.skip_taskbar
@@ -621,7 +621,7 @@ function tasklist.new(args, filter, buttons, style, update_function, base_widget
capi.client.connect_signal("property::hidden", u)
capi.client.connect_signal("tagged", u)
capi.client.connect_signal("untagged", u)
- capi.client.connect_signal("unmanage", function(c)
+ capi.client.connect_signal("request::unmanage", function(c)
u(c)
for _, i in pairs(instances) do
for _, tlist in pairs(i) do
@@ -630,8 +630,7 @@ function tasklist.new(args, filter, buttons, style, update_function, base_widget
end
end)
capi.client.connect_signal("list", u)
- capi.client.connect_signal("focus", u)
- capi.client.connect_signal("unfocus", u)
+ capi.client.connect_signal("property::active", u)
capi.screen.connect_signal("removed", function(s)
instances[get_screen(s)] = nil
end)
@@ -723,7 +722,7 @@ end
-- @filterfunction awful.tasklist.filter.focused
function tasklist.filter.focused(c, screen)
-- Only print client on the same screen as this widget
- return get_screen(c.screen) == get_screen(screen) and capi.client.focus == c
+ return get_screen(c.screen) == get_screen(screen) and c.active
end
--- Get all the clients in an undefined order.
diff --git a/lib/beautiful/init.lua b/lib/beautiful/init.lua
index 7731eddc5..9db96f254 100644
--- a/lib/beautiful/init.lua
+++ b/lib/beautiful/init.lua
@@ -132,23 +132,12 @@ local active_font
-- @beautiful beautiful.useless_gap
-- @param[opt=0] number
---- The client border width.
+--- The fallback border width.
-- @beautiful beautiful.border_width
-- @param number
---- The default clients border color.
--- Note that only solid colors are supported.
--- @beautiful beautiful.border_normal
--- @param color
-
---- The focused client border color.
--- Note that only solid colors are supported.
--- @beautiful beautiful.border_focus
--- @param color
-
---- The marked clients border color.
--- Note that only solid colors are supported.
--- @beautiful beautiful.border_marked
+--- The fallback border color.
+-- @beautiful beautiful.border_color
-- @param color
--- The wallpaper path.
diff --git a/lib/naughty/core.lua b/lib/naughty/core.lua
index e65704b47..92342f9e3 100644
--- a/lib/naughty/core.lua
+++ b/lib/naughty/core.lua
@@ -630,7 +630,7 @@ end
-- @string[opt=`beautiful.notification_fg` or `beautiful.fg_focus` or `'#ffffff'`] args.fg Foreground color.
-- @string[opt=`beautiful.notification_fg` or `beautiful.bg_focus` or `'#535d6c'`] args.bg Background color.
-- @int[opt=`beautiful.notification_border_width` or 1] args.border_width Border width.
--- @string[opt=`beautiful.notification_border_color` or `beautiful.border_focus` or `'#535d6c'`] args.border_color Border color.
+-- @string[opt=`beautiful.notification_border_color` or `beautiful.border_color_active` or `'#535d6c'`] args.border_color Border color.
-- @tparam[opt=`beautiful.notification_shape`] gears.shape args.shape Widget shape.
-- @tparam[opt=`beautiful.notification_opacity`] gears.opacity args.opacity Widget opacity.
-- @tparam[opt=`beautiful.notification_margin`] gears.margin args.margin Widget margin.
diff --git a/lib/naughty/notification.lua b/lib/naughty/notification.lua
index f06d21c8d..5edb45e70 100644
--- a/lib/naughty/notification.lua
+++ b/lib/naughty/notification.lua
@@ -777,7 +777,7 @@ end
-- @string[opt=`beautiful.notification_fg` or `beautiful.bg_focus` or `'#535d6c'`] args.bg Background color.
-- @int[opt=`beautiful.notification_border_width` or 1] args.border_width Border width.
-- @string[opt=`beautiful.notification_border_color` or
--- `beautiful.border_focus` or `'#535d6c'`] args.border_color Border color.
+-- `beautiful.border_color_active` or `'#535d6c'`] args.border_color Border color.
-- @tparam[opt=`beautiful.notification_shape`] gears.shape args.shape Widget shape.
-- @tparam[opt=`beautiful.notification_opacity`] gears.opacity args.opacity Widget opacity.
-- @tparam[opt=`beautiful.notification_margin`] gears.margin args.margin Widget margin.
diff --git a/lib/wibox/init.lua b/lib/wibox/init.lua
index 6bf2b85fd..46f72d49c 100644
--- a/lib/wibox/init.lua
+++ b/lib/wibox/init.lua
@@ -221,7 +221,20 @@ function wibox:get_children_by_id(name)
return {}
end
-for _, k in pairs{ "struts", "geometry", "get_xproperty", "set_xproperty" } do
+-- Proxy those properties to decorate their accessors with an extra flag to
+-- define when they are set by the user. This allows to "manage" the value of
+-- those properties internally until they are manually overridden.
+for _, prop in ipairs { "border_width", "border_color", "opacity" } do
+ wibox["get_"..prop] = function(self)
+ return self["_"..prop]
+ end
+ wibox["set_"..prop] = function(self, value)
+ self._private["_user_"..prop] = true
+ self["_"..prop] = value
+ end
+end
+
+for _, k in ipairs{ "struts", "geometry", "get_xproperty", "set_xproperty" } do
wibox[k] = function(self, ...)
return self.drawin[k](self.drawin, ...)
end
@@ -362,6 +375,14 @@ local function new(args)
ret.shape = args.shape
end
+ if args.border_width then
+ ret.border_width = args.border_width
+ end
+
+ if args.border_color then
+ ret.border_color = args.border_color
+ end
+
if args.input_passthrough then
ret.input_passthrough = args.input_passthrough
end
diff --git a/objects/client.c b/objects/client.c
index 150bc0bfa..4178235b0 100644
--- a/objects/client.c
+++ b/objects/client.c
@@ -63,7 +63,7 @@
*
* To execute a callback when a new client is added, use the `manage` signal:
*
- * client.connect_signal("manage", function(c)
+ * client.connect_signal("request::manage", function(c)
* -- do something
* end)
*
@@ -164,7 +164,48 @@
*/
/** When a new client appears and gets managed by Awesome.
- * @signal manage
+ *
+ * This request should be implemented by code which track the client. It isn't
+ * recommended to use this to initialize the client content. This use case is
+ * a better fit for `ruled.client`, which has built-in dependency management.
+ * Using this request to mutate the client state will likely conflict with
+ * `ruled.client`.
+ *
+ * @signal request::manage
+ * @tparam client c The client.
+ * @tparam string context What created the client. It is currently either "new"
+ * or "startup".
+ * @tparam table hints More metadata (currently empty, it exists for compliance
+ * with the other `request::` signals).
+ * @request client border added granted When a new client needs a its initial
+ * border settings.
+ */
+
+/** When a client is going away.
+ *
+ * Each places which store `client` objects in non-weak table or whose state
+ * depend on the current client should answer this request.
+ *
+ * The contexts are:
+ *
+ * * **user**: `c:unmanage()` was called.
+ * * **reparented**: The window was reparented to another window. It is no
+ * longer a stand alone client.
+ * * **destroyed**: The window was closed.
+ *
+ * @signal request::unmanage
+ * @tparam client c The client.
+ * @tparam string context Why was the client unmanaged.
+ * @tparam table hints More metadata (currently empty, it exists for compliance
+ * with the other `request::` signals).
+ */
+
+/** Use `request::manage`.
+ * @deprecatedsignal manage
+ */
+
+/** Use `request::unmanage`.
+ * @deprecatedsignal unmanage
*/
/**
@@ -225,15 +266,41 @@
* @tparam string context The context where this signal was used.
* @tparam[opt] table hints A table with additional hints:
* @tparam[opt=false] boolean hints.raise should the client be raised?
+ * @request client activate ewmh granted When the client asks to be activated.
*/
-/**
+/** When an event could lead to the client being activated.
+ *
+ * This is an layer "on top" of `request::activate` for event which are not
+ * actual request for activation/focus, but where "it would be nice" if the
+ * client got the focus. This includes the focus-follow-mouse model and focusing
+ * previous clients when the selected tag changes.
+ *
+ * This idea is that `request::autoactivate` will emit `request::activate`.
+ * However it is much easier to replace the handler for `request::autoactivate`
+ * than it is to replace the handler for `request::activate`. Thus it provides
+ * a nice abstraction to simplify handling the focus when switching tags or
+ * moving the mouse.
+ *
+ * @signal request::autoactivate
+ * @tparam string context The context where this signal was used.
+ * @tparam[opt] table hints A table with additional hints:
+ * @tparam[opt=false] boolean hints.raise should the client be raised?
+ *
+ */
+
+/** When something request a client geometry to be modified.
+ *
* @signal request::geometry
* @tparam client c The client
* @tparam string context Why and what to resize. This is used for the
* handlers to know if they are capable of applying the new geometry.
* @tparam[opt={}] table Additional arguments. Each context handler may
* interpret this differently.
+ * @request client geometry client_maximize_horizontal granted When a client
+ * (programmatically) asks for the maximization to be changed.
+ * @request client geometry client_maximize_vertical granted When a client
+ * (programmatically) asks for the maximization to be changed.
*/
/**
@@ -253,6 +320,7 @@
* @signal request::default_mousebindings
* @tparam string context The reason why the signal was sent (currently always
* `startup`).
+ * @classsignal
*/
/** Emitted during startup to gather the default client keybindings.
@@ -264,6 +332,20 @@
* @signal request::default_keybindings
* @tparam string context The reason why the signal was sent (currently always
* `startup`).
+ * @classsignal
+ */
+
+/** Sent once when AwesomeWM starts to add default keybindings.
+ *
+ * Keybindings can be set directly on clients. Actually, older version of
+ * AwesomeWM did that through the rules. However this makes it impossible for
+ * auto-configured modules to add their own keybindings. Using the signals,
+ * `rc.lua` or any module can cleanly manage keybindings.
+ *
+ * @signal request::default_keybindings
+ * @tparam string context The context (currently always "startup").
+ * @classsignal
+ * @request client default_keybindings startup granted Sent when AwesomeWM starts.
*/
/** When a client gets tagged.
@@ -275,10 +357,6 @@
* @signal unfocus
*/
-/**
- * @signal unmanage
- */
-
/** When a client gets untagged.
* @signal untagged
* @tag t The tag object.
@@ -533,20 +611,19 @@
* The client border width.
* @property border_width
* @param integer
+ * @propemits false false
+ * @see request::border
*/
/**
* The client border color.
*
- * **Signal:**
- *
- * * *property::border\_color*
- *
- * @see gears.color
- *
* @property border_color
- * @param pattern Any string, gradients and patterns will be converted to a
+ * @param color Any string, gradients and patterns will be converted to a
* cairo pattern.
+ * @propemits false false
+ * @see request::border
+ * @see gears.color
*/
/**
@@ -558,6 +635,8 @@
*
* @property urgent
* @param boolean
+ * @propemits false false
+ * @see request::border
*/
/**
@@ -584,6 +663,8 @@
*
* @property opacity
* @param number Between 0 (transparent) to 1 (opaque)
+ * @propemits false false
+ * @see request::border
*/
/**
@@ -619,12 +700,11 @@
*
* @DOC_sequences_client_fullscreen_EXAMPLE@
*
- * **Signal:**
- *
- * * *property::fullscreen*
- *
* @property fullscreen
- * @param boolean
+ * @tparam boolean fullscreen
+ * @propemits false false
+ * @request client geometry fullscreen granted When the client must be resized
+ * because it became (or stop being) fullscreen.
*/
/**
@@ -632,12 +712,12 @@
*
* @DOC_sequences_client_maximized_EXAMPLE@
*
- * **Signal:**
- *
- * * *property::maximized*
- *
* @property maximized
- * @param boolean
+ * @tparam boolean maximized
+ * @propemits false false
+ * @request client geometry maximized granted When the client must be resized
+ * because it became (or stop being) maximized.
+ * @see request::border
*/
/**
@@ -645,12 +725,11 @@
*
* @DOC_sequences_client_maximized_horizontal_EXAMPLE@
*
- * **Signal:**
- *
- * * *property::maximized\_horizontal*
- *
* @property maximized_horizontal
- * @param boolean
+ * @tparam boolean maximized_horizontal
+ * @propemits false false
+ * @request client geometry maximized_horizontal granted When the client must be resized
+ * because it became (or stop being) maximized horizontally.
*/
/**
@@ -658,34 +737,27 @@
*
* @DOC_sequences_client_maximized_vertical_EXAMPLE@
*
- * **Signal:**
- *
- * * *property::maximized\_vertical*
- *
* @property maximized_vertical
- * @param boolean
+ * @tparam boolean maximized_vertical
+ * @propemits false false
+ * @request client geometry maximized_vertical granted When the client must be resized
+ * because it became (or stop being) maximized vertically.
*/
/**
* The client the window is transient for.
*
- * **Signal:**
- *
- * * *property::transient\_for*
- *
* @property transient_for
* @param client
+ * @propemits false false
*/
/**
* Window identification unique to a group of windows.
*
- * **Signal:**
- *
- * * *property::group\_window*
- *
* @property group_window
* @param client
+ * @propemits false false
*/
/**
@@ -697,10 +769,6 @@
/**
* A table with size hints of the client.
*
- * **Signal:**
- *
- * * *property::size\_hints*
- *
* @property size_hints
* @param table
* @tfield integer table.user_position
@@ -713,6 +781,7 @@
* @tfield integer table.min_height
* @tfield integer table.width_inc
* @tfield integer table.height_inc
+ * @propemits false false
* @see size_hints_honor
*/
@@ -727,10 +796,6 @@
* "resize" and "all" are set, this means that all but the resize function
* should be enabled.
*
- * **Signal:**
- *
- * * *property::motif\_wm\_hints*
- *
* @property motif_wm_hints
* @param table
* @tfield[opt] table table.functions
@@ -751,51 +816,40 @@
* @tfield[opt] string table.input_mode
* @tfield[opt] table table.status
* @tfield[opt] boolean table.status.tearoff_window
+ * @propemits false false
*/
/**
* Set the client sticky, i.e. available on all tags.
*
- * **Signal:**
- *
- * * *property::sticky*
- *
* @property sticky
* @param boolean
+ * @propemits false false
*/
/**
* Indicate if the client is modal.
*
- * **Signal:**
- *
- * * *property::modal*
- *
* @property modal
* @param boolean
+ * @propemits false false
*/
/**
* True if the client can receive the input focus.
*
- * **Signal:**
- *
- * * *property::focusable*
- *
* @property focusable
* @param boolean
+ * @propemits false false
*/
/**
* The client's bounding shape as set by awesome as a (native) cairo surface.
*
- * **Signal:**
- *
- * * *property::shape\_bounding*
- *
* @see gears.surface.apply_shape_bounding
* @property shape_bounding
* @param surface
+ * @propemits false false
*/
/**
@@ -941,27 +995,6 @@
* @see client.geometry
*/
-/**
- * The border color when the client is focused.
- *
- * @beautiful beautiful.border_focus
- * @param string
- */
-
-/**
- * The border color when the client is not focused.
- *
- * @beautiful beautiful.border_normal
- * @param string
- */
-
-/**
- * The client border width.
- *
- * @beautiful beautiful.border_width
- * @param integer
- */
-
/** Return client struts (reserved space at the edge of the screen).
*
* @param struts A table with new strut values, or none.
@@ -1217,6 +1250,9 @@ client_unfocus_internal(client_t *c)
globalconf.focus.client = NULL;
luaA_object_push(L, c);
+
+ lua_pushboolean(L, false);
+ luaA_object_emit_signal(L, -2, "property::active", 1);
luaA_object_emit_signal(L, -1, "unfocus", 0);
lua_pop(L, 1);
}
@@ -1334,8 +1370,11 @@ client_focus_update(client_t *c)
luaA_object_push(L, c);
client_set_urgent(L, -1, false);
- if(focused_new)
+ if(focused_new) {
+ lua_pushboolean(L, true);
+ luaA_object_emit_signal(L, -2, "property::active", 1);
luaA_object_emit_signal(L, -1, "focus", 0);
+ }
lua_pop(L, 1);
@@ -1735,7 +1774,19 @@ client_manage(xcb_window_t w, xcb_get_geometry_reply_t *wgeom, xcb_get_window_at
luaA_class_emit_signal(L, &client_class, "list", 0);
+ /* Add the context */
+ if (globalconf.loop == NULL)
+ lua_pushstring(L, "startup");
+ else
+ lua_pushstring(L, "new");
+
+ /* Hints */
+ lua_newtable(L);
+
/* client is still on top of the stack; emit signal */
+ luaA_object_emit_signal(L, -3, "request::manage", 2);
+
+ /*TODO v6: remove this*/
luaA_object_emit_signal(L, -1, "manage", 0);
xcb_generic_error_t *error = xcb_request_check(globalconf.connection, reparent_cookie);
@@ -1744,7 +1795,7 @@ client_manage(xcb_window_t w, xcb_get_geometry_reply_t *wgeom, xcb_get_window_at
NONULL(c->name), NONULL(c->class), NONULL(c->instance));
event_handle((xcb_generic_event_t *) error);
p_delete(&error);
- client_unmanage(c, true);
+ client_unmanage(c, CLIENT_UNMANAGE_FAILED);
}
/* pop client */
@@ -2354,10 +2405,10 @@ client_unban(client_t *c)
/** Unmanage a client.
* \param c The client.
- * \param window_valid Is the client's window still valid?
+ * \param reason Why was the unmanage done.
*/
void
-client_unmanage(client_t *c, bool window_valid)
+client_unmanage(client_t *c, client_unmanage_t reason)
{
lua_State *L = globalconf_get_lua_State();
@@ -2384,6 +2435,28 @@ client_unmanage(client_t *c, bool window_valid)
untag_client(c, globalconf.tags.tab[i]);
luaA_object_push(L, c);
+
+ /* Give the context to Lua */
+ switch (reason)
+ {
+ break;
+ case CLIENT_UNMANAGE_USER:
+ lua_pushstring(L, "user");
+ break;
+ case CLIENT_UNMANAGE_REPARENT:
+ lua_pushstring(L, "reparented");
+ break;
+ case CLIENT_UNMANAGE_UNMAP:
+ case CLIENT_UNMANAGE_FAILED:
+ case CLIENT_UNMANAGE_DESTROYED:
+ lua_pushstring(L, "destroyed");
+ break;
+ }
+
+ /* Hints */
+ lua_newtable(L);
+
+ luaA_object_emit_signal(L, -3, "request::unmanage", 2);
luaA_object_emit_signal(L, -1, "unmanage", 0);
lua_pop(L, 1);
@@ -2413,7 +2486,7 @@ client_unmanage(client_t *c, bool window_valid)
/* Clear our event mask so that we don't receive any events from now on,
* especially not for the following requests. */
- if(window_valid)
+ if(reason != CLIENT_UNMANAGE_DESTROYED)
xcb_change_window_attributes(globalconf.connection,
c->window,
XCB_CW_EVENT_MASK,
@@ -2423,7 +2496,7 @@ client_unmanage(client_t *c, bool window_valid)
XCB_CW_EVENT_MASK,
(const uint32_t []) { 0 });
- if(window_valid)
+ if(reason != CLIENT_UNMANAGE_DESTROYED)
{
xcb_unmap_window(globalconf.connection, c->window);
xcb_reparent_window(globalconf.connection, c->window, globalconf.screen->root,
@@ -2434,7 +2507,7 @@ client_unmanage(client_t *c, bool window_valid)
window_array_append(&globalconf.destroy_later_windows, c->nofocus_window);
window_array_append(&globalconf.destroy_later_windows, c->frame_window);
- if(window_valid)
+ if(reason != CLIENT_UNMANAGE_DESTROYED)
{
/* Remove this window from the save set since this shouldn't be made visible
* after a restart anymore. */
@@ -2823,7 +2896,7 @@ static int
luaA_client_unmanage(lua_State *L)
{
client_t *c = luaA_checkudata(L, 1, &client_class);
- client_unmanage(c, true);
+ client_unmanage(c, CLIENT_UNMANAGE_USER);
return 0;
}
@@ -4020,4 +4093,6 @@ client_class_setup(lua_State *L)
/* @DOC_cobject_COMMON@ */
+/* @DOC_client_theme_COMMON@ */
+
// vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
diff --git a/objects/client.h b/objects/client.h
index 0c8193184..74a23131c 100644
--- a/objects/client.h
+++ b/objects/client.h
@@ -47,6 +47,14 @@ typedef enum {
CLIENT_TITLEBAR_COUNT = 4
} client_titlebar_t;
+typedef enum {
+ CLIENT_UNMANAGE_DESTROYED = 0,
+ CLIENT_UNMANAGE_USER = 1,
+ CLIENT_UNMANAGE_REPARENT = 2,
+ CLIENT_UNMANAGE_UNMAP = 3,
+ CLIENT_UNMANAGE_FAILED = 4
+} client_unmanage_t;
+
/* Special bit we invented to "fake" unset hints */
#define MWM_HINTS_AWESOME_SET (1L << 15)
@@ -206,7 +214,7 @@ void client_ban_unfocus(client_t *);
void client_unban(client_t *);
void client_manage(xcb_window_t, xcb_get_geometry_reply_t *, xcb_get_window_attributes_reply_t *);
bool client_resize(client_t *, area_t, bool);
-void client_unmanage(client_t *, bool);
+void client_unmanage(client_t *, client_unmanage_t);
void client_kill(client_t *);
void client_set_sticky(lua_State *, int, bool);
void client_set_above(lua_State *, int, bool);
diff --git a/objects/screen.c b/objects/screen.c
index fc3417c89..230683684 100644
--- a/objects/screen.c
+++ b/objects/screen.c
@@ -115,6 +115,8 @@
/**
* This signal is emitted when a screen is removed from the setup.
* @signal removed
+ * @request tag screen removed granted When a screen is removed, `request::screen`
+ * is called on all screen tags to try to relocate them.
*/
/** This signal is emitted when the list of available screens changes.
diff --git a/objects/tag.c b/objects/tag.c
index 729cfe299..f4747071b 100644
--- a/objects/tag.c
+++ b/objects/tag.c
@@ -203,8 +203,39 @@
#include "ewmh.h"
#include "luaa.h"
-/**
+/** When a tag requests to be selected.
* @signal request::select
+ * @tparam string context The reason why it was called.
+ * @request tag select ewmh granted When the client request to be moved to a
+ * specific virtual desktop. AwesomeWM interprets virtual desktop as indexed
+ * tags.
+ */
+
+/**
+ * This signal is emitted to fill the list of default layouts.
+ *
+ * It is emitted on the global `tag` class rather than individual tag objects.
+ * The default handler is part of `rc.lua`. New modules can also use this signal
+ * to dynamically add new layouts to the list of default layouts.
+ *
+ * @signal request::default_layouts
+ * @tparam string context The context (currently always "startup").
+ * @classsignal
+ * @request tag default_layouts startup granted When AwesomeWM starts, it queries
+ * for default layout using this request.
+ * @see awful.layout.layouts
+ * @see awful.layout.append_default_layout
+ * @see awful.layout.remove_default_layout
+ */
+
+/** This signals is emitted when a tag needs layouts for the first time.
+ *
+ * If no handler implement it, it will fallback to the content added by
+ * `request::default_layouts`
+ *
+ * @signal request::layouts
+ * @tparam string context The context (currently always "awful").
+ * @tparam table hints A, currently empty, table with hints.
*/
/** When a client gets tagged with this tag.
diff --git a/objects/window.c b/objects/window.c
index 623cdbb6e..440970f3d 100644
--- a/objects/window.c
+++ b/objects/window.c
@@ -530,15 +530,15 @@ window_class_setup(lua_State *L)
NULL,
(lua_class_propfunc_t) luaA_window_get_window,
NULL);
- luaA_class_add_property(&window_class, "opacity",
+ luaA_class_add_property(&window_class, "_opacity",
(lua_class_propfunc_t) luaA_window_set_opacity,
(lua_class_propfunc_t) luaA_window_get_opacity,
(lua_class_propfunc_t) luaA_window_set_opacity);
- luaA_class_add_property(&window_class, "border_color",
+ luaA_class_add_property(&window_class, "_border_color",
(lua_class_propfunc_t) luaA_window_set_border_color,
(lua_class_propfunc_t) luaA_window_get_border_color,
(lua_class_propfunc_t) luaA_window_set_border_color);
- luaA_class_add_property(&window_class, "border_width",
+ luaA_class_add_property(&window_class, "_border_width",
(lua_class_propfunc_t) luaA_window_set_border_width,
(lua_class_propfunc_t) luaA_window_get_border_width,
(lua_class_propfunc_t) luaA_window_set_border_width);
diff --git a/property.c b/property.c
index a585ce915..7dee8eb2a 100644
--- a/property.c
+++ b/property.c
@@ -199,6 +199,7 @@ property_update_wm_hints(client_t *c, xcb_get_property_cookie_t cookie)
luaA_object_push(L, c);
+ /*TODO v5: Add a context */
lua_pushboolean(L, xcb_icccm_wm_hints_get_urgency(&wmh));
luaA_object_emit_signal(L, -2, "request::urgent", 1);
diff --git a/spec/awful/ewmh_spec.lua b/spec/awful/permissions_spec.lua
similarity index 68%
rename from spec/awful/ewmh_spec.lua
rename to spec/awful/permissions_spec.lua
index 058b77781..fb58f3f93 100644
--- a/spec/awful/ewmh_spec.lua
+++ b/spec/awful/permissions_spec.lua
@@ -1,4 +1,4 @@
-describe("awful.ewmh.client_geometry_requests", function()
+describe("awful.permissions.client_geometry_requests", function()
package.loaded["awful.client"] = {}
package.loaded["awful.layout"] = {}
package.loaded["awful.screen"] = {}
@@ -11,36 +11,39 @@ describe("awful.ewmh.client_geometry_requests", function()
_G.screen = {
connect_signal = function() end,
}
+ _G.tag = {
+ connect_signal = function() end,
+ }
- local ewmh = require("awful.ewmh")
+ local permissions = require("awful.permissions")
it("removes x/y/width/height when immobilized", function()
local c = {}
local s = stub.new(c, "geometry")
- ewmh.client_geometry_requests(c, "ewmh", {})
+ permissions.client_geometry_requests(c, "ewmh", {})
assert.stub(s).was_called_with(c, {})
- ewmh.client_geometry_requests(c, "ewmh", {x=0, width=400})
+ permissions.client_geometry_requests(c, "ewmh", {x=0, width=400})
assert.stub(s).was_called_with(c, {x=0, width=400})
c.immobilized_horizontal = true
c.immobilized_vertical = false
- ewmh.client_geometry_requests(c, "ewmh", {x=0, width=400})
+ permissions.client_geometry_requests(c, "ewmh", {x=0, width=400})
assert.stub(s).was_called_with(c, {})
- ewmh.client_geometry_requests(c, "ewmh", {x=0, width=400, y=0})
+ permissions.client_geometry_requests(c, "ewmh", {x=0, width=400, y=0})
assert.stub(s).was_called_with(c, {y=0})
c.immobilized_horizontal = true
c.immobilized_vertical = true
- ewmh.client_geometry_requests(c, "ewmh", {x=0, width=400, y=0})
+ permissions.client_geometry_requests(c, "ewmh", {x=0, width=400, y=0})
assert.stub(s).was_called_with(c, {})
c.immobilized_horizontal = false
c.immobilized_vertical = true
local hints = {x=0, width=400, y=0}
- ewmh.client_geometry_requests(c, "ewmh", hints)
+ permissions.client_geometry_requests(c, "ewmh", hints)
assert.stub(s).was_called_with(c, {x=0, width=400})
-- Table passed as argument should not have been modified.
assert.is.same(hints, {x=0, width=400, y=0})
diff --git a/tests/examples/awful/placement/no_offscreen.lua b/tests/examples/awful/placement/no_offscreen.lua
index c7ac1038f..4b4054ef0 100644
--- a/tests/examples/awful/placement/no_offscreen.lua
+++ b/tests/examples/awful/placement/no_offscreen.lua
@@ -1,6 +1,9 @@
--DOC_GEN_OUTPUT --DOC_GEN_IMAGE --DOC_HIDE
local awful = {placement = require("awful.placement")} --DOC_HIDE
+--DOC_HIDE no_offscreen is auto-called when startup is true, avoid this.
+awesome.startup = false -- luacheck: globals awesome.startup --DOC_HIDE
+
local c = client.gen_fake {x = -30, y = -30, width= 100, height=100} --DOC_HIDE
print("Before:", "x="..c.x..", y="..c.y..", width="..c.width..", height="..c.height) --DOC_HIDE
diff --git a/tests/examples/sequences/client/fullscreen.lua b/tests/examples/sequences/client/fullscreen.lua
index 3ac6744b6..c7eea1d16 100644
--- a/tests/examples/sequences/client/fullscreen.lua
+++ b/tests/examples/sequences/client/fullscreen.lua
@@ -2,7 +2,7 @@
local module = ... --DOC_HIDE
local awful = {tag = require("awful.tag"), layout = require("awful.layout")} --DOC_HIDE
awful.placement = require("awful.placement") --DOC_HIDE
-require("awful.ewmh") --DOC_HIDE
+require("awful.permissions") --DOC_HIDE
screen[1]:fake_resize(0, 0, 1024/2, 768/2) --DOC_HIDE
screen.fake_add(1034/2, 0, 1024/2, 768/2).outputs = {["eVGA1"] = {mm_height=60/2, mm_width=80/2 }} --DOC_HIDE
screen.fake_add(2074/2, 0, 1024/2, 768/2).outputs = {["DVI1" ] = {mm_height=60/2, mm_width=80/2 }} --DOC_HIDE
diff --git a/tests/examples/sequences/client/maximized.lua b/tests/examples/sequences/client/maximized.lua
index c6370605d..7a778f2bd 100644
--- a/tests/examples/sequences/client/maximized.lua
+++ b/tests/examples/sequences/client/maximized.lua
@@ -2,7 +2,7 @@
local module = ... --DOC_HIDE
local awful = {tag = require("awful.tag"), layout = require("awful.layout")} --DOC_HIDE
awful.placement = require("awful.placement") --DOC_HIDE
-require("awful.ewmh") --DOC_HIDE
+require("awful.permissions") --DOC_HIDE
screen[1]:fake_resize(0, 0, 1024/2, 768/2) --DOC_HIDE
screen.fake_add(1034/2, 0, 1024/2, 768/2).outputs = {["eVGA1"] = {mm_height=60/2, mm_width=80/2 }} --DOC_HIDE
screen.fake_add(2074/2, 0, 1024/2, 768/2).outputs = {["DVI1" ] = {mm_height=60/2, mm_width=80/2 }} --DOC_HIDE
diff --git a/tests/examples/sequences/client/maximized_horizontal.lua b/tests/examples/sequences/client/maximized_horizontal.lua
index fe642fab2..a655de52b 100644
--- a/tests/examples/sequences/client/maximized_horizontal.lua
+++ b/tests/examples/sequences/client/maximized_horizontal.lua
@@ -2,7 +2,7 @@
local module = ... --DOC_HIDE
local awful = {tag = require("awful.tag"), layout = require("awful.layout")} --DOC_HIDE
awful.placement = require("awful.placement") --DOC_HIDE
-require("awful.ewmh") --DOC_HIDE
+require("awful.permissions") --DOC_HIDE
screen[1]:fake_resize(0, 0, 1024/2, 768/2) --DOC_HIDE
screen.fake_add(1034/2, 0, 1024/2, 768/2).outputs = {["eVGA1"] = {mm_height=60/2, mm_width=80/2 }} --DOC_HIDE
screen.fake_add(2074/2, 0, 1024/2, 768/2).outputs = {["DVI1" ] = {mm_height=60/2, mm_width=80/2 }} --DOC_HIDE
diff --git a/tests/examples/sequences/client/maximized_vertical.lua b/tests/examples/sequences/client/maximized_vertical.lua
index 8573eaa2f..8a15a6c02 100644
--- a/tests/examples/sequences/client/maximized_vertical.lua
+++ b/tests/examples/sequences/client/maximized_vertical.lua
@@ -2,7 +2,7 @@
local module = ... --DOC_HIDE
local awful = {tag = require("awful.tag"), layout = require("awful.layout")} --DOC_HIDE
awful.placement = require("awful.placement") --DOC_HIDE
-require("awful.ewmh") --DOC_HIDE
+require("awful.permissions") --DOC_HIDE
screen[1]:fake_resize(0, 0, 1024/2, 768/2) --DOC_HIDE
screen.fake_add(1034/2, 0, 1024/2, 768/2).outputs = {["eVGA1"] = {mm_height=60/2, mm_width=80/2 }} --DOC_HIDE
screen.fake_add(2074/2, 0, 1024/2, 768/2).outputs = {["DVI1" ] = {mm_height=60/2, mm_width=80/2 }} --DOC_HIDE
diff --git a/tests/examples/shims/client.lua b/tests/examples/shims/client.lua
index f2067131f..f554b76bd 100644
--- a/tests/examples/shims/client.lua
+++ b/tests/examples/shims/client.lua
@@ -70,7 +70,7 @@ function client.gen_fake(args)
ret.type = "normal"
ret.valid = true
ret.size_hints = {}
- ret.border_width = 1
+ ret._border_width = 1
ret.icon_sizes = {{16,16}}
ret.name = "Example Client"
ret._private._struts = { top = 0, right = 0, left = 0, bottom = 0 }
@@ -287,7 +287,11 @@ function client.gen_fake(args)
end
})
+ client.emit_signal("request::manage", ret)
+
+ --TODO v6 remove this.
client.emit_signal("manage", ret)
+
assert(not args.screen or (args.screen == ret.screen))
return ret
diff --git a/tests/examples/shims/drawin.lua b/tests/examples/shims/drawin.lua
index 61ca495c8..8eb5d1bfe 100644
--- a/tests/examples/shims/drawin.lua
+++ b/tests/examples/shims/drawin.lua
@@ -15,7 +15,7 @@ local function new_drawin(_, args)
ret.y=0
ret.width=1
ret.height=1
- ret.border_width=0
+ ret._border_width=0
ret.ontop = false
ret.below = false
ret.above = false
diff --git a/tests/examples/text/awful/layout/remove.lua b/tests/examples/text/awful/layout/remove.lua
new file mode 100644
index 000000000..500f52435
--- /dev/null
+++ b/tests/examples/text/awful/layout/remove.lua
@@ -0,0 +1,23 @@
+--DOC_GEN_OUTPUT --DOC_HIDE
+local awful = { layout = require("awful.layout"), --DOC_HIDE
+ suit= require("awful.layout.suit")} --DOC_HIDE
+
+awful.layout.append_default_layouts({
+ awful.layout.suit.floating,
+ awful.layout.suit.tile,
+ awful.layout.suit.max,
+})
+
+for _, l in ipairs(awful.layout.layouts) do
+ print("Before:", l.name)
+end
+
+--DOC_NEWLINE
+
+awful.layout.remove_default_layout(awful.layout.suit.tile)
+
+--DOC_NEWLINE
+
+for _, l in ipairs(awful.layout.layouts) do
+ print("After:", l.name)
+end
diff --git a/tests/examples/text/awful/layout/remove.output.txt b/tests/examples/text/awful/layout/remove.output.txt
new file mode 100644
index 000000000..40d6558c0
--- /dev/null
+++ b/tests/examples/text/awful/layout/remove.output.txt
@@ -0,0 +1,5 @@
+Before: floating
+Before: tile
+Before: max
+After: floating
+After: max
diff --git a/tests/test-awesomerc.lua b/tests/test-awesomerc.lua
index 07326e520..bf3601206 100644
--- a/tests/test-awesomerc.lua
+++ b/tests/test-awesomerc.lua
@@ -4,6 +4,7 @@ local hotkeys_widget = require("awful.hotkeys_popup").widget
-- luacheck: globals modkey
local old_c = nil
+local called = false
-- Get a tag and a client
@@ -322,7 +323,50 @@ local steps = {
end
end
end,
+ -- Test the `c:activate{}` keybindings.
+ function()
+ client.connect_signal("request::activate", function()
+ called = true
+ end)
+ old_c = client.focus
+
+ root.fake_input("key_press", "Super_L")
+ awful.placement.centered(mouse, {parent=old_c})
+ root.fake_input("button_press",1)
+ root.fake_input("button_release",1)
+ root.fake_input("key_release", "Super_L")
+
+ return true
+ end,
+ function()
+ if not called then return end
+
+ client.focus = nil
+ called = false
+ root.fake_input("key_press", "Super_L")
+ root.fake_input("button_press",1)
+ root.fake_input("button_release",1)
+ root.fake_input("key_release", "Super_L")
+ return true
+ end,
+ -- Test resize.
+ function()
+ if not called then return end
+
+ called = false
+ root.fake_input("key_press", "Super_L")
+ root.fake_input("button_press",3)
+ root.fake_input("button_release",3)
+ root.fake_input("key_release", "Super_L")
+
+ return true
+ end,
+ function()
+ if not called then return end
+
+ return true
+ end
}
require("_runner").run_steps(steps)
diff --git a/tests/test-awful-client.lua b/tests/test-awful-client.lua
index 92c423ff4..e69a7ca03 100644
--- a/tests/test-awful-client.lua
+++ b/tests/test-awful-client.lua
@@ -133,7 +133,7 @@ table.insert(steps, function()
c1, c2 = client.get()[1], client.get()[2]
-- This should still be the case
- assert(client.focus == c1)
+ assert(c1.active)
c2:emit_signal("request::activate", "i_said_so")
@@ -143,11 +143,11 @@ end)
-- Check if writing a focus stealing filter works.
table.insert(steps, function()
-- This should still be the case
- assert(client.focus == c2)
+ assert(c2.active)
- original_count = #awful.ewmh.generic_activate_filters
+ original_count = #awful.permissions.generic_activate_filters
- awful.ewmh.add_activate_filter(function(c)
+ awful.permissions.add_activate_filter(function(c)
if c == c1 then return false end
end)
@@ -158,20 +158,20 @@ end)
table.insert(steps, function()
-- The request should have been denied
- assert(client.focus == c2)
+ assert(c2.active)
-- Test the remove function
- awful.ewmh.remove_activate_filter(function() end)
+ awful.permissions.remove_activate_filter(function() end)
- awful.ewmh.add_activate_filter(awful.ewmh.generic_activate_filters[1])
+ awful.permissions.add_activate_filter(awful.permissions.generic_activate_filters[1])
- awful.ewmh.remove_activate_filter(awful.ewmh.generic_activate_filters[1])
+ awful.permissions.remove_activate_filter(awful.permissions.generic_activate_filters[1])
- assert(original_count == #awful.ewmh.generic_activate_filters)
+ assert(original_count == #awful.permissions.generic_activate_filters)
c1:emit_signal("request::activate", "i_said_so")
- return client.focus == c1
+ return c1.active
end)
local has_error
diff --git a/tests/test-awful-placement.lua b/tests/test-awful-placement.lua
index 7761b3798..6aedf95c3 100644
--- a/tests/test-awful-placement.lua
+++ b/tests/test-awful-placement.lua
@@ -1,6 +1,4 @@
local awful = require("awful")
-local gears = require("gears")
-local beautiful = require("beautiful")
local test_client = require("_client")
local runner = require("_runner")
@@ -14,8 +12,9 @@ end
local tests = {}
-local tb_height = gears.math.round(beautiful.get_font_height() * 1.5)
-local border_width = beautiful.border_width
+-- Set it to something different than the default to make sure it doesn't change
+-- due to some request::border.
+local border_width = 3
local class = "test-awful-placement"
local rule = {
@@ -24,6 +23,7 @@ local rule = {
},
properties = {
floating = true,
+ border_width = border_width,
placement = awful.placement.no_overlap + awful.placement.no_offscreen
}
}
@@ -40,7 +40,7 @@ end
local function default_test(c, geometry)
check_geometry(c, geometry.expected_x, geometry.expected_y,
geometry.expected_width or geometry.width,
- geometry.expected_height or (geometry.height + tb_height))
+ geometry.expected_height or (geometry.height))
return true
end
@@ -242,7 +242,7 @@ for _, tag_num in ipairs{1, 2, 3} do
width = wa.width - 50,
height = 100,
expected_x = wa.x,
- expected_y = wa.y + tb_height + 2*border_width + 100
+ expected_y = wa.y + 2*border_width + 100
}
end
}
@@ -258,7 +258,7 @@ for _, tag_num in ipairs{1, 2, 3} do
width = wa.width - 10,
height = wa.height - 50,
expected_x = wa.x,
- expected_y = wa.y + 50 - 2*border_width - tb_height
+ expected_y = (wa.y + wa.height) - (wa.height - 50 + 2*border_width)
}
end
}
@@ -270,7 +270,7 @@ for _, tag_num in ipairs{1, 2, 3} do
return {
width = wa.width - 10,
height = wa.height - 50,
- expected_x = wa.x + 10 - 2*border_width,
+ expected_x = (wa.x + wa.width) - (wa.width - 10 + 2*border_width),
expected_y = wa.y
}
end
@@ -283,8 +283,8 @@ for _, tag_num in ipairs{1, 2, 3} do
return {
width = wa.width - 10,
height = wa.height - 50,
- expected_x = wa.x + 10 - 2*border_width,
- expected_y = wa.y + 50 - 2*border_width - tb_height
+ expected_x = (wa.x + wa.width ) - (wa.width - 10 + 2*border_width),
+ expected_y = (wa.y + wa.height) - (wa.height - 50 + 2*border_width)
}
end
}
diff --git a/tests/test-awful-rules.lua b/tests/test-awful-rules.lua
index db89234d6..2482363da 100644
--- a/tests/test-awful-rules.lua
+++ b/tests/test-awful-rules.lua
@@ -1,6 +1,5 @@
local awful = require("awful")
local gears = require("gears")
-local beautiful = require("beautiful")
local test_client = require("_client")
local unpack = unpack or table.unpack -- luacheck: globals unpack (compatibility with Lua 5.1)
@@ -10,10 +9,9 @@ local message_printed = false
-- Magic table to store tests
local tests = {}
-local tb_height = gears.math.round(beautiful.get_font_height() * 1.5)
-- local border_width = beautiful.border_width
--- Detect "manage" race conditions
+-- Detect "request::manage" race conditions
local real_apply = awful.rules.apply
function awful.rules.apply(c)
assert(#c:tags() == 0)
@@ -74,7 +72,7 @@ test_rule {
-- The size should not have changed
local geo = get_client_by_class(class):geometry()
- assert(geo.width == 100 and geo.height == 100+tb_height)
+ assert(geo.width == 100 and geo.height == 100)
return true
end
diff --git a/tests/test-awful-tag.lua b/tests/test-awful-tag.lua
index 688ddd48b..91ad2b443 100644
--- a/tests/test-awful-tag.lua
+++ b/tests/test-awful-tag.lua
@@ -1,5 +1,6 @@
local awful = require("awful")
local gtable = require("gears.table")
+local gdebug = require("gears.debug")
local beautiful = require("beautiful")
local function check_order()
@@ -64,7 +65,7 @@ local steps = {
client.focus = client.get()[1]
local c = client.focus
- assert(c and client.focus == c)
+ assert(c and c.active)
assert(beautiful.awesome_icon)
local t = awful.tag.add("Test", {clients={c}, icon = beautiful.awesome_icon})
@@ -196,6 +197,26 @@ local steps = {
return true
end,
+
+ -- Test adding and removing layouts.
+ function()
+ local t = mouse.screen.tags[9]
+ local count = #t.layouts
+ t:append_layout(awful.layout.suit.floating)
+ assert(#t.layouts == count + 1)
+
+ t:append_layouts({
+ awful.layout.suit.floating,
+ awful.layout.suit.floating,
+ })
+
+ assert(#t.layouts == count + 3)
+
+ t:remove_layout(awful.layout.suit.floating)
+ assert(#t.layouts == count)
+
+ return true
+ end
}
local multi_screen_steps = {}
@@ -313,6 +334,27 @@ local ms = require("_multi_screen")
ms.disable_wibox()
ms(steps, multi_screen_steps)
+-- Check deprecation.
+table.insert(steps, function()
+ assert(#awful.layout.layouts > 0)
+
+ local called1, called2 = false, false
+ gdebug.deprecate = function() called1 = true end
+ gdebug.print_warning = function() called2 = true end
+
+ awful.layout.layouts = {}
+
+ assert(called2)
+ assert(not called1)
+ assert(#awful.layout.layouts == 0)
+
+ -- Test the random property setter.
+ awful.layout.foo = "bar"
+ assert(awful.layout.foo == "bar")
+
+ return true
+end)
+
require("_runner").run_steps(steps)
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
diff --git a/tests/test-current-desktop.lua b/tests/test-current-desktop.lua
index c47a98478..bb6c6aa8b 100644
--- a/tests/test-current-desktop.lua
+++ b/tests/test-current-desktop.lua
@@ -76,7 +76,7 @@ local steps = {
-- Killing the client means the first selected tag counts
function(count)
if count == 1 then
- assert(client.focus == c)
+ assert(c.active)
c:kill()
c = nil
return
diff --git a/tests/test-focus.lua b/tests/test-focus.lua
index e7fa09123..6b5191f0d 100644
--- a/tests/test-focus.lua
+++ b/tests/test-focus.lua
@@ -3,10 +3,11 @@
local runner = require("_runner")
local awful = require("awful")
+local gdebug = require("gears.debug")
local beautiful = require("beautiful")
-beautiful.border_normal = "#0000ff"
-beautiful.border_focus = "#00ff00"
+beautiful.border_color_normal = "#0000ff"
+beautiful.border_color_active = "#00ff00"
client.connect_signal("focus", function(c)
c.border_color = "#ff0000"
@@ -64,8 +65,36 @@ local steps = {
test("#123456")
test("#12345678")
test("#123456ff", "#123456")
+
return true
- end
+ end,
+
+ function()
+ assert(client.focus)
+ local called, called2 = false, false
+ gdebug.print_warning = function() called = true end
+ local c2 = client.focus
+
+ client.focus.active = false
+ assert(called)
+ assert(not client.focus)
+ assert(not c2.active)
+ called = false
+
+ local real_assert = assert
+ assert = function() called2 = true end --luacheck: globals assert
+
+ c2.active = true
+
+ assert = real_assert --luacheck: globals assert
+
+ assert(called2)
+ assert(not called)
+ assert(c2.active)
+ assert(client.focus == c2)
+
+ return true
+ end,
}
runner.run_steps(steps)
diff --git a/tests/test-geometry.lua b/tests/test-geometry.lua
index c90d1b9d5..a97491a88 100644
--- a/tests/test-geometry.lua
+++ b/tests/test-geometry.lua
@@ -17,7 +17,7 @@ awful.rules.rules = {
y = 0,
width = 100,
height = 100,
- border_color = beautiful.border_normal
+ border_color = beautiful.border_color_normal
}
}
}
@@ -103,7 +103,7 @@ local steps = {
function()
local c = client.get()[1]
- assert(not pcall(function() c.border_width = -2000 end))
+ assert(not pcall(function() c._border_width = -2000 end))
assert(c.border_width==0)
c.border_width = 125
diff --git a/tests/test-leak-client.lua b/tests/test-leak-client.lua
index ca989b231..12b520e45 100644
--- a/tests/test-leak-client.lua
+++ b/tests/test-leak-client.lua
@@ -52,7 +52,7 @@ local function create_titlebar(c)
end
-- "Enable" titlebars (so that the titlebar can prevent garbage collection)
-client.connect_signal("manage", function (c)
+client.connect_signal("request::manage", function (c)
create_titlebar(c)
end)
diff --git a/tests/test-maximize.lua b/tests/test-maximize.lua
index f3f249e8e..8f28b25d1 100644
--- a/tests/test-maximize.lua
+++ b/tests/test-maximize.lua
@@ -358,8 +358,8 @@ gears.table.merge(steps, {
-- Remove the default handler and replace it with a testing one.
-- **WARNING**: add tests **BEFORE** this function if you want them
-- to be relevant.
- client.disconnect_signal("request::geometry", awful.ewmh.geometry)
- client.disconnect_signal("request::geometry", awful.ewmh.merge_maximization)
+ client.disconnect_signal("request::geometry", awful.permissions.geometry)
+ client.disconnect_signal("request::geometry", awful.permissions.merge_maximization)
client.connect_signal("request::geometry", geometry_handler)
test_client(nil,nil,nil,nil,nil,{maximize_after=true})
diff --git a/tests/test-spawn-snid.lua b/tests/test-spawn-snid.lua
index 1a4530701..6e08c2f2d 100644
--- a/tests/test-spawn-snid.lua
+++ b/tests/test-spawn-snid.lua
@@ -5,7 +5,7 @@ local test_client = require("_client")
local manage_called, c_snid
-client.connect_signal("manage", function(c)
+client.connect_signal("request::manage", function(c)
manage_called = true
c_snid = c.startup_id
assert(c.machine == awesome.hostname,
diff --git a/tests/test-urgent.lua b/tests/test-urgent.lua
index f0558097d..169c2512c 100644
--- a/tests/test-urgent.lua
+++ b/tests/test-urgent.lua
@@ -16,7 +16,7 @@ client.connect_signal("property::urgent", function (c)
end)
local manage_cb_done
-client.connect_signal("manage", function (c)
+client.connect_signal("request::manage", function (c)
manage_cb_done = true
assert(c.class == "XTerm", "Client should be xterm!")
end)
@@ -56,7 +56,7 @@ local steps = {
assert(#client.get() == 1)
local c = client.get()[1]
assert(not c.urgent, "Client is not urgent anymore.")
- assert(c == client.focus, "Client is focused.")
+ assert(c.active, "Client is focused.")
assert(awful.tag.getproperty(awful.screen.focused().tags[2], "urgent") == false)
assert(awful.tag.getproperty(awful.screen.focused().tags[2], "urgent_count") == 0)
return true
diff --git a/themes/default/theme.lua b/themes/default/theme.lua
index b0be48012..fd9f9c43c 100644
--- a/themes/default/theme.lua
+++ b/themes/default/theme.lua
@@ -24,11 +24,11 @@ theme.fg_focus = "#ffffff"
theme.fg_urgent = "#ffffff"
theme.fg_minimize = "#ffffff"
-theme.useless_gap = dpi(0)
-theme.border_width = dpi(1)
-theme.border_normal = "#000000"
-theme.border_focus = "#535d6c"
-theme.border_marked = "#91231c"
+theme.useless_gap = dpi(0)
+theme.border_width = dpi(1)
+theme.border_color_normal = "#000000"
+theme.border_color_active = "#535d6c"
+theme.border_marked = "#91231c"
-- There are other variable sets
-- overriding the default one when
diff --git a/themes/gtk/theme.lua b/themes/gtk/theme.lua
index ca31be8f0..392791ff3 100644
--- a/themes/gtk/theme.lua
+++ b/themes/gtk/theme.lua
@@ -120,9 +120,9 @@ theme.fg_minimize = mix(theme.wibar_fg, theme.wibar_bg, 0.9)
theme.bg_systray = theme.wibar_bg
-theme.border_normal = theme.gtk.wm_border_unfocused_color
-theme.border_focus = theme.gtk.wm_border_focused_color
-theme.border_marked = theme.gtk.success_color
+theme.border_color_normal = theme.gtk.wm_border_unfocused_color
+theme.border_color_active = theme.gtk.wm_border_focused_color
+theme.border_color_marked = theme.gtk.success_color
theme.border_width = dpi(theme.gtk.button_border_width or 1)
theme.border_radius = theme.gtk.button_border_radius
diff --git a/themes/sky/theme.lua b/themes/sky/theme.lua
index 0aa7d1bd6..b724bad89 100644
--- a/themes/sky/theme.lua
+++ b/themes/sky/theme.lua
@@ -27,9 +27,9 @@ theme.fg_minimize = "#2e3436"
theme.useless_gap = dpi(0)
theme.border_width = dpi(2)
-theme.border_normal = "#dae3e0"
-theme.border_focus = "#729fcf"
-theme.border_marked = "#eeeeec"
+theme.border_color_normal = "#dae3e0"
+theme.border_color_active = "#729fcf"
+theme.border_color_marked = "#eeeeec"
-- IMAGES
theme.layout_fairh = themes_path .. "sky/layouts/fairh.png"
diff --git a/themes/xresources/theme.lua b/themes/xresources/theme.lua
index e11a83c9e..4168bcc07 100644
--- a/themes/xresources/theme.lua
+++ b/themes/xresources/theme.lua
@@ -29,9 +29,9 @@ theme.fg_minimize = theme.bg_normal
theme.useless_gap = dpi(3)
theme.border_width = dpi(2)
-theme.border_normal = xrdb.color0
-theme.border_focus = theme.bg_focus
-theme.border_marked = xrdb.color10
+theme.border_color_normal = xrdb.color0
+theme.border_color_active = theme.bg_focus
+theme.border_color_marked = xrdb.color10
-- There are other variable sets
-- overriding the default one when
diff --git a/themes/zenburn/theme.lua b/themes/zenburn/theme.lua
index 2b6fa08a8..ead4081b2 100644
--- a/themes/zenburn/theme.lua
+++ b/themes/zenburn/theme.lua
@@ -27,9 +27,9 @@ theme.bg_systray = theme.bg_normal
-- {{{ Borders
theme.useless_gap = dpi(0)
theme.border_width = dpi(2)
-theme.border_normal = "#3F3F3F"
-theme.border_focus = "#6F6F6F"
-theme.border_marked = "#CC9393"
+theme.border_color_normal = "#3F3F3F"
+theme.border_color_active = "#6F6F6F"
+theme.border_color_marked = "#CC9393"
-- }}}
-- {{{ Titlebars