2008-09-29 16:49:18 +02:00
|
|
|
---------------------------------------------------------------------------
|
|
|
|
-- @author Julien Danjou <julien@danjou.info>
|
|
|
|
-- @copyright 2008 Julien Danjou
|
|
|
|
-- @release @AWESOME_VERSION@
|
|
|
|
---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
-- Grab environment we need
|
|
|
|
local util = require("awful.util")
|
2008-11-15 09:53:21 +01:00
|
|
|
local tag = require("awful.tag")
|
2008-09-29 16:49:18 +02:00
|
|
|
local pairs = pairs
|
2009-05-18 16:42:03 +02:00
|
|
|
local type = type
|
2008-09-29 16:49:18 +02:00
|
|
|
local ipairs = ipairs
|
|
|
|
local table = table
|
|
|
|
local math = math
|
2008-11-03 23:15:00 +01:00
|
|
|
local setmetatable = setmetatable
|
2008-09-29 16:49:18 +02:00
|
|
|
local capi =
|
|
|
|
{
|
|
|
|
client = client,
|
|
|
|
mouse = mouse,
|
|
|
|
screen = screen,
|
|
|
|
}
|
|
|
|
|
2009-08-28 15:31:16 +02:00
|
|
|
--- Useful client manipulation functions.
|
2008-09-29 16:49:18 +02:00
|
|
|
module("awful.client")
|
|
|
|
|
|
|
|
-- Private data
|
2009-04-11 14:01:30 +02:00
|
|
|
data = {}
|
2008-09-29 16:49:18 +02:00
|
|
|
data.focus = {}
|
|
|
|
data.urgent = {}
|
|
|
|
data.marked = {}
|
2009-04-11 14:01:30 +02:00
|
|
|
data.properties = setmetatable({}, { __mode = 'k' })
|
2008-09-29 16:49:18 +02:00
|
|
|
|
2009-02-07 15:12:57 +01:00
|
|
|
-- Functions
|
2008-09-29 16:49:18 +02:00
|
|
|
urgent = {}
|
|
|
|
focus = {}
|
|
|
|
focus.history = {}
|
2008-10-24 20:31:18 +02:00
|
|
|
swap = {}
|
2008-12-01 16:26:41 +01:00
|
|
|
floating = {}
|
2009-03-26 22:22:05 +01:00
|
|
|
dockable = {}
|
2009-02-07 15:12:57 +01:00
|
|
|
property = {}
|
2008-09-29 16:49:18 +02:00
|
|
|
|
|
|
|
--- Get the first client that got the urgent hint.
|
|
|
|
-- @return The first urgent client.
|
|
|
|
function urgent.get()
|
|
|
|
if #data.urgent > 0 then
|
2008-11-15 09:53:21 +01:00
|
|
|
return data.urgent[1]
|
2008-09-29 16:49:18 +02:00
|
|
|
else
|
|
|
|
-- fallback behaviour: iterate through clients and get the first urgent
|
|
|
|
local clients = capi.client.get()
|
|
|
|
for k, 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 urgent.jumpto()
|
|
|
|
local c = urgent.get()
|
|
|
|
if c then
|
|
|
|
local s = capi.client.focus and capi.client.focus.screen or capi.mouse.screen
|
|
|
|
-- focus the screen
|
|
|
|
if s ~= c.screen then
|
|
|
|
capi.mouse.screen = c.screen
|
|
|
|
end
|
2009-03-28 09:25:19 +01:00
|
|
|
-- focus the tag only if the client is not sticky
|
|
|
|
if not c.sticky then
|
|
|
|
tag.viewonly(c:tags()[1])
|
|
|
|
end
|
2008-09-29 16:49:18 +02:00
|
|
|
-- focus the client
|
|
|
|
capi.client.focus = c
|
|
|
|
c:raise()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
--- Adds client to urgent stack.
|
|
|
|
-- @param c The client object.
|
2009-08-21 21:15:19 +02:00
|
|
|
-- @param prop The property which is updated.
|
|
|
|
function urgent.add(c, prop)
|
|
|
|
if type(c) == "client" and prop == "urgent" and c.urgent then
|
2008-09-29 16:49:18 +02:00
|
|
|
table.insert(data.urgent, c)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
--- Remove client from urgent stack.
|
|
|
|
-- @param c The client object.
|
|
|
|
function urgent.delete(c)
|
|
|
|
for k, cl in ipairs(data.urgent) do
|
|
|
|
if c == cl then
|
|
|
|
table.remove(data.urgent, k)
|
|
|
|
break
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
--- Remove a client from the focus history
|
|
|
|
-- @param c The client that must be removed.
|
|
|
|
function focus.history.delete(c)
|
|
|
|
for k, v in ipairs(data.focus) do
|
|
|
|
if v == c then
|
|
|
|
table.remove(data.focus, k)
|
|
|
|
break
|
|
|
|
end
|
|
|
|
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.
|
|
|
|
-- @param c A client.
|
|
|
|
-- @return The same client if it's ok, nil otherwise.
|
|
|
|
function focus.filter(c)
|
|
|
|
if c.type == "desktop"
|
|
|
|
or c.type == "dock"
|
2010-08-12 13:18:40 +02:00
|
|
|
or c.type == "splash"
|
|
|
|
or not c.focusable then
|
2008-09-29 16:49:18 +02:00
|
|
|
return nil
|
|
|
|
end
|
|
|
|
return c
|
|
|
|
end
|
|
|
|
|
|
|
|
--- Update client focus history.
|
|
|
|
-- @param c The client that has been focused.
|
|
|
|
function focus.history.add(c)
|
|
|
|
if focus.filter(c) then
|
|
|
|
-- Remove the client if its in stack
|
|
|
|
focus.history.delete(c)
|
|
|
|
-- Record the client has latest focused
|
|
|
|
table.insert(data.focus, 1, c)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
--- Get the latest focused client for a screen in history.
|
|
|
|
-- @param screen The screen number to look for.
|
|
|
|
-- @param idx The index: 0 will return first candidate,
|
|
|
|
-- 1 will return second, etc.
|
|
|
|
-- @return A client.
|
|
|
|
function focus.history.get(screen, idx)
|
|
|
|
-- When this counter is equal to idx, we return the client
|
|
|
|
local counter = 0
|
|
|
|
local vc = visible(screen)
|
|
|
|
for k, c in ipairs(data.focus) do
|
|
|
|
if c.screen == screen then
|
|
|
|
for j, 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
|
|
|
|
-- Argh nobody found in history, give the first one visible if there is one
|
2008-12-29 11:51:46 +01:00
|
|
|
-- that passes the filter.
|
2008-09-29 16:49:18 +02:00
|
|
|
if counter == 0 then
|
2008-12-29 11:51:46 +01:00
|
|
|
for k, v in ipairs(vc) do
|
|
|
|
if focus.filter(v) then
|
|
|
|
return v
|
|
|
|
end
|
|
|
|
end
|
2008-09-29 16:49:18 +02:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
--- Focus the previous client in history.
|
|
|
|
function focus.history.previous()
|
|
|
|
local sel = capi.client.focus
|
|
|
|
local s
|
|
|
|
if sel then
|
|
|
|
s = sel.screen
|
|
|
|
else
|
|
|
|
s = capi.mouse.screen
|
|
|
|
end
|
|
|
|
local c = focus.history.get(s, 1)
|
|
|
|
if c then capi.client.focus = c end
|
|
|
|
end
|
|
|
|
|
|
|
|
--- Get visible clients from a screen.
|
2008-11-21 15:35:56 +01:00
|
|
|
-- @param screen The screen number, or nil for all screens.
|
2008-09-29 16:49:18 +02:00
|
|
|
-- @return A table with all visible clients.
|
|
|
|
function visible(screen)
|
|
|
|
local cls = capi.client.get(screen)
|
|
|
|
local vcls = {}
|
|
|
|
for k, c in pairs(cls) do
|
|
|
|
if c:isvisible() then
|
|
|
|
table.insert(vcls, c)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return vcls
|
|
|
|
end
|
|
|
|
|
2008-11-25 16:40:41 +01:00
|
|
|
--- Get visible and tiled clients
|
|
|
|
-- @param screen The screen number, or nil for all screens.
|
|
|
|
-- @return A tabl with all visible and tiled clients.
|
|
|
|
function tiled(screen)
|
|
|
|
local clients = visible(screen)
|
|
|
|
local tclients = {}
|
|
|
|
-- Remove floating clients
|
|
|
|
for k, c in pairs(clients) do
|
2008-12-01 16:26:41 +01:00
|
|
|
if not floating.get(c) then
|
2008-11-25 16:40:41 +01:00
|
|
|
table.insert(tclients, c)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return tclients
|
|
|
|
end
|
|
|
|
|
2008-09-29 16:49:18 +02:00
|
|
|
--- Get a client by its relative index to the focused window.
|
|
|
|
-- @usage Set i to 1 to get next, -1 to get previous.
|
|
|
|
-- @param i The index.
|
|
|
|
-- @param c Optional client.
|
|
|
|
-- @return A client, or nil if no client is available.
|
|
|
|
function next(i, c)
|
|
|
|
-- Get currently focused client
|
|
|
|
local sel = c or capi.client.focus
|
|
|
|
if sel then
|
|
|
|
-- Get all visible clients
|
|
|
|
local cls = visible(sel.screen)
|
2008-11-27 11:18:35 +01:00
|
|
|
local fcls = {}
|
|
|
|
-- Remove all non-normal clients
|
2008-09-29 16:49:18 +02:00
|
|
|
for idx, c in ipairs(cls) do
|
2008-12-29 11:54:10 +01:00
|
|
|
if focus.filter(c) or c == sel then
|
2008-11-27 11:18:35 +01:00
|
|
|
table.insert(fcls, c)
|
2008-09-29 16:49:18 +02:00
|
|
|
end
|
|
|
|
end
|
2008-11-27 11:18:35 +01:00
|
|
|
cls = fcls
|
2008-09-29 16:49:18 +02:00
|
|
|
-- Loop upon each client
|
|
|
|
for idx, c in ipairs(cls) do
|
|
|
|
if c == sel then
|
|
|
|
-- Cycle
|
|
|
|
return cls[util.cycle(#cls, idx + i)]
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2009-01-08 12:21:47 +01:00
|
|
|
-- Return true whether client B is in the right direction
|
2008-09-29 16:49:18 +02:00
|
|
|
-- compared to client A.
|
|
|
|
-- @param dir The direction.
|
|
|
|
-- @param cA The first client.
|
|
|
|
-- @param cB The second client.
|
|
|
|
-- @return True if B is in the direction of A.
|
|
|
|
local function is_in_direction(dir, cA, cB)
|
2009-08-28 15:31:16 +02:00
|
|
|
local gA = cA:geometry()
|
|
|
|
local gB = cB:geometry()
|
2008-09-29 16:49:18 +02:00
|
|
|
if dir == "up" then
|
2009-08-28 15:31:16 +02:00
|
|
|
return gA.y > gB.y
|
2008-09-29 16:49:18 +02:00
|
|
|
elseif dir == "down" then
|
2009-08-28 15:31:16 +02:00
|
|
|
return gA.y < gB.y
|
2008-09-29 16:49:18 +02:00
|
|
|
elseif dir == "left" then
|
2009-08-28 15:31:16 +02:00
|
|
|
return gA.x > gB.x
|
2008-09-29 16:49:18 +02:00
|
|
|
elseif dir == "right" then
|
2009-08-28 15:31:16 +02:00
|
|
|
return gA.x < gB.x
|
2008-09-29 16:49:18 +02:00
|
|
|
end
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
|
2009-01-08 12:21:47 +01:00
|
|
|
-- Calculate distance between two points.
|
2008-09-29 16:49:18 +02:00
|
|
|
-- i.e: if we want to move to the right, we will take the right border
|
|
|
|
-- of the currently focused client and the left side of the checked client.
|
|
|
|
-- This avoid the focus of an upper client when you move to the right in a
|
|
|
|
-- tilebottom layout with nmaster=2 and 5 clients open, for instance.
|
|
|
|
-- @param dir The direction.
|
|
|
|
-- @param cA The first client.
|
|
|
|
-- @param cB The second client.
|
|
|
|
-- @return The distance between the clients.
|
|
|
|
local function calculate_distance(dir, cA, cB)
|
2009-08-28 15:31:16 +02:00
|
|
|
local gA = cA:geometry()
|
|
|
|
local gB = cB:geometry()
|
2008-09-29 16:49:18 +02:00
|
|
|
|
|
|
|
if dir == "up" then
|
2009-08-28 15:31:16 +02:00
|
|
|
gB.y = gB.y + gB.height
|
2008-09-29 16:49:18 +02:00
|
|
|
elseif dir == "down" then
|
2009-08-28 15:31:16 +02:00
|
|
|
gA.y = gA.y + gA.height
|
2008-09-29 16:49:18 +02:00
|
|
|
elseif dir == "left" then
|
2009-08-28 15:31:16 +02:00
|
|
|
gB.x = gB.x + gB.width
|
2008-09-29 16:49:18 +02:00
|
|
|
elseif dir == "right" then
|
2009-08-28 15:31:16 +02:00
|
|
|
gA.x = gA.x + gA.width
|
2008-09-29 16:49:18 +02:00
|
|
|
end
|
|
|
|
|
2009-08-28 15:31:16 +02:00
|
|
|
return math.sqrt(math.pow(gB.x - gA.x, 2) + math.pow(gB.y - gA.y, 2))
|
2008-09-29 16:49:18 +02:00
|
|
|
end
|
|
|
|
|
2009-08-28 15:31:16 +02:00
|
|
|
-- Get the nearest client in the given direction.
|
2008-09-29 16:49:18 +02:00
|
|
|
-- @param dir The direction, can be either "up", "down", "left" or "right".
|
2008-10-06 21:55:06 +02:00
|
|
|
-- @param c Optional client to get a client relative to. Else focussed is used.
|
|
|
|
local function get_client_in_direction(dir, c)
|
2008-09-29 16:49:18 +02:00
|
|
|
local sel = c or capi.client.focus
|
|
|
|
if sel then
|
2009-08-21 21:15:19 +02:00
|
|
|
local geometry = sel:geometry()
|
2008-09-29 16:49:18 +02:00
|
|
|
local dist, dist_min
|
|
|
|
local target = nil
|
|
|
|
local cls = visible(sel.screen)
|
|
|
|
|
|
|
|
-- We check each client.
|
|
|
|
for i, c in ipairs(cls) do
|
2008-10-21 15:31:52 +02:00
|
|
|
-- Check geometry to see if client is located in the right direction.
|
2009-09-03 15:38:02 +02:00
|
|
|
if is_in_direction(dir, sel, c) then
|
2008-09-29 16:49:18 +02:00
|
|
|
|
|
|
|
-- Calculate distance between focused client and checked client.
|
2009-09-03 15:38:02 +02:00
|
|
|
dist = calculate_distance(dir, sel, c)
|
2008-09-29 16:49:18 +02:00
|
|
|
|
|
|
|
-- If distance is shorter then keep the client.
|
|
|
|
if not target or dist < dist_min then
|
|
|
|
target = c
|
|
|
|
dist_min = dist
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2008-10-06 21:55:06 +02:00
|
|
|
return target
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
--- Focus a client by the given direction.
|
|
|
|
-- @param dir The direction, can be either "up", "down", "left" or "right".
|
|
|
|
-- @param c Optional client.
|
2008-10-24 20:31:18 +02:00
|
|
|
function focus.bydirection(dir, c)
|
2008-10-06 21:55:06 +02:00
|
|
|
local sel = c or capi.client.focus
|
|
|
|
if sel then
|
|
|
|
local target = get_client_in_direction(dir, sel)
|
|
|
|
|
2008-09-29 16:49:18 +02:00
|
|
|
-- If we found a client to focus, then do it.
|
|
|
|
if target then
|
|
|
|
capi.client.focus = target
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
--- Focus a client by its relative index.
|
|
|
|
-- @param i The index.
|
|
|
|
-- @param c Optional client.
|
|
|
|
function focus.byidx(i, c)
|
|
|
|
local target = next(i, c)
|
|
|
|
if target then
|
|
|
|
capi.client.focus = target
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2008-10-06 21:55:06 +02:00
|
|
|
--- Swap a client with another client in the given direction
|
|
|
|
-- @param dir The direction, can be either "up", "down", "left" or "right".
|
|
|
|
-- @param c Optional client.
|
2008-10-24 20:31:18 +02:00
|
|
|
function swap.bydirection(dir, c)
|
2008-10-06 21:55:06 +02:00
|
|
|
local sel = c or capi.client.focus
|
|
|
|
if sel then
|
|
|
|
local target = get_client_in_direction(dir, sel)
|
|
|
|
|
|
|
|
-- If we found a client to swap with, then go for it
|
|
|
|
if target then
|
|
|
|
target:swap(sel)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2008-09-29 16:49:18 +02:00
|
|
|
--- Swap a client by its relative index.
|
|
|
|
-- @param i The index.
|
|
|
|
-- @param c Optional client, otherwise focused one is used.
|
2008-11-03 23:15:00 +01:00
|
|
|
function swap.byidx(i, c)
|
2008-09-29 16:49:18 +02:00
|
|
|
local sel = c or capi.client.focus
|
|
|
|
local target = next(i, sel)
|
|
|
|
if target then
|
|
|
|
target:swap(sel)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2009-02-14 17:53:03 +01:00
|
|
|
--- Cycle clients.
|
|
|
|
-- @param clockwise True to cycle clients clockwise.
|
|
|
|
-- @param screen Optional screen where to cycle clients.
|
|
|
|
function cycle(clockwise, screen)
|
|
|
|
local screen = screen or capi.mouse.screen
|
|
|
|
local cls = visible(screen)
|
|
|
|
-- We can't rotate without at least 2 clients, buddy.
|
|
|
|
if #cls >= 2 then
|
|
|
|
local c = table.remove(cls, 1)
|
|
|
|
if clockwise then
|
|
|
|
for i = #cls, 1, -1 do
|
|
|
|
c:swap(cls[i])
|
|
|
|
end
|
|
|
|
else
|
|
|
|
for _, rc in pairs(cls) do
|
|
|
|
c:swap(rc)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2008-09-29 16:49:18 +02:00
|
|
|
--- Get the master window.
|
|
|
|
-- @param screen Optional screen number, otherwise screen mouse is used.
|
|
|
|
-- @return The master window.
|
2008-11-07 14:27:29 +01:00
|
|
|
function getmaster(screen)
|
2008-09-29 16:49:18 +02:00
|
|
|
local s = screen or capi.mouse.screen
|
|
|
|
return visible(s)[1]
|
|
|
|
end
|
|
|
|
|
2008-11-27 10:58:02 +01:00
|
|
|
--- Set the client as slave: put it at the end of other windows.
|
2008-09-29 16:49:18 +02:00
|
|
|
-- @param c The window to set as slave.
|
|
|
|
function setslave(c)
|
|
|
|
local cls = visible(c.screen)
|
|
|
|
for k, v in pairs(cls) do
|
|
|
|
c:swap(v)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
--- Move/resize a client relative to current coordinates.
|
|
|
|
-- @param x The relative x coordinate.
|
|
|
|
-- @param y The relative y coordinate.
|
|
|
|
-- @param w The relative width.
|
|
|
|
-- @param h The relative height.
|
|
|
|
-- @param c The optional client, otherwise focused one is used.
|
|
|
|
function moveresize(x, y, w, h, c)
|
2009-08-21 21:15:19 +02:00
|
|
|
local sel = c or capi.client.focus
|
|
|
|
local geometry = sel:geometry()
|
|
|
|
geometry['x'] = geometry['x'] + x
|
|
|
|
geometry['y'] = geometry['y'] + y
|
|
|
|
geometry['width'] = geometry['width'] + w
|
|
|
|
geometry['height'] = geometry['height'] + h
|
|
|
|
sel:geometry(geometry)
|
2008-09-29 16:49:18 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
--- Move a client to a tag.
|
|
|
|
-- @param target The tag to move the client to.
|
|
|
|
-- @param c Optional client to move, otherwise the focused one is used.
|
|
|
|
function movetotag(target, c)
|
|
|
|
local sel = c or capi.client.focus
|
2009-08-24 16:09:56 +02:00
|
|
|
if sel and target.screen then
|
|
|
|
-- Set client on the same screen as the tag.
|
|
|
|
sel.screen = target.screen
|
2008-09-29 16:49:18 +02:00
|
|
|
sel:tags({ target })
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
--- Toggle a tag on a client.
|
|
|
|
-- @param target The tag to toggle.
|
|
|
|
-- @param c Optional client to toggle, otherwise the focused one is used.
|
|
|
|
function toggletag(target, c)
|
|
|
|
local sel = c or capi.client.focus
|
|
|
|
-- Check that tag and client screen are identical
|
|
|
|
if sel and sel.screen == target.screen then
|
|
|
|
local tags = sel:tags()
|
2009-04-23 12:53:24 +02:00
|
|
|
local index = nil;
|
|
|
|
for i, v in ipairs(tags) do
|
|
|
|
if v == target then
|
|
|
|
index = i
|
|
|
|
break
|
|
|
|
end
|
|
|
|
end
|
|
|
|
if index then
|
2008-09-29 16:49:18 +02:00
|
|
|
-- If it's the only tag for the window, stop.
|
|
|
|
if #tags == 1 then return end
|
2009-04-23 12:53:24 +02:00
|
|
|
tags[index] = nil
|
2008-09-29 16:49:18 +02:00
|
|
|
else
|
2009-04-23 12:53:24 +02:00
|
|
|
tags[#tags + 1] = target
|
2008-09-29 16:49:18 +02:00
|
|
|
end
|
|
|
|
sel:tags(tags)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
--- Move a client to a screen. Default is next screen, cycling.
|
|
|
|
-- @param c The client to move.
|
|
|
|
-- @param s The screen number, default to current + 1.
|
|
|
|
function movetoscreen(c, s)
|
|
|
|
local sel = c or capi.client.focus
|
|
|
|
if sel then
|
|
|
|
local sc = capi.screen.count()
|
|
|
|
if not s then
|
|
|
|
s = sel.screen + 1
|
|
|
|
end
|
|
|
|
if s > sc then s = 1 elseif s < 1 then s = sc end
|
|
|
|
sel.screen = s
|
2008-10-21 15:31:52 +02:00
|
|
|
capi.mouse.coords(capi.screen[s].geometry)
|
2008-09-29 16:49:18 +02:00
|
|
|
capi.client.focus = sel
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
--- Mark a client, and then call 'marked' hook.
|
|
|
|
-- @param c The client to mark, the focused one if not specified.
|
|
|
|
-- @return True if the client has been marked. False if the client was already marked.
|
|
|
|
function mark(c)
|
|
|
|
local cl = c or capi.client.focus
|
|
|
|
if cl then
|
|
|
|
for k, v in pairs(data.marked) do
|
|
|
|
if cl == v then
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
table.insert(data.marked, cl)
|
|
|
|
|
|
|
|
-- Call callback
|
2009-08-11 15:27:31 +02:00
|
|
|
cl:emit_signal("marked")
|
2008-09-29 16:49:18 +02:00
|
|
|
return true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
--- Unmark a client and then call 'unmarked' hook.
|
|
|
|
-- @param c The client to unmark, or the focused one if not specified.
|
|
|
|
-- @return True if the client has been unmarked. False if the client was not marked.
|
|
|
|
function unmark(c)
|
|
|
|
local cl = c or capi.client.focus
|
|
|
|
|
|
|
|
for k, v in pairs(data.marked) do
|
|
|
|
if cl == v then
|
|
|
|
table.remove(data.marked, k)
|
2009-08-11 15:27:31 +02:00
|
|
|
cl:emit_signal("unmarked")
|
2008-09-29 16:49:18 +02:00
|
|
|
return true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
|
|
|
|
--- Check if a client is marked.
|
|
|
|
-- @param c The client to check, or the focused one otherwise.
|
|
|
|
function ismarked(c)
|
|
|
|
local cl = c or capi.client.focus
|
|
|
|
if cl then
|
|
|
|
for k, v in pairs(data.marked) do
|
|
|
|
if cl == v then
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
|
|
|
|
--- Toggle a client as marked.
|
|
|
|
-- @param c The client to toggle mark.
|
|
|
|
function togglemarked(c)
|
|
|
|
local cl = c or capi.client.focus
|
|
|
|
|
2008-10-29 09:39:06 +01:00
|
|
|
if not mark(c) then
|
|
|
|
unmark(c)
|
2008-09-29 16:49:18 +02:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
--- Return the marked clients and empty the marked table.
|
|
|
|
-- @return A table with all marked clients.
|
|
|
|
function getmarked()
|
|
|
|
for k, v in pairs(data.marked) do
|
2009-08-11 15:27:31 +02:00
|
|
|
v:emit_signal("unmarked")
|
2008-09-29 16:49:18 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
t = data.marked
|
|
|
|
data.marked = {}
|
|
|
|
return t
|
|
|
|
end
|
|
|
|
|
2009-01-14 14:55:56 +01:00
|
|
|
--- Set a client floating state, overriding auto-detection.
|
2008-12-01 16:26:41 +01:00
|
|
|
-- Floating client are not handled by tiling layouts.
|
|
|
|
-- @param c A client.
|
2009-03-26 22:58:17 +01:00
|
|
|
-- @param s True or false.
|
2008-12-01 16:26:41 +01:00
|
|
|
function floating.set(c, s)
|
|
|
|
local c = c or capi.client.focus
|
2009-02-07 15:19:37 +01:00
|
|
|
if c and property.get(c, "floating") ~= s then
|
|
|
|
property.set(c, "floating", s)
|
2009-03-12 20:11:52 +01:00
|
|
|
local screen = c.screen
|
|
|
|
if s == true then
|
2009-08-21 21:15:19 +02:00
|
|
|
c:geometry(property.get(c, "floating_geometry"))
|
2009-03-12 20:11:52 +01:00
|
|
|
end
|
|
|
|
c.screen = screen
|
2008-12-01 16:26:41 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2009-08-10 18:12:19 +02:00
|
|
|
local function store_floating_geometry(c)
|
|
|
|
if floating.get(c) then
|
2009-08-21 21:15:19 +02:00
|
|
|
property.set(c, "floating_geometry", c:geometry())
|
2009-03-12 20:11:52 +01:00
|
|
|
end
|
2009-08-10 18:12:19 +02:00
|
|
|
end
|
|
|
|
|
2009-09-22 15:56:58 +02:00
|
|
|
-- Store the initial client geometry.
|
2009-10-09 20:39:55 +02:00
|
|
|
capi.client.connect_signal("new", function(c)
|
2009-09-22 15:56:58 +02:00
|
|
|
local function store_init_geometry(c)
|
|
|
|
property.set(c, "floating_geometry", c:geometry())
|
2009-10-09 20:39:55 +02:00
|
|
|
c:disconnect_signal("property::geometry", store_init_geometry)
|
2009-09-22 15:56:58 +02:00
|
|
|
end
|
2009-10-09 20:39:55 +02:00
|
|
|
c:connect_signal("property::geometry", store_init_geometry)
|
2009-09-22 15:56:58 +02:00
|
|
|
end)
|
|
|
|
|
2009-10-09 20:39:55 +02:00
|
|
|
capi.client.connect_signal("manage", function(c)
|
|
|
|
c:connect_signal("property::geometry", store_floating_geometry)
|
2009-03-12 20:11:52 +01:00
|
|
|
end)
|
|
|
|
|
2008-12-09 14:56:01 +01:00
|
|
|
--- Return if a client has a fixe size or not.
|
|
|
|
-- @param c The client.
|
|
|
|
function isfixed(c)
|
2009-01-06 11:11:21 +01:00
|
|
|
local c = c or capi.client.focus
|
2008-12-09 14:56:01 +01:00
|
|
|
if not c then return end
|
|
|
|
local h = c.size_hints
|
2009-01-06 11:11:21 +01:00
|
|
|
if h.min_width and h.max_width
|
2008-12-09 14:56:01 +01:00
|
|
|
and h.max_height and h.min_height
|
|
|
|
and h.min_width > 0 and h.max_width > 0
|
|
|
|
and h.max_height > 0 and h.min_height > 0
|
|
|
|
and h.min_width == h.max_width
|
2009-01-06 11:11:21 +01:00
|
|
|
and h.min_height == h.max_height then
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
return false
|
2008-12-09 14:56:01 +01:00
|
|
|
end
|
|
|
|
|
2008-12-01 16:26:41 +01:00
|
|
|
--- Get a client floating state.
|
|
|
|
-- @param c A client.
|
|
|
|
-- @return True or false. Note that some windows might be floating even if you
|
|
|
|
-- did not set them manually. For example, windows with a type different than
|
|
|
|
-- normal.
|
|
|
|
function floating.get(c)
|
|
|
|
local c = c or capi.client.focus
|
|
|
|
if c then
|
2009-02-07 15:19:37 +01:00
|
|
|
local value = property.get(c, "floating")
|
|
|
|
if value ~= nil then
|
|
|
|
return value
|
2008-12-01 16:26:41 +01:00
|
|
|
end
|
2009-01-06 11:11:21 +01:00
|
|
|
if c.type ~= "normal"
|
2008-12-01 16:26:41 +01:00
|
|
|
or c.fullscreen
|
|
|
|
or c.maximized_vertical
|
2008-12-09 14:56:01 +01:00
|
|
|
or c.maximized_horizontal
|
2009-01-06 11:11:21 +01:00
|
|
|
or isfixed(c) then
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
return false
|
2008-12-01 16:26:41 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2009-01-14 14:55:56 +01:00
|
|
|
--- Toggle the floating state of a client between 'auto' and 'true'.
|
2008-12-01 16:26:41 +01:00
|
|
|
-- @param c A client.
|
|
|
|
function floating.toggle(c)
|
2009-02-24 11:08:22 +01:00
|
|
|
local c = c or capi.client.focus
|
2009-01-14 14:55:56 +01:00
|
|
|
-- If it has been set to floating
|
2009-02-07 15:19:37 +01:00
|
|
|
if property.get(c, "floating") then
|
2009-01-14 14:55:56 +01:00
|
|
|
floating.set(c, nil)
|
|
|
|
else
|
|
|
|
floating.set(c, true)
|
|
|
|
end
|
2008-12-01 16:26:41 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
--- Remove the floating information on a client.
|
|
|
|
-- @param c The client.
|
|
|
|
function floating.delete(c)
|
2009-02-08 02:09:33 +01:00
|
|
|
floating.set(c, nil)
|
2008-12-01 16:26:41 +01:00
|
|
|
end
|
|
|
|
|
2009-01-13 20:31:37 +01:00
|
|
|
-- Normalize a set of numbers to 1
|
|
|
|
-- @param set the set of numbers to normalize
|
|
|
|
-- @param num the number of numbers to normalize
|
|
|
|
local function normalize(set, num)
|
|
|
|
local num = num or #set
|
|
|
|
local total = 0
|
|
|
|
if num then
|
|
|
|
for i = 1,num do
|
|
|
|
total = total + set[i]
|
|
|
|
end
|
|
|
|
for i = 1,num do
|
|
|
|
set[i] = set[i] / total
|
|
|
|
end
|
|
|
|
else
|
|
|
|
for i,v in ipairs(set) do
|
|
|
|
total = total + v
|
|
|
|
end
|
|
|
|
|
|
|
|
for i,v in ipairs(set) do
|
|
|
|
set[i] = v / total
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
--- Calculate a client's column number, index in that column, and
|
|
|
|
-- number of visible clients in this column.
|
|
|
|
-- @param c the client
|
|
|
|
-- @return col the column number
|
|
|
|
-- @return idx index of the client in the column
|
|
|
|
-- @return num the number of visible clients in the column
|
|
|
|
local function idx(c)
|
|
|
|
local c = c or capi.client.focus
|
|
|
|
if not c then return end
|
|
|
|
|
|
|
|
local clients = tiled(c.screen)
|
|
|
|
local idx = nil
|
|
|
|
for k, cl in ipairs(clients) do
|
|
|
|
if cl == c then
|
|
|
|
idx = k
|
|
|
|
break
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2009-08-26 14:35:44 +02:00
|
|
|
local t = tag.selected(c.screen)
|
2009-01-13 20:31:37 +01:00
|
|
|
local nmaster = tag.getnmaster(t)
|
|
|
|
if idx <= nmaster then
|
|
|
|
return {idx = idx, col=0, num=nmaster}
|
|
|
|
end
|
|
|
|
local nother = #clients - nmaster
|
|
|
|
idx = idx - nmaster
|
|
|
|
|
|
|
|
-- rather than regenerate the column number we can calculate it
|
|
|
|
-- based on the how the tiling algorithm places clients we calculate
|
|
|
|
-- the column, we could easily use the for loop in the program but we can
|
|
|
|
-- calculate it.
|
|
|
|
local ncol = tag.getncol(t)
|
|
|
|
-- minimum number of clients per column
|
|
|
|
local percol = math.floor(nother / ncol)
|
|
|
|
-- number of columns with an extra client
|
|
|
|
local overcol = math.mod(nother, ncol)
|
|
|
|
-- number of columns filled with [percol] clients
|
|
|
|
local regcol = ncol - overcol
|
|
|
|
|
|
|
|
local col = math.floor( (idx - 1) / percol) + 1
|
|
|
|
if col > regcol then
|
|
|
|
-- col = math.floor( (idx - (percol*regcol) - 1) / (percol + 1) ) + regcol + 1
|
|
|
|
-- simplified
|
|
|
|
col = math.floor( (idx + regcol + percol) / (percol+1) )
|
|
|
|
-- calculate the index in the column
|
|
|
|
idx = idx - percol*regcol - (col - regcol - 1) * (percol+1)
|
|
|
|
percol = percol+1
|
|
|
|
else
|
|
|
|
idx = idx - percol*(col-1)
|
|
|
|
end
|
|
|
|
|
|
|
|
return {idx = idx, col=col, num=percol}
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
--- Set the window factor of a client
|
|
|
|
-- @param wfact the window factor value
|
|
|
|
-- @param c the client
|
|
|
|
function setwfact(wfact, c)
|
|
|
|
-- get the currently selected window
|
|
|
|
local c = c or capi.client.focus
|
|
|
|
if not c then return end
|
|
|
|
|
|
|
|
local t = tag.selected(c.screen)
|
|
|
|
local w = idx(c)
|
|
|
|
|
|
|
|
local cls = tiled(t.screen)
|
|
|
|
local nmaster = tag.getnmaster(t)
|
|
|
|
|
|
|
|
-- n is the number of windows currently visible for which we have to be concerned with the properties
|
|
|
|
local data = tag.getproperty(t, "windowfact") or {}
|
|
|
|
local colfact = data[w.col]
|
|
|
|
|
|
|
|
colfact[w.idx] = wfact
|
|
|
|
rest = 1-wfact
|
|
|
|
|
|
|
|
-- calculate the current denominator
|
|
|
|
local total = 0
|
|
|
|
for i = 1,w.num do
|
|
|
|
if i ~= w.idx then
|
|
|
|
total = total + colfact[i]
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
-- normalize the windows
|
|
|
|
for i = 1,w.num do
|
|
|
|
if i ~= w.idx then
|
|
|
|
colfact[i] = (colfact[i] * rest) / total
|
|
|
|
end
|
|
|
|
end
|
2009-07-14 20:15:18 +02:00
|
|
|
|
2009-08-11 15:27:31 +02:00
|
|
|
t:emit_signal("property::windowfact")
|
2009-01-13 20:31:37 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
--- Increment a client's window factor
|
|
|
|
-- @param add amount to increase the client's window
|
|
|
|
-- @param c the client
|
|
|
|
function incwfact(add, c)
|
|
|
|
local c = c or capi.client.focus
|
|
|
|
if not c then return end
|
|
|
|
|
|
|
|
local t = tag.selected(c.screen)
|
|
|
|
|
|
|
|
local w = idx(c)
|
|
|
|
|
|
|
|
local nmaster = tag.getnmaster(t)
|
|
|
|
local data = tag.getproperty(t, "windowfact") or {}
|
|
|
|
local colfact = data[w.col]
|
|
|
|
curr = colfact[w.idx] or 1
|
|
|
|
colfact[w.idx] = curr + add
|
|
|
|
|
|
|
|
-- keep our ratios normalized
|
|
|
|
normalize(colfact, w.num)
|
2009-07-14 20:15:18 +02:00
|
|
|
|
2009-08-11 15:27:31 +02:00
|
|
|
t:emit_signal("property::windowfact")
|
2009-01-13 20:31:37 +01:00
|
|
|
end
|
|
|
|
|
2009-03-26 22:22:05 +01:00
|
|
|
--- Get a client dockable state.
|
|
|
|
-- @param c A client.
|
|
|
|
-- @return True or false. Note that some windows might be dockable even if you
|
|
|
|
-- did not set them manually. For example, windows with a type "utility", "toolbar"
|
|
|
|
-- or "dock"
|
|
|
|
function dockable.get(c)
|
|
|
|
local value = property.get(c, "dockable")
|
|
|
|
|
|
|
|
-- Some sane defaults
|
|
|
|
if value == nil then
|
|
|
|
if (c.type == "utility" or c.type == "toolbar" or c.type == "dock") then
|
|
|
|
value = true
|
|
|
|
else
|
|
|
|
value = false
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
return value
|
|
|
|
end
|
|
|
|
|
|
|
|
--- Set a client dockable state, overriding auto-detection.
|
|
|
|
-- With this enabled you can dock windows by moving them from the center
|
|
|
|
-- to the edge of the workarea.
|
|
|
|
-- @param c A client.
|
|
|
|
-- @param value True or false.
|
|
|
|
function dockable.set(c, value)
|
|
|
|
property.set(c, "dockable", value)
|
|
|
|
end
|
|
|
|
|
2009-02-07 15:12:57 +01:00
|
|
|
--- Get a client property.
|
|
|
|
-- @param c The client.
|
|
|
|
-- @param prop The property name.
|
|
|
|
-- @return The property.
|
|
|
|
function property.get(c, prop)
|
|
|
|
if data.properties[c] then
|
|
|
|
return data.properties[c][prop]
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
--- Set a client property.
|
|
|
|
-- This properties are internal to awful. Some are used to move clients, etc.
|
|
|
|
-- @param c The client.
|
|
|
|
-- @param prop The property name.
|
|
|
|
-- @param value The value.
|
|
|
|
function property.set(c, prop, value)
|
|
|
|
if not data.properties[c] then
|
|
|
|
data.properties[c] = {}
|
|
|
|
end
|
|
|
|
data.properties[c][prop] = value
|
2009-08-26 15:22:46 +02:00
|
|
|
c:emit_signal("property::" .. prop)
|
2009-02-07 15:12:57 +01:00
|
|
|
end
|
|
|
|
|
2009-08-10 18:12:19 +02:00
|
|
|
-- Register standards signals
|
2009-10-09 20:39:55 +02:00
|
|
|
capi.client.connect_signal("focus", focus.history.add)
|
|
|
|
capi.client.connect_signal("unmanage", focus.history.delete)
|
2008-09-29 16:49:18 +02:00
|
|
|
|
2009-10-09 20:39:55 +02:00
|
|
|
capi.client.connect_signal("manage", function(c) c:connect_signal("property::urgent", urgent.add) end)
|
|
|
|
capi.client.connect_signal("focus", urgent.delete)
|
|
|
|
capi.client.connect_signal("unmanage", urgent.delete)
|
2008-09-29 16:49:18 +02:00
|
|
|
|
2009-10-09 20:39:55 +02:00
|
|
|
capi.client.connect_signal("unmanage", floating.delete)
|
2008-12-01 16:26:41 +01:00
|
|
|
|
2008-09-29 16:49:18 +02:00
|
|
|
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80
|