From 22d656903563f75678f3634964731ccf93355dfd Mon Sep 17 00:00:00 2001 From: ache Date: Mon, 13 Mar 2017 23:17:19 +0100 Subject: Init commit --- lib/awful/client/focus.lua | 215 ++++++++++++++++++++++++++++++++++++++++++++ lib/awful/client/shape.lua | 93 +++++++++++++++++++ lib/awful/client/urgent.lua | 88 ++++++++++++++++++ 3 files changed, 396 insertions(+) create mode 100644 lib/awful/client/focus.lua create mode 100644 lib/awful/client/shape.lua create mode 100644 lib/awful/client/urgent.lua (limited to 'lib/awful/client') diff --git a/lib/awful/client/focus.lua b/lib/awful/client/focus.lua new file mode 100644 index 0000000..a8b04f2 --- /dev/null +++ b/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/lib/awful/client/shape.lua b/lib/awful/client/shape.lua new file mode 100644 index 0000000..e51d873 --- /dev/null +++ b/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/lib/awful/client/urgent.lua b/lib/awful/client/urgent.lua new file mode 100644 index 0000000..22f6a6d --- /dev/null +++ b/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 -- cgit v1.2.3-70-g09d2