diff --git a/awesomerc.lua b/awesomerc.lua index 733bc1840..578a9a37f 100644 --- a/awesomerc.lua +++ b/awesomerc.lua @@ -535,8 +535,3 @@ client.connect_signal("request::titlebars", function(c) } end) -- }}} - --- Enable sloppy focus, so that focus follows mouse. -client.connect_signal("mouse::enter", function(c) - c:activate { context = "mouse_enter", raise = false } -end) diff --git a/docs/config.ld b/docs/config.ld index c9686a040..6a1470a81 100644 --- a/docs/config.ld +++ b/docs/config.ld @@ -444,6 +444,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', diff --git a/lib/awful/autofocus.lua b/lib/awful/autofocus.lua index f3ea73c15..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("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) - --- 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/ewmh.lua b/lib/awful/ewmh.lua index b8a2d3b78..c39fb2426 100644 --- a/lib/awful/ewmh.lua +++ b/lib/awful/ewmh.lua @@ -18,6 +18,7 @@ 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 ewmh = { generic_activate_filters = {}, @@ -75,6 +76,60 @@ local function repair_geometry(window) 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. @@ -605,6 +660,32 @@ function ewmh.update_border(c, context) 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.ewmh.autoactivate +function ewmh.autoactivate(c, context, args) + if not pcommon.check("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", ewmh.autoactivate) client.connect_signal("request::border", ewmh.update_border) client.connect_signal("request::activate", ewmh.activate) client.connect_signal("request::tag", ewmh.tag) @@ -614,12 +695,27 @@ 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) +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 ewmh -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/lib/awful/permissions/_common.lua b/lib/awful/permissions/_common.lua new file mode 100644 index 000000000..435033603 --- /dev/null +++ b/lib/awful/permissions/_common.lua @@ -0,0 +1,56 @@ +-- 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(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 + + 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 + +return module diff --git a/objects/client.c b/objects/client.c index 1b10f8a85..6385d232e 100644 --- a/objects/client.c +++ b/objects/client.c @@ -266,6 +266,26 @@ * @tparam[opt=false] boolean hints.raise should the client be raised? */ +/** 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? + * + */ + /** * @signal request::geometry * @tparam client c The client diff --git a/spec/awful/ewmh_spec.lua b/spec/awful/ewmh_spec.lua index 058b77781..67c0328fa 100644 --- a/spec/awful/ewmh_spec.lua +++ b/spec/awful/ewmh_spec.lua @@ -11,6 +11,9 @@ describe("awful.ewmh.client_geometry_requests", function() _G.screen = { connect_signal = function() end, } + _G.tag = { + connect_signal = function() end, + } local ewmh = require("awful.ewmh")