summaryrefslogtreecommitdiff
path: root/awesome/lib/awful/ewmh.lua
diff options
context:
space:
mode:
Diffstat (limited to 'awesome/lib/awful/ewmh.lua')
-rw-r--r--awesome/lib/awful/ewmh.lua295
1 files changed, 295 insertions, 0 deletions
diff --git a/awesome/lib/awful/ewmh.lua b/awesome/lib/awful/ewmh.lua
new file mode 100644
index 0000000..eb72a16
--- /dev/null
+++ b/awesome/lib/awful/ewmh.lua
@@ -0,0 +1,295 @@
+---------------------------------------------------------------------------
+--- Implements EWMH requests handling.
+--
+-- @author Julien Danjou <julien@danjou.info>
+-- @copyright 2009 Julien Danjou
+-- @module awful.ewmh
+---------------------------------------------------------------------------
+
+local client = client
+local screen = screen
+local ipairs = ipairs
+local util = require("awful.util")
+local aclient = require("awful.client")
+local aplace = require("awful.placement")
+local asuit = require("awful.layout.suit")
+
+local ewmh = {
+ generic_activate_filters = {},
+ contextual_activate_filters = {},
+}
+
+--- 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 geometry_change_lock = false
+local function geometry_change(window)
+ if geometry_change_lock then return end
+ geometry_change_lock = true
+
+ -- Fix up the geometry in case this window needs to cover the whole screen.
+ local bw = window.border_width or 0
+ local g = window.screen.workarea
+ if window.maximized_vertical then
+ window:geometry { height = g.height - 2*bw, y = g.y }
+ end
+ if window.maximized_horizontal then
+ window:geometry { width = g.width - 2*bw, x = g.x }
+ end
+ if window.fullscreen then
+ window.border_width = 0
+ window:geometry(window.screen.geometry)
+ end
+
+ geometry_change_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?
+function ewmh.activate(c, context, hints) -- luacheck: no unused args
+ hints = hints or {}
+
+ if c.focusable == false and not hints.force then 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
+
+ if ret ~= false and c:isvisible() then
+ client.focus = c
+ elseif ret == false and not hints.force then
+ return
+ end
+
+ if hints and hints.raise then
+ c:raise()
+ if not awesome.startup and not c:isvisible() then
+ c.urgent = true
+ end
+ 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 unless the `force`
+-- hint is set. If the callback returns `false`, the activation request is
+-- cancelled. 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, "ewmh")
+-- if c.class == "Firefox" then return false end
+-- end)
+--
+-- @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
+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
+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 then
+ c.screen = c.transient_for.screen
+ if not c.sticky then
+ c:tags(c.transient_for: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",
+ 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 wont 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 = util.table.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 correspond to a boolean property
+ local state = c[original_context]
+
+ -- If the property is boolean and it correspond to the undo operation,
+ -- restore the stored geometry.
+ if state == false then
+ aplace.restore(c,{context=context})
+ return
+ end
+
+ local honor_default = original_context ~= "fullscreen"
+
+ if props.honor_workarea == nil then
+ props.honor_workarea = honor_default
+ end
+
+ aplace[context](c, props)
+ end
+end
+
+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("property::border_width", geometry_change)
+client.connect_signal("property::geometry", geometry_change)
+screen.connect_signal("property::workarea", function(s)
+ for _, c in pairs(client.get(s)) do
+ geometry_change(c)
+ end
+end)
+
+return ewmh
+
+-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80