348 lines
14 KiB
Lua
348 lines
14 KiB
Lua
---------------------------------------------------------------------------
|
|
-- @author Julien Danjou <julien@danjou.info>
|
|
-- @copyright 2008 Julien Danjou
|
|
-- @release @AWESOME_VERSION@
|
|
---------------------------------------------------------------------------
|
|
|
|
-- Grab environment we need
|
|
local layout = require("awful.layout")
|
|
local tag = require("awful.tag")
|
|
local hooks = require("awful.hooks")
|
|
local aclient = require("awful.client")
|
|
local math = math
|
|
local ipairs = ipairs
|
|
local capi =
|
|
{
|
|
mouse = mouse,
|
|
screen = screen,
|
|
client = client,
|
|
mousegrabber = mousegrabber,
|
|
}
|
|
|
|
--- Mouse module for awful
|
|
module("awful.mouse")
|
|
|
|
client = {}
|
|
|
|
local function snap_outside(g, sg, snap)
|
|
if g.x < snap + sg.x + sg.width and g.x > sg.x + sg.width then
|
|
g.x = sg.x + sg.width
|
|
elseif g.x + g.width < sg.x and g.x + g.width > sg.x - snap then
|
|
g.x = sg.x - g.width
|
|
end
|
|
if g.y < snap + sg.y + sg.height and g.y > sg.y + sg.height then
|
|
g.y = sg.y + sg.height
|
|
elseif g.y + g.height < sg.y and g.y + g.height > sg.y - snap then
|
|
g.y = sg.y - g.height
|
|
end
|
|
return g
|
|
end
|
|
|
|
local function snap_inside(g, sg, snap)
|
|
if math.abs(g.x) < snap + sg.x and g.x > sg.x then
|
|
g.x = sg.x
|
|
elseif math.abs((sg.x + sg.width) - (g.x + g.width)) < snap then
|
|
g.x = sg.x + sg.width - g.width
|
|
end
|
|
if math.abs(g.y) < snap + sg.y and g.y > sg.y then
|
|
g.y = sg.y
|
|
elseif math.abs((sg.y + sg.height) - (g.y + g.height)) < snap then
|
|
g.y = sg.y + sg.height - g.height
|
|
end
|
|
return g
|
|
end
|
|
|
|
--- Snap a client to the closest client or screen edge.
|
|
-- @param c The client to snap.
|
|
-- @param snap The pixel to snap clients.
|
|
-- @param x The client x coordinate.
|
|
-- @param y The client y coordinate.
|
|
function client.snap(c, snap, x, y)
|
|
local snap = snap or 8
|
|
local c = c or client.focus
|
|
local geom = c:fullgeometry()
|
|
geom.x = x or geom.x
|
|
geom.y = y or geom.y
|
|
|
|
geom = snap_inside(geom, capi.screen[c.screen].geometry, snap)
|
|
geom = snap_inside(geom, capi.screen[c.screen].workarea, snap)
|
|
|
|
for k, snapper in ipairs(aclient.visible(c.screen)) do
|
|
if snapper ~= c then
|
|
geom = snap_outside(geom, snapper:fullgeometry(), snap)
|
|
end
|
|
end
|
|
return geom
|
|
end
|
|
|
|
--- Move a client.
|
|
-- @param c The client to move, or the focused one if nil.
|
|
-- @param snap The pixel to snap clients.
|
|
function client.move(c, snap)
|
|
local c = c or capi.client.focus
|
|
|
|
if not c then return end
|
|
|
|
if c.fullscreen
|
|
or c.type == "desktop"
|
|
or c.type == "splash"
|
|
or c.type == "dock" then
|
|
return
|
|
end
|
|
|
|
c:raise()
|
|
|
|
local orig = c:fullgeometry()
|
|
local m_c = capi.mouse.coords()
|
|
local dist_x = m_c.x - orig.x
|
|
local dist_y = m_c.y - orig.y
|
|
|
|
local function ug(c, prop)
|
|
if prop == "geometry" then
|
|
local g = c:fullgeometry()
|
|
capi.mouse.coords({ x = g.x + dist_x, y = g.y + dist_y })
|
|
end
|
|
end
|
|
|
|
capi.mousegrabber.run(function (mouse)
|
|
hooks.property.unregister(ug)
|
|
for k, v in ipairs(mouse.buttons) do
|
|
if v then
|
|
local lay = layout.get(c.screen)
|
|
if lay == layout.suit.floating or c.floating then
|
|
local x = mouse.x - dist_x
|
|
local y = mouse.y - dist_y
|
|
c:fullgeometry(client.snap(c, snap, x, y))
|
|
if layout.get(c.screen) ~= layout.suit.floating and not c.floating then
|
|
hooks.property.register(ug)
|
|
end
|
|
elseif lay ~= layout.suit.magnifier then
|
|
c.screen = capi.mouse.screen
|
|
if layout.get(c.screen) ~= layout.suit.floating then
|
|
local c_u_m = capi.mouse.client_under_pointer()
|
|
if c_u_m and not c_u_m.floating then
|
|
if c_u_m ~= c then
|
|
c:swap(c_u_m)
|
|
end
|
|
end
|
|
else
|
|
hooks.property.register(ug)
|
|
end
|
|
end
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end, "fleur")
|
|
end
|
|
|
|
--- Get a client corner coordinates.
|
|
-- @param c The client to get corner from, focused one by default.
|
|
-- @param corner The corner to use: auto, top_left, top_right, bottom_left,
|
|
-- bottom_right. Default is auto, and auto find the nearest corner.
|
|
-- @return Actual used corner and x and y coordinates.
|
|
function client.corner(c, corner)
|
|
local c = c or capi.client.focus
|
|
if not c then return end
|
|
|
|
local g = c:geometry()
|
|
|
|
if not corner or corner == "auto" then
|
|
local m_c = capi.mouse.coords()
|
|
if math.abs(g.y - m_c.y) < math.abs(g.y + g.height - m_c.y) then
|
|
if math.abs(g.x - m_c.x) < math.abs(g.x + g.width - m_c.x) then
|
|
corner = "top_left"
|
|
else
|
|
corner = "top_right"
|
|
end
|
|
else
|
|
if math.abs(g.x - m_c.x) < math.abs(g.x + g.width - m_c.x) then
|
|
corner = "bottom_left"
|
|
else
|
|
corner = "bottom_right"
|
|
end
|
|
end
|
|
end
|
|
|
|
local x, y
|
|
if corner == "top_right" then
|
|
x = g.x + g.width
|
|
y = g.y
|
|
elseif corner == "top_left" then
|
|
x = g.x
|
|
y = g.y
|
|
elseif corner == "bottom_left" then
|
|
x = g.x
|
|
y = g.y + g.height
|
|
else
|
|
x = g.x + g.width
|
|
y = g.y + g.height
|
|
end
|
|
|
|
return corner, x, y
|
|
end
|
|
|
|
local function client_resize_magnifier(c, corner)
|
|
local corner, x, y = client.corner(c, corner)
|
|
capi.mouse.coords({ x = x, y = y })
|
|
|
|
local wa = capi.screen[c.screen].workarea
|
|
local center_x = wa.x + wa.width / 2
|
|
local center_y = wa.y + wa.height / 2
|
|
local maxdist_pow = (wa.width^2 + wa.height^2) / 4
|
|
|
|
capi.mousegrabber.run(function (mouse)
|
|
for k, v in ipairs(mouse.buttons) do
|
|
if v then
|
|
local dx = center_x - mouse.x
|
|
local dy = center_y - mouse.y
|
|
local dist = dx^2 + dy^2
|
|
|
|
-- New master width factor
|
|
local mwfact = dist / maxdist_pow
|
|
tag.setmwfact(math.min(math.max(0.01, mwfact), 0.99), tag.selected(c.screen))
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end, corner .. "_corner")
|
|
end
|
|
|
|
local function client_resize_tiled(c, lay)
|
|
local wa = capi.screen[c.screen].workarea
|
|
local mwfact = tag.getmwfact()
|
|
local cursor
|
|
if lay == layout.suit.tile then
|
|
capi.mouse.coords({ x = wa.x + wa.width * mwfact })
|
|
cursor = "sb_h_double_arrow"
|
|
elseif lay == layout.suit.tile.left then
|
|
capi.mouse.coords({ x = wa.x + wa.width * (1 - mwfact) })
|
|
cursor = "sb_h_double_arrow"
|
|
elseif lay == layout.suit.tile.bottom then
|
|
capi.mouse.coords({ y = wa.y + wa.height * mwfact })
|
|
cursor = "sb_v_double_arrow"
|
|
else
|
|
capi.mouse.coords({ y = wa.y + wa.height * (1 - mwfact) })
|
|
cursor = "sb_v_double_arrow"
|
|
end
|
|
|
|
capi.mousegrabber.run(function (mouse)
|
|
for k, v in ipairs(mouse.buttons) do
|
|
if v then
|
|
local fact_x = (mouse.x - wa.x) / wa.width
|
|
local fact_y = (mouse.y - wa.y) / wa.height
|
|
local mwfact
|
|
|
|
if lay == layout.suit.tile then
|
|
mwfact = fact_x
|
|
elseif lay == layout.suit.tile.left then
|
|
mwfact = 1 - fact_x
|
|
elseif lay == layout.suit.tile.bottom then
|
|
mwfact = fact_y
|
|
else
|
|
mwfact = 1 - fact_y
|
|
end
|
|
|
|
tag.setmwfact(math.min(math.max(mwfact, 0.01), 0.99), tag.selected(c.screen))
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end, cursor)
|
|
end
|
|
|
|
local function client_resize_floating(c, corner)
|
|
local corner, x, y = client.corner(c, corner)
|
|
local fixed_x, fixed_y
|
|
local g = c:geometry()
|
|
|
|
-- Warp mouse pointer
|
|
capi.mouse.coords({ x = x, y = y })
|
|
|
|
local fixed_x, fixed_y
|
|
|
|
capi.mousegrabber.run(function (mouse)
|
|
for k, v in ipairs(mouse.buttons) do
|
|
if v then
|
|
-- Ignore screen changes
|
|
if not c.floating
|
|
and capi.mouse.screen ~= c.screen then
|
|
return true
|
|
end
|
|
|
|
local ng
|
|
if corner == "bottom_right" then
|
|
ng = { width = mouse.x - g.x,
|
|
height = mouse.y - g.y }
|
|
elseif corner == "bottom_left" then
|
|
ng = { x = mouse.x,
|
|
width = (g.x + g.width) - mouse.x,
|
|
height = mouse.y - g.y }
|
|
elseif corner == "top_left" then
|
|
ng = { x = mouse.x,
|
|
width = (g.x + g.width) - mouse.x,
|
|
y = mouse.y,
|
|
height = (g.y + g.height) - mouse.y }
|
|
else
|
|
ng = { width = mouse.x - g.x,
|
|
y = mouse.y,
|
|
height = (g.y + g.height) - mouse.y }
|
|
end
|
|
if ng.width <= 0 then ng.width = nil end
|
|
if ng.height <= 0 then ng.height = nil end
|
|
c:geometry({ width = ng.width, height = ng.height })
|
|
-- Get real geometry that has been applied
|
|
-- in case we honor size hints
|
|
-- XXX: This should be rewritten when size
|
|
-- hints are available from Lua.
|
|
local rg = c:geometry()
|
|
|
|
if corner == "bottom_right" then
|
|
ng = {}
|
|
elseif corner == "bottom_left" then
|
|
ng = { x = (g.x + g.width) - rg.width }
|
|
elseif corner == "top_left" then
|
|
ng = { x = (g.x + g.width) - rg.width,
|
|
y = (g.y + g.height) - rg.height }
|
|
else
|
|
ng = { y = (g.y + g.height) - rg.height }
|
|
end
|
|
c:geometry({ x = ng.x, y = ng.y })
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end, corner .. "_corner")
|
|
end
|
|
|
|
--- Resize a client.
|
|
-- @param c The client to resize, or the focused one by default.
|
|
-- @param corner The corner to grab on resize. Auto detected by default.
|
|
function client.resize(c, corner)
|
|
local c = c or capi.client.focus
|
|
|
|
if not c then return end
|
|
|
|
if c.fullscreen
|
|
or c.type == "desktop"
|
|
or c.type == "splash"
|
|
or c.type == "dock" then
|
|
return
|
|
end
|
|
|
|
local lay = layout.get(c.screen)
|
|
|
|
if lay == layout.suit.floating or c.floating then
|
|
return client_resize_floating(c, corner)
|
|
elseif lay == layout.suit.tile
|
|
or lay == layout.suit.tile.left
|
|
or lay == layout.suit.tile.top
|
|
or lay == layout.suit.tile.bottom then
|
|
return client_resize_tiled(c, lay)
|
|
elseif lay == layout.suit.magnifier then
|
|
return client_resize_magnifier(c, corner)
|
|
end
|
|
end
|
|
|
|
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80
|