--------------------------------------------------------------------------- --- 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