summaryrefslogtreecommitdiff
path: root/awesome/lib/awful/client
diff options
context:
space:
mode:
Diffstat (limited to 'awesome/lib/awful/client')
-rw-r--r--awesome/lib/awful/client/focus.lua215
-rw-r--r--awesome/lib/awful/client/shape.lua93
-rw-r--r--awesome/lib/awful/client/urgent.lua88
3 files changed, 396 insertions, 0 deletions
diff --git a/awesome/lib/awful/client/focus.lua b/awesome/lib/awful/client/focus.lua
new file mode 100644
index 0000000..a8b04f2
--- /dev/null
+++ b/awesome/lib/awful/client/focus.lua
@@ -0,0 +1,215 @@
+---------------------------------------------------------------------------
+--- Keep track of the focused clients.
+--
+-- @author Julien Danjou <julien@danjou.info>
+-- @copyright 2008 Julien Danjou
+-- @submodule client
+---------------------------------------------------------------------------
+local grect = require("gears.geometry").rectangle
+
+local capi =
+{
+ screen = screen,
+ client = client,
+}
+
+-- We use a metatable to prevent circular dependency loops.
+local screen
+do
+ screen = setmetatable({}, {
+ __index = function(_, k)
+ screen = require("awful.screen")
+ return screen[k]
+ end,
+ __newindex = error -- Just to be sure in case anything ever does this
+ })
+end
+
+local client
+do
+ client = setmetatable({}, {
+ __index = function(_, k)
+ client = require("awful.client")
+ return client[k]
+ end,
+ __newindex = error -- Just to be sure in case anything ever does this
+ })
+end
+
+local focus = {history = {list = {}}}
+
+local function get_screen(s)
+ return s and capi.screen[s]
+end
+
+--- Remove a client from the focus history
+--
+-- @client c The client that must be removed.
+-- @function awful.client.focus.history.delete
+function focus.history.delete(c)
+ for k, v in ipairs(focus.history.list) do
+ if v == c then
+ table.remove(focus.history.list, k)
+ break
+ end
+ end
+end
+
+--- Focus a client by its relative index.
+--
+-- @function awful.client.focus.byidx
+-- @param i The index.
+-- @client[opt] c The client.
+function focus.byidx(i, c)
+ local target = client.next(i, c)
+ if target then
+ target:emit_signal("request::activate", "client.focus.byidx",
+ {raise=true})
+ end
+end
+
+--- Filter out window that we do not want handled by focus.
+-- This usually means that desktop, dock and splash windows are
+-- not registered and cannot get focus.
+--
+-- @client c A client.
+-- @return The same client if it's ok, nil otherwise.
+-- @function awful.client.focus.filter
+function focus.filter(c)
+ if c.type == "desktop"
+ or c.type == "dock"
+ or c.type == "splash"
+ or not c.focusable then
+ return nil
+ end
+ return c
+end
+
+--- Update client focus history.
+--
+-- @client c The client that has been focused.
+-- @function awful.client.focus.history.add
+function focus.history.add(c)
+ -- Remove the client if its in stack
+ focus.history.delete(c)
+ -- Record the client has latest focused
+ table.insert(focus.history.list, 1, c)
+end
+
+--- Get the latest focused client for a screen in history.
+--
+-- @tparam int|screen s The screen to look for.
+-- @tparam int idx The index: 0 will return first candidate,
+-- 1 will return second, etc.
+-- @tparam function filter An optional filter. If no client is found in the
+-- first iteration, `awful.client.focus.filter` is used by default to get any
+-- client.
+-- @treturn client.object A client.
+-- @function awful.client.focus.history.get
+function focus.history.get(s, idx, filter)
+ s = get_screen(s)
+ -- When this counter is equal to idx, we return the client
+ local counter = 0
+ local vc = client.visible(s, true)
+ for _, c in ipairs(focus.history.list) do
+ if get_screen(c.screen) == s then
+ if not filter or filter(c) then
+ for _, vcc in ipairs(vc) do
+ if vcc == c then
+ if counter == idx then
+ return c
+ end
+ -- We found one, increment the counter only.
+ counter = counter + 1
+ break
+ end
+ end
+ end
+ end
+ end
+ -- Argh nobody found in history, give the first one visible if there is one
+ -- that passes the filter.
+ filter = filter or focus.filter
+ if counter == 0 then
+ for _, v in ipairs(vc) do
+ if filter(v) then
+ return v
+ end
+ end
+ end
+end
+
+--- Focus the previous client in history.
+-- @function awful.client.focus.history.previous
+function focus.history.previous()
+ local sel = capi.client.focus
+ local s = sel and sel.screen or screen.focused()
+ local c = focus.history.get(s, 1)
+ if c then
+ c:emit_signal("request::activate", "client.focus.history.previous",
+ {raise=false})
+ end
+end
+
+--- Focus a client by the given direction.
+--
+-- @tparam string dir The direction, can be either
+-- `"up"`, `"down"`, `"left"` or `"right"`.
+-- @client[opt] c The client.
+-- @tparam[opt=false] boolean stacked Use stacking order? (top to bottom)
+-- @function awful.client.focus.bydirection
+function focus.bydirection(dir, c, stacked)
+ local sel = c or capi.client.focus
+ if sel then
+ local cltbl = client.visible(sel.screen, stacked)
+ local geomtbl = {}
+ for i,cl in ipairs(cltbl) do
+ geomtbl[i] = cl:geometry()
+ end
+
+ local target = grect.get_in_direction(dir, geomtbl, sel:geometry())
+
+ -- If we found a client to focus, then do it.
+ if target then
+ cltbl[target]:emit_signal("request::activate",
+ "client.focus.bydirection", {raise=false})
+ end
+ end
+end
+
+--- Focus a client by the given direction. Moves across screens.
+--
+-- @param dir The direction, can be either "up", "down", "left" or "right".
+-- @client[opt] c The client.
+-- @tparam[opt=false] boolean stacked Use stacking order? (top to bottom)
+-- @function awful.client.focus.global_bydirection
+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())
+
+ -- change focus inside the screen
+ focus.bydirection(dir, sel)
+
+ -- if focus not changed, we must change screen
+ if sel == capi.client.focus then
+ screen.focus_bydirection(dir, scr)
+ if scr ~= get_screen(screen.focused()) then
+ local cltbl = client.visible(screen.focused(), stacked)
+ local geomtbl = {}
+ for i,cl in ipairs(cltbl) do
+ geomtbl[i] = cl:geometry()
+ end
+ local target = grect.get_in_direction(dir, geomtbl, scr.geometry)
+
+ if target then
+ cltbl[target]:emit_signal("request::activate",
+ "client.focus.global_bydirection",
+ {raise=false})
+ end
+ end
+ end
+end
+
+return focus
+
+-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
diff --git a/awesome/lib/awful/client/shape.lua b/awesome/lib/awful/client/shape.lua
new file mode 100644
index 0000000..e51d873
--- /dev/null
+++ b/awesome/lib/awful/client/shape.lua
@@ -0,0 +1,93 @@
+---------------------------------------------------------------------------
+--- Handle client shapes.
+--
+-- @author Uli Schlachter <psychon@znc.in>
+-- @copyright 2014 Uli Schlachter
+-- @submodule client
+---------------------------------------------------------------------------
+
+-- Grab environment we need
+local surface = require("gears.surface")
+local cairo = require("lgi").cairo
+local capi =
+{
+ client = client,
+}
+
+local shape = {}
+shape.update = {}
+
+--- Get one of a client's shapes and transform it to include window decorations.
+-- @function awful.shape.get_transformed
+-- @client c The client whose shape should be retrieved
+-- @tparam string shape_name Either "bounding" or "clip"
+function shape.get_transformed(c, shape_name)
+ local border = shape_name == "bounding" and c.border_width or 0
+ local shape_img = surface.load_silently(c["client_shape_" .. shape_name], false)
+ if not shape_img then return end
+
+ -- Get information about various sizes on the client
+ local geom = c:geometry()
+ local _, t = c:titlebar_top()
+ local _, b = c:titlebar_bottom()
+ local _, l = c:titlebar_left()
+ local _, r = c:titlebar_right()
+
+ -- Figure out the size of the shape that we need
+ local img_width = geom.width + 2*border
+ local img_height = geom.height + 2*border
+ local result = cairo.ImageSurface(cairo.Format.A1, img_width, img_height)
+ local cr = cairo.Context(result)
+
+ -- Fill everything (this paints the titlebars and border)
+ cr:paint()
+
+ -- Draw the client's shape in the middle
+ cr:set_operator(cairo.Operator.SOURCE)
+ cr:set_source_surface(shape_img, border + l, border + t)
+ cr:rectangle(border + l, border + t, geom.width - l - r, geom.height - t - b)
+ cr:fill()
+
+ return result
+end
+
+--- Update a client's bounding shape from the shape the client set itself.
+-- @function awful.shape.update.bounding
+-- @client c The client to act on
+function shape.update.bounding(c)
+ local res = shape.get_transformed(c, "bounding")
+ c.shape_bounding = res and res._native
+ -- Free memory
+ if res then
+ res:finish()
+ end
+end
+
+--- Update a client's clip shape from the shape the client set itself.
+-- @function awful.shape.update.clip
+-- @client c The client to act on
+function shape.update.clip(c)
+ local res = shape.get_transformed(c, "clip")
+ c.shape_clip = res and res._native
+ -- Free memory
+ if res then
+ res:finish()
+ end
+end
+
+--- Update all of a client's shapes from the shapes the client set itself.
+-- @function awful.shape.update.all
+-- @client c The client to act on
+function shape.update.all(c)
+ shape.update.bounding(c)
+ shape.update.clip(c)
+end
+
+capi.client.connect_signal("property::shape_client_bounding", shape.update.bounding)
+capi.client.connect_signal("property::shape_client_clip", shape.update.clip)
+capi.client.connect_signal("property::width", shape.update.all)
+capi.client.connect_signal("property::height", shape.update.all)
+
+return shape
+
+-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
diff --git a/awesome/lib/awful/client/urgent.lua b/awesome/lib/awful/client/urgent.lua
new file mode 100644
index 0000000..22f6a6d
--- /dev/null
+++ b/awesome/lib/awful/client/urgent.lua
@@ -0,0 +1,88 @@
+---------------------------------------------------------------------------
+--- Keep track of the urgent clients.
+--
+-- @author Julien Danjou <julien@danjou.info>
+-- @copyright 2008 Julien Danjou
+-- @submodule client
+---------------------------------------------------------------------------
+
+local urgent = {}
+
+local capi =
+{
+ client = client,
+}
+
+local client
+do
+ client = setmetatable({}, {
+ __index = function(_, k)
+ client = require("awful.client")
+ return client[k]
+ end,
+ __newindex = error -- Just to be sure in case anything ever does this
+ })
+end
+
+local data = setmetatable({}, { __mode = 'k' })
+
+--- Get the first client that got the urgent hint.
+--
+-- @function awful.urgent.get
+-- @treturn client.object The first urgent client.
+function urgent.get()
+ if #data > 0 then
+ return data[1]
+ else
+ -- fallback behaviour: iterate through clients and get the first urgent
+ local clients = capi.client.get()
+ for _, cl in pairs(clients) do
+ if cl.urgent then
+ return cl
+ end
+ end
+ end
+end
+
+--- Jump to the client that received the urgent hint first.
+--
+-- @function awful.urgent.jumpto
+-- @tparam bool|function merge If true then merge tags (select the client's
+-- first tag additionally) when the client is not visible.
+-- If it is a function, it will be called with the client as argument.
+function urgent.jumpto(merge)
+ local c = client.urgent.get()
+ if c then
+ c:jump_to(merge)
+ end
+end
+
+--- Adds client to urgent stack.
+--
+-- @function awful.urgent.add
+-- @client c The client object.
+-- @param prop The property which is updated.
+function urgent.add(c, prop)
+ if type(c) == "client" and prop == "urgent" and c.urgent then
+ table.insert(data, c)
+ end
+end
+
+--- Remove client from urgent stack.
+--
+-- @function awful.urgent.delete
+-- @client c The client object.
+function urgent.delete(c)
+ for k, cl in ipairs(data) do
+ if c == cl then
+ table.remove(data, k)
+ break
+ end
+ end
+end
+
+capi.client.connect_signal("property::urgent", urgent.add)
+capi.client.connect_signal("focus", urgent.delete)
+capi.client.connect_signal("unmanage", urgent.delete)
+
+return urgent