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