441 lines
13 KiB
Lua
441 lines
13 KiB
Lua
---------------------------------------------------------------------------
|
|
--- Mouse module for awful
|
|
--
|
|
-- @author Julien Danjou <julien@danjou.info>
|
|
-- @copyright 2008 Julien Danjou
|
|
-- @module mouse
|
|
---------------------------------------------------------------------------
|
|
|
|
-- Grab environment we need
|
|
local floating = require("awful.layout.suit.floating")
|
|
local aplace = require("awful.placement")
|
|
local gdebug = require("gears.debug")
|
|
local type = type
|
|
local ipairs = ipairs
|
|
local capi =
|
|
{
|
|
root = root,
|
|
mouse = mouse,
|
|
screen = screen,
|
|
client = client,
|
|
mousegrabber = mousegrabber,
|
|
}
|
|
|
|
local mouse = {
|
|
snap = require("awful.mouse.snap"),
|
|
client = require("awful.mouse.client"),
|
|
resize = require("awful.mouse.resize"),
|
|
drag_to_tag = require("awful.mouse.drag_to_tag")
|
|
}
|
|
|
|
mouse.object = {}
|
|
mouse.wibox = {}
|
|
|
|
--- The default distance before snapping clients together.
|
|
--
|
|
-- @DOC_screen_client_snap_EXAMPLE@
|
|
--
|
|
-- @tfield integer awful.mouse.snap.default_distance
|
|
-- @tparam[opt=8] integer default_distance
|
|
-- @see awful.mouse.snap
|
|
|
|
--- The default distance before activating screen edge snap.
|
|
-- @tfield integer awful.mouse.snap.aerosnap_distance
|
|
-- @tparam[opt=16] integer aerosnap_distance
|
|
-- @see awful.mouse.snap
|
|
|
|
--- Enable screen edges snapping.
|
|
--
|
|
--@DOC_awful_placement_aero_snap_EXAMPLE@
|
|
--
|
|
-- @tfield[opt=true] boolean awful.mouse.snap.edge_enabled
|
|
-- @tparam boolean edge_enabled
|
|
|
|
--- Enable client to client snapping.
|
|
-- @tfield[opt=true] boolean awful.mouse.snap.client_enabled
|
|
-- @tparam boolean client_enabled
|
|
|
|
--- Enable changing tag when a client is dragged to the edge of the screen.
|
|
-- @tfield[opt=false] boolean awful.mouse.drag_to_tag.enabled
|
|
-- @tparam boolean enabled
|
|
|
|
--- The snap outline background color.
|
|
-- @beautiful beautiful.snap_bg
|
|
-- @tparam color|string|gradient|pattern color
|
|
|
|
--- The snap outline width.
|
|
-- @beautiful beautiful.snap_border_width
|
|
-- @tparam integer snap_border_width
|
|
|
|
--- The snap outline shape.
|
|
-- @beautiful beautiful.snap_shape
|
|
-- @tparam function shape A `gears.shape` compatible function
|
|
|
|
--- The gap between snapped clients.
|
|
-- @beautiful beautiful.snapper_gap
|
|
-- @tparam[opt=0] number snapper_gap
|
|
|
|
--- The resize cursor name.
|
|
-- @beautiful beautiful.cursor_mouse_resize
|
|
-- @tparam[opt="cross"] string cursor
|
|
|
|
--- The move cursor name.
|
|
-- @beautiful beautiful.cursor_mouse_move
|
|
-- @tparam[opt="fleur"] string cursor
|
|
|
|
--- Get the client object under the pointer.
|
|
-- @deprecated awful.mouse.client_under_pointer
|
|
-- @treturn client|nil The client object under the pointer, if one can be found.
|
|
-- @see current_client
|
|
function mouse.client_under_pointer()
|
|
gdebug.deprecate("Use mouse.current_client instead of awful.mouse.client_under_pointer()", {deprecated_in=4})
|
|
|
|
return mouse.object.get_current_client()
|
|
end
|
|
|
|
-- Older layouts implement their own mousegrabber.
|
|
-- @tparam client c The client
|
|
-- @tparam table args Additional arguments
|
|
-- @treturn boolean This return false when the resize need to be aborted
|
|
mouse.resize.add_enter_callback(function(c, args) --luacheck: no unused args
|
|
if c.floating then return end
|
|
|
|
local l = c.screen.selected_tag and c.screen.selected_tag.layout or nil
|
|
if l == floating then return end
|
|
|
|
if l.mouse_resize_handler then
|
|
capi.mousegrabber.stop()
|
|
|
|
local geo, corner = aplace.closest_corner(capi.mouse, {parent=c})
|
|
|
|
l.mouse_resize_handler(c, corner, geo.x, geo.y)
|
|
|
|
return false
|
|
end
|
|
end, "mouse.resize")
|
|
|
|
--- Get the client currently under the mouse cursor.
|
|
-- @property current_client
|
|
-- @tparam[opt=nil] client|nil current_client
|
|
-- @propertytype nil When the mouse cursor is not over a client.
|
|
|
|
function mouse.object.get_current_client()
|
|
local obj = capi.mouse.object_under_pointer()
|
|
if type(obj) == "client" then
|
|
return obj
|
|
end
|
|
end
|
|
|
|
--- Get the wibox currently under the mouse cursor.
|
|
-- @property current_wibox
|
|
-- @tparam[opt=nil] wibox|nil current_wibox
|
|
-- @propertytype nil When the mouse cursor is not over a wibox.
|
|
|
|
function mouse.object.get_current_wibox()
|
|
local obj = capi.mouse.object_under_pointer()
|
|
if type(obj) == "drawin" and obj.get_wibox then
|
|
return obj:get_wibox()
|
|
end
|
|
end
|
|
|
|
--- Get the widgets currently under the mouse cursor.
|
|
--
|
|
-- @property current_widgets
|
|
-- @tparam[opt=nil] nil|table current_widgets
|
|
-- @tablerowtype table The list of `wibox.widget`s. The first element is the biggest
|
|
-- container while the last is the topmost widget.
|
|
-- @propertytype nil When the mouse cursor is not over any widget.
|
|
|
|
function mouse.object.get_current_widgets()
|
|
local w = mouse.object.get_current_wibox()
|
|
if w then
|
|
local geo, coords = w:geometry(), capi.mouse:coords()
|
|
local bw = w.border_width
|
|
|
|
local list = w:find_widgets(coords.x - geo.x - bw, coords.y - geo.y - bw)
|
|
|
|
local ret = {}
|
|
|
|
for k, v in ipairs(list) do
|
|
ret[k] = v.widget
|
|
end
|
|
|
|
return ret, list
|
|
end
|
|
end
|
|
|
|
--- Get the topmost widget currently under the mouse cursor.
|
|
-- @property current_widget
|
|
-- @tparam[opt=nil] widget|nil current_widget
|
|
-- @propertytype nil When the mouse cursor is not over a widget.
|
|
-- @see current_widget_geometry
|
|
|
|
function mouse.object.get_current_widget()
|
|
local wdgs, geos = mouse.object.get_current_widgets()
|
|
|
|
if wdgs then
|
|
return wdgs[#wdgs], geos[#geos]
|
|
end
|
|
end
|
|
|
|
--- Get the current widget geometry.
|
|
-- @property current_widget_geometry
|
|
-- @tparam[opt=nil] table|nil current_widget_geometry
|
|
-- @tparam number current_widget_geometry.x
|
|
-- @tparam number current_widget_geometry.y
|
|
-- @tparam number current_widget_geometry.width
|
|
-- @tparam number current_widget_geometry.height
|
|
-- @propertytype nil When there is no current widgets.
|
|
-- @propertytype table
|
|
-- @see current_widget
|
|
|
|
function mouse.object.get_current_widget_geometry()
|
|
local _, ret = mouse.object.get_current_widget()
|
|
|
|
return ret
|
|
end
|
|
|
|
--- Get the current widget geometries.
|
|
-- @property current_widget_geometries
|
|
-- @tparam[opt=nil] table|nil current_widget_geometries A list of geometry tables.
|
|
-- @propertytype nil When there is no widgets.
|
|
-- @propertytype table A list of geometry tables.
|
|
-- @tablerowkey x integer
|
|
-- @tablerowkey y integer
|
|
-- @tablerowkey width integer
|
|
-- @tablerowkey height integer
|
|
-- @see current_widgets
|
|
|
|
function mouse.object.get_current_widget_geometries()
|
|
local _, ret = mouse.object.get_current_widgets()
|
|
|
|
return ret
|
|
end
|
|
|
|
--- Move the wibox under the cursor.
|
|
-- @staticfct awful.mouse.wibox.move
|
|
-- @tparam wibox w The wibox to move, or none to use that under the pointer
|
|
-- @noreturn
|
|
-- @request wibox geometry mouse.move granted Requests to move the wibox.
|
|
function mouse.wibox.move(w)
|
|
w = w or mouse.current_wibox
|
|
if not w then return end
|
|
|
|
if not w
|
|
or w.type == "desktop"
|
|
or w.type == "splash"
|
|
or w.type == "dock" then
|
|
return
|
|
end
|
|
|
|
-- Compute the offset
|
|
local coords = capi.mouse.coords()
|
|
local geo = aplace.centered(capi.mouse,{parent=w, pretend=true})
|
|
|
|
local offset = {
|
|
x = geo.x - coords.x,
|
|
y = geo.y - coords.y,
|
|
}
|
|
|
|
mouse.resize(w, "mouse.move", {
|
|
placement = aplace.under_mouse,
|
|
offset = offset
|
|
})
|
|
end
|
|
|
|
--- True if the left mouse button is pressed.
|
|
-- @property is_left_mouse_button_pressed
|
|
-- @tparam[opt=false] boolean is_left_mouse_button_pressed
|
|
|
|
--- True if the right mouse button is pressed.
|
|
-- @property is_right_mouse_button_pressed
|
|
-- @tparam[opt=false] boolean is_right_mouse_button_pressed
|
|
|
|
--- True if the middle mouse button is pressed.
|
|
-- @property is_middle_mouse_button_pressed
|
|
-- @tparam[opt=false] boolean is_middle_mouse_button_pressed
|
|
|
|
--- Add an `awful.button` based mousebinding to the global set.
|
|
--
|
|
-- A **global** mousebinding is one which is always present, even when there is
|
|
-- no focused client. If your intent is too add a mousebinding which acts on
|
|
-- the focused client do **not** use this.
|
|
--
|
|
-- @staticfct awful.mouse.append_global_mousebinding
|
|
-- @tparam awful.button button The button object.
|
|
-- @noreturn
|
|
-- @see awful.button
|
|
|
|
function mouse.append_global_mousebinding(button)
|
|
capi.root._append_button(button)
|
|
end
|
|
|
|
--- Add multiple `awful.button` based mousebindings to the global set.
|
|
--
|
|
-- A **global** mousebinding is one which is always present, even when there is
|
|
-- no focused client. If your intent is too add a mousebinding which acts on
|
|
-- the focused client do **not** use this
|
|
--
|
|
-- @staticfct awful.mouse.append_global_mousebindings
|
|
-- @tparam table buttons A table of `awful.button` objects. Optionally, it can have
|
|
-- a `group` entry. If set, the `group` property will be set on all `awful.buttons`
|
|
-- objects.
|
|
-- @noreturn
|
|
-- @see awful.button
|
|
|
|
function mouse.append_global_mousebindings(buttons)
|
|
local g = buttons.group
|
|
buttons.group = nil
|
|
|
|
-- Avoid the boilerplate. If the user is adding multiple buttons at once, then
|
|
-- they are probably related.
|
|
if g then
|
|
for _, k in ipairs(buttons) do
|
|
k.group = g
|
|
end
|
|
end
|
|
|
|
capi.root._append_buttons(buttons)
|
|
buttons.group = g
|
|
end
|
|
|
|
--- Remove a mousebinding from the global set.
|
|
--
|
|
-- @staticfct awful.mouse.remove_global_mousebinding
|
|
-- @tparam awful.button button The button object.
|
|
-- @noreturn
|
|
-- @see awful.button
|
|
|
|
function mouse.remove_global_mousebinding(button)
|
|
capi.root._remove_button(button)
|
|
end
|
|
|
|
local default_buttons = {}
|
|
|
|
--- Add an `awful.button` to the default client buttons.
|
|
--
|
|
-- @staticfct awful.mouse.append_client_mousebinding
|
|
-- @tparam awful.button button The button.
|
|
-- @noreturn
|
|
-- @emits client_mousebinding::added
|
|
-- @emitstparam client_mousebinding::added awful.button button The button.
|
|
-- @see awful.button
|
|
-- @see awful.keyboard.append_client_keybinding
|
|
|
|
function mouse.append_client_mousebinding(button)
|
|
table.insert(default_buttons, button)
|
|
|
|
for _, c in ipairs(capi.client.get(nil, false)) do
|
|
c:append_mousebinding(button)
|
|
end
|
|
|
|
capi.client.emit_signal("client_mousebinding::added", button)
|
|
end
|
|
|
|
--- Add a `awful.button`s to the default client buttons.
|
|
--
|
|
-- @staticfct awful.mouse.append_client_mousebindings
|
|
-- @tparam table buttons A table containing `awful.button` objects.
|
|
-- @noreturn
|
|
-- @see awful.button
|
|
-- @see awful.keyboard.append_client_keybinding
|
|
-- @see awful.mouse.append_client_mousebinding
|
|
-- @see awful.keyboard.append_client_keybindings
|
|
|
|
function mouse.append_client_mousebindings(buttons)
|
|
for _, button in ipairs(buttons) do
|
|
mouse.append_client_mousebinding(button)
|
|
end
|
|
end
|
|
|
|
--- Remove a mousebinding from the default client buttons.
|
|
--
|
|
-- @staticfct awful.mouse.remove_client_mousebinding
|
|
-- @tparam awful.button button The button.
|
|
-- @treturn boolean True if the button was removed and false if it wasn't found.
|
|
-- @see awful.keyboard.append_client_keybinding
|
|
|
|
function mouse.remove_client_mousebinding(button)
|
|
for k, v in ipairs(default_buttons) do
|
|
if button == v then
|
|
table.remove(default_buttons, k)
|
|
|
|
for _, c in ipairs(capi.client.get(nil, false)) do
|
|
c:remove_mousebinding(button)
|
|
end
|
|
|
|
return true
|
|
end
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
for k, b in ipairs {"left", "middle", "right"} do
|
|
mouse.object["is_".. b .."_mouse_button_pressed"] = function()
|
|
return capi.mouse.coords().buttons[k]
|
|
end
|
|
end
|
|
|
|
-- Set the cursor at startup
|
|
capi.root.cursor("left_ptr")
|
|
|
|
-- Implement the custom property handler
|
|
local props = {}
|
|
|
|
capi.mouse.set_newindex_miss_handler(function(_,key,value)
|
|
if mouse.object["set_"..key] then
|
|
mouse.object["set_"..key](value)
|
|
elseif not mouse.object["get_"..key] then
|
|
props[key] = value
|
|
else
|
|
-- If there is a getter, but no setter, then the property is read-only
|
|
error("Cannot set '" .. tostring(key) .. " because it is read-only")
|
|
end
|
|
end)
|
|
|
|
capi.mouse.set_index_miss_handler(function(_,key)
|
|
if mouse.object["get_"..key] then
|
|
return mouse.object["get_"..key]()
|
|
elseif mouse.object[key] and key:sub(1, 3) == "is_" then
|
|
return mouse.object[key]()
|
|
elseif mouse.object[key] then
|
|
return mouse.object[key]
|
|
else
|
|
return props[key]
|
|
end
|
|
end)
|
|
|
|
--- Get or set the mouse coords.
|
|
--
|
|
--@DOC_awful_mouse_coords_EXAMPLE@
|
|
--
|
|
-- @tparam[opt=nil] table coords_table None or a table with x and y keys as mouse
|
|
-- coordinates.
|
|
-- @tparam[opt=nil] integer coords_table.x The mouse horizontal position
|
|
-- @tparam[opt=nil] integer coords_table.y The mouse vertical position
|
|
-- @tparam[opt=false] boolean silent Disable mouse::enter or mouse::leave events that
|
|
-- could be triggered by the pointer when moving.
|
|
-- @treturn table The coords. It contains the `x`, `y` and `buttons` keys.
|
|
-- `buttons` contains the button number as key and a boolean as value (if it is
|
|
-- pressed).
|
|
-- @staticfct mouse.coords
|
|
-- @see is_left_mouse_button_pressed
|
|
-- @see is_right_mouse_button_pressed
|
|
-- @see is_middle_mouse_button_pressed
|
|
|
|
capi.client.connect_signal("scanning", function()
|
|
capi.client.emit_signal("request::default_mousebindings", "startup")
|
|
end)
|
|
|
|
-- Private function to be used by `ruled.client`.
|
|
function mouse._get_client_mousebindings()
|
|
return default_buttons
|
|
end
|
|
|
|
mouse.resize_handler = mouse.resize._resize_handler
|
|
|
|
return mouse
|
|
|
|
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|