Implement support for EWMH _NET_WM_MOVERESIZE client messages
This commit is contained in:
parent
aa8c7c6e27
commit
1b80f2de35
|
@ -8,6 +8,7 @@ _NET_DESKTOP_NAMES
|
||||||
_NET_ACTIVE_WINDOW
|
_NET_ACTIVE_WINDOW
|
||||||
_NET_SUPPORTING_WM_CHECK
|
_NET_SUPPORTING_WM_CHECK
|
||||||
_NET_CLOSE_WINDOW
|
_NET_CLOSE_WINDOW
|
||||||
|
_NET_WM_MOVERESIZE
|
||||||
_NET_FRAME_EXTENTS
|
_NET_FRAME_EXTENTS
|
||||||
_NET_WM_NAME
|
_NET_WM_NAME
|
||||||
_NET_WM_STRUT_PARTIAL
|
_NET_WM_STRUT_PARTIAL
|
||||||
|
|
73
ewmh.c
73
ewmh.c
|
@ -35,6 +35,9 @@
|
||||||
#define _NET_WM_STATE_ADD 1
|
#define _NET_WM_STATE_ADD 1
|
||||||
#define _NET_WM_STATE_TOGGLE 2
|
#define _NET_WM_STATE_TOGGLE 2
|
||||||
|
|
||||||
|
#define _NET_WM_MOVERESIZE_MOVE 8
|
||||||
|
#define _NET_WM_MOVERESIZE_CANCEL 11
|
||||||
|
|
||||||
#define ALL_DESKTOPS 0xffffffff
|
#define ALL_DESKTOPS 0xffffffff
|
||||||
|
|
||||||
/** Update client EWMH hints.
|
/** Update client EWMH hints.
|
||||||
|
@ -141,6 +144,7 @@ ewmh_init(void)
|
||||||
_NET_DESKTOP_NAMES,
|
_NET_DESKTOP_NAMES,
|
||||||
_NET_ACTIVE_WINDOW,
|
_NET_ACTIVE_WINDOW,
|
||||||
_NET_CLOSE_WINDOW,
|
_NET_CLOSE_WINDOW,
|
||||||
|
_NET_WM_MOVERESIZE,
|
||||||
_NET_FRAME_EXTENTS,
|
_NET_FRAME_EXTENTS,
|
||||||
_NET_WM_NAME,
|
_NET_WM_NAME,
|
||||||
_NET_WM_STRUT_PARTIAL,
|
_NET_WM_STRUT_PARTIAL,
|
||||||
|
@ -455,6 +459,34 @@ ewmh_process_desktop(client_t *c, uint32_t desktop)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char *moveresize_size_dir_map[] = {
|
||||||
|
"top_left", "top", "top_right", "right",
|
||||||
|
"bottom_right", "bottom", "bottom_left", "left"
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
push_moveresize_data(lua_State *L, const uint32_t data[5])
|
||||||
|
{
|
||||||
|
lua_newtable(L);
|
||||||
|
|
||||||
|
lua_pushstring(L, "mouse_pos");
|
||||||
|
lua_newtable(L);
|
||||||
|
|
||||||
|
lua_pushstring(L, "x");
|
||||||
|
lua_pushnumber(L, data[0]);
|
||||||
|
lua_settable(L, -3);
|
||||||
|
|
||||||
|
lua_pushstring(L, "y");
|
||||||
|
lua_pushnumber(L, data[1]);
|
||||||
|
lua_settable(L, -3);
|
||||||
|
|
||||||
|
lua_settable(L, -3);
|
||||||
|
|
||||||
|
lua_pushstring(L, "button");
|
||||||
|
lua_pushnumber(L, data[3]);
|
||||||
|
lua_settable(L, -3);
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
ewmh_process_client_message(xcb_client_message_event_t *ev)
|
ewmh_process_client_message(xcb_client_message_event_t *ev)
|
||||||
{
|
{
|
||||||
|
@ -511,6 +543,47 @@ ewmh_process_client_message(xcb_client_message_event_t *ev)
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if(ev->type == _NET_WM_MOVERESIZE)
|
||||||
|
{
|
||||||
|
if((c = client_getbywin(ev->window)))
|
||||||
|
{
|
||||||
|
lua_State *L = globalconf_get_lua_State();
|
||||||
|
uint32_t dir = ev->data.data32[2];
|
||||||
|
if(dir < 8) /* It's _NET_WM_MOVERESIZE_SIZE_* */
|
||||||
|
{
|
||||||
|
luaA_object_push(L, c);
|
||||||
|
lua_pushstring(L, "ewmh");
|
||||||
|
push_moveresize_data(L, ev->data.data32);
|
||||||
|
|
||||||
|
lua_pushstring(L, "corner");
|
||||||
|
lua_pushstring(L, moveresize_size_dir_map[dir]);
|
||||||
|
lua_settable(L, -3);
|
||||||
|
|
||||||
|
luaA_object_emit_signal(L, -3, "request::mouse_resize", 2);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch(dir)
|
||||||
|
{
|
||||||
|
case _NET_WM_MOVERESIZE_MOVE:
|
||||||
|
luaA_object_push(L, c);
|
||||||
|
lua_pushstring(L, "ewmh");
|
||||||
|
push_moveresize_data(L, ev->data.data32);
|
||||||
|
luaA_object_emit_signal(L, -3, "request::mouse_move", 2);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
break;
|
||||||
|
case _NET_WM_MOVERESIZE_CANCEL:
|
||||||
|
luaA_object_push(L, c);
|
||||||
|
lua_pushstring(L, "ewmh");
|
||||||
|
luaA_object_emit_signal(L, -2, "request::mouse_cancel", 1);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
break;
|
||||||
|
/* Simply ignore the _NET_WM_MOVERESIZE_*_KEYBOARD cases like i3 does */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -188,8 +188,14 @@ local function handler(_, client, context, args) --luacheck: no unused_args
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Quit when the button is released
|
-- Quit when the button is released
|
||||||
for _,v in pairs(coords.buttons) do
|
if args.mouse_buttons and #args.mouse_buttons > 0 then
|
||||||
if v then return true end
|
for _,v in pairs(args.mouse_buttons) do
|
||||||
|
if coords.buttons[v] then return true end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
for _,v in pairs(coords.buttons) do
|
||||||
|
if v then return true end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Only resize after the mouse is released, this avoids losing content
|
-- Only resize after the mouse is released, this avoids losing content
|
||||||
|
|
|
@ -26,6 +26,7 @@ local ipairs = ipairs
|
||||||
local timer = require("gears.timer")
|
local timer = require("gears.timer")
|
||||||
local gtable = require("gears.table")
|
local gtable = require("gears.table")
|
||||||
local aclient = require("awful.client")
|
local aclient = require("awful.client")
|
||||||
|
local mresize = require("awful.mouse.resize")
|
||||||
local aplace = require("awful.placement")
|
local aplace = require("awful.placement")
|
||||||
local asuit = require("awful.layout.suit")
|
local asuit = require("awful.layout.suit")
|
||||||
local beautiful = require("beautiful")
|
local beautiful = require("beautiful")
|
||||||
|
@ -567,6 +568,101 @@ function permissions.client_geometry_requests(c, context, hints)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Begins moving a client with the mouse.
|
||||||
|
--
|
||||||
|
-- This is the default handler for `request::mouse_move`. When a request is
|
||||||
|
-- received, it uses `awful.mouse.resize` to initiate a mouse movement transaction
|
||||||
|
-- that lasts so long as the specified mouse button is held.
|
||||||
|
--
|
||||||
|
-- @signalhandler awful.permissions.client_mouse_move
|
||||||
|
-- @tparam client c The client
|
||||||
|
-- @tparam string context The context
|
||||||
|
-- @tparam table args Additional information describing the movement.
|
||||||
|
-- @tparam number args.mouse_pos.x The x coordinate of the mouse when grabbed.
|
||||||
|
-- @tparam number args.mouse_pos.y The y coordinate of the mouse when grabbed.
|
||||||
|
-- @tparam number args.button The mouse button that initiated the movement.
|
||||||
|
-- @sourcesignal client request::mouse_move
|
||||||
|
function permissions.client_mouse_move(c, context, args)
|
||||||
|
if not pcommon.check(c, "client", "mouse_move", context) then return end
|
||||||
|
|
||||||
|
if not c
|
||||||
|
or c.fullscreen
|
||||||
|
or c.maximized
|
||||||
|
or c.type == "desktop"
|
||||||
|
or c.type == "splash"
|
||||||
|
or c.type == "dock" then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local center_pos = aplace.centered(mouse, {parent=c, pretend=true})
|
||||||
|
mresize(c, "mouse.move", {
|
||||||
|
placement = aplace.under_mouse,
|
||||||
|
offset = {
|
||||||
|
x = center_pos.x - args.mouse_pos.x,
|
||||||
|
y = center_pos.y - args.mouse_pos.y
|
||||||
|
},
|
||||||
|
mouse_buttons = {args.button}
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Begins resizing a client with the mouse.
|
||||||
|
--
|
||||||
|
-- This is the default handler for `request::mouse_resize`. When a request is
|
||||||
|
-- received, it uses `awful.mouse.resize` to initiate a mouse resizing transaction
|
||||||
|
-- that lasts so long as the specified mouse button is held.
|
||||||
|
--
|
||||||
|
-- @signalhandler awful.permissions.client_mouse_resize
|
||||||
|
-- @tparam client c The client
|
||||||
|
-- @tparam string context The context
|
||||||
|
-- @tparam table args Additional information describing the resizing.
|
||||||
|
-- @tparam number args.mouse_pos.x The x coordinate of the mouse when grabbed.
|
||||||
|
-- @tparam number args.mouse_pos.y The y coordinate of the mouse when grabbed.
|
||||||
|
-- @tparam number args.button The mouse button that initiated the resizing.
|
||||||
|
-- @tparam string args.corner The corner/side of the window being resized.
|
||||||
|
-- @sourcesignal client request::mouse_resize
|
||||||
|
function permissions.client_mouse_resize(c, context, args)
|
||||||
|
if not pcommon.check(c, "client", "mouse_resize", context) then return end
|
||||||
|
|
||||||
|
if not c
|
||||||
|
or c.fullscreen
|
||||||
|
or c.maximized
|
||||||
|
or c.type == "desktop"
|
||||||
|
or c.type == "splash"
|
||||||
|
or c.type == "dock" then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local corner_pos = aplace[args.corner](mouse, {parent = c, pretend = true})
|
||||||
|
mresize(c, "mouse.resize", {
|
||||||
|
corner = args.corner,
|
||||||
|
corner_lock = true,
|
||||||
|
mouse_offset = {
|
||||||
|
x = args.mouse_pos.x - corner_pos.x,
|
||||||
|
y = args.mouse_pos.y - corner_pos.y
|
||||||
|
},
|
||||||
|
mouse_buttons = {args.button}
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Cancels a mouse movement/resizing operation.
|
||||||
|
--
|
||||||
|
-- This is the default handler for `request::mouse_cancel`. It simply ends any
|
||||||
|
-- ongoing `mousegrabber` transaction.
|
||||||
|
--
|
||||||
|
-- @signalhandler awful.permissions.client_mouse_cancel
|
||||||
|
-- @tparam client c The client
|
||||||
|
-- @tparam string context The context
|
||||||
|
-- @sourcesignal client request::mouse_cancel
|
||||||
|
function permissions.client_mouse_cancel(c, context)
|
||||||
|
if not pcommon.check(c, "client", "mouse_cancel", context) then return end
|
||||||
|
|
||||||
|
-- This will also stop other mouse grabber transactions, but that's probably fine;
|
||||||
|
-- a well-behaved client should only raise this signal when a mouse movement or
|
||||||
|
-- resizing operation is in progress, and so no other mouse grabber transactions
|
||||||
|
-- should be happening at this time.
|
||||||
|
mousegrabber.stop()
|
||||||
|
end
|
||||||
|
|
||||||
-- The magnifier layout doesn't work with focus follow mouse.
|
-- The magnifier layout doesn't work with focus follow mouse.
|
||||||
permissions.add_activate_filter(function(c)
|
permissions.add_activate_filter(function(c)
|
||||||
if alayout.get(c.screen) ~= alayout.suit.magnifier
|
if alayout.get(c.screen) ~= alayout.suit.magnifier
|
||||||
|
@ -799,6 +895,9 @@ client.connect_signal("request::urgent" , permissions.urgent)
|
||||||
client.connect_signal("request::geometry" , permissions.geometry)
|
client.connect_signal("request::geometry" , permissions.geometry)
|
||||||
client.connect_signal("request::geometry" , permissions.merge_maximization)
|
client.connect_signal("request::geometry" , permissions.merge_maximization)
|
||||||
client.connect_signal("request::geometry" , permissions.client_geometry_requests)
|
client.connect_signal("request::geometry" , permissions.client_geometry_requests)
|
||||||
|
client.connect_signal("request::mouse_move" , permissions.client_mouse_move)
|
||||||
|
client.connect_signal("request::mouse_resize" , permissions.client_mouse_resize)
|
||||||
|
client.connect_signal("request::mouse_cancel" , permissions.client_mouse_cancel)
|
||||||
client.connect_signal("property::border_width" , repair_geometry)
|
client.connect_signal("property::border_width" , repair_geometry)
|
||||||
client.connect_signal("property::screen" , repair_geometry)
|
client.connect_signal("property::screen" , repair_geometry)
|
||||||
client.connect_signal("request::unmanage" , check_focus_delayed)
|
client.connect_signal("request::unmanage" , check_focus_delayed)
|
||||||
|
|
|
@ -1123,6 +1123,11 @@ end
|
||||||
--
|
--
|
||||||
-- * *axis*: The axis (vertical or horizontal). If none is
|
-- * *axis*: The axis (vertical or horizontal). If none is
|
||||||
-- specified, then the drawable will be resized on both axis.
|
-- specified, then the drawable will be resized on both axis.
|
||||||
|
-- * *corner*: The corner to resize from. If not specified, then
|
||||||
|
-- the corner nearest to the cursor is used.
|
||||||
|
-- * *corner_lock*: Whether to lock the corner to *corner* or not.
|
||||||
|
-- if not set, then the corner may be updated to the one nearest
|
||||||
|
-- to the cursor in the middle of resizing operation.
|
||||||
--
|
--
|
||||||
--@DOC_awful_placement_resize_to_mouse_EXAMPLE@
|
--@DOC_awful_placement_resize_to_mouse_EXAMPLE@
|
||||||
-- @tparam drawable d A drawable (like `client`, `mouse` or `wibox`)
|
-- @tparam drawable d A drawable (like `client`, `mouse` or `wibox`)
|
||||||
|
@ -1138,25 +1143,38 @@ function placement.resize_to_mouse(d, args)
|
||||||
local h_only = args.axis == "horizontal"
|
local h_only = args.axis == "horizontal"
|
||||||
local v_only = args.axis == "vertical"
|
local v_only = args.axis == "vertical"
|
||||||
|
|
||||||
-- To support both growing and shrinking the drawable, it is necessary
|
if args.mouse_offset then
|
||||||
-- to decide to use either "north or south" and "east or west" directions.
|
coords = {
|
||||||
-- Otherwise, the result will always be 1x1
|
x = coords.x - args.mouse_offset.x,
|
||||||
local _, closest_corner = placement.closest_corner(capi.mouse, {
|
y = coords.y - args.mouse_offset.y
|
||||||
parent = d,
|
}
|
||||||
pretend = true,
|
end
|
||||||
include_sides = args.include_sides or false,
|
|
||||||
})
|
local corner
|
||||||
|
if args.corner and args.corner_lock then
|
||||||
|
corner = args.corner
|
||||||
|
else
|
||||||
|
-- To support both growing and shrinking the drawable, it is necessary
|
||||||
|
-- to decide to use either "north or south" and "east or west" directions.
|
||||||
|
-- Otherwise, the result will always be 1x1
|
||||||
|
local _, closest_corner = placement.closest_corner(capi.mouse, {
|
||||||
|
parent = d,
|
||||||
|
pretend = true,
|
||||||
|
include_sides = args.include_sides or false,
|
||||||
|
})
|
||||||
|
corner = closest_corner
|
||||||
|
end
|
||||||
|
|
||||||
-- Given "include_sides" wasn't set, it will always return a name
|
-- Given "include_sides" wasn't set, it will always return a name
|
||||||
-- with the 2 axis. If only one axis is needed, adjust the result
|
-- with the 2 axis. If only one axis is needed, adjust the result
|
||||||
if h_only then
|
if h_only then
|
||||||
closest_corner = closest_corner:match("left") or closest_corner:match("right")
|
corner = corner:match("left") or corner:match("right")
|
||||||
elseif v_only then
|
elseif v_only then
|
||||||
closest_corner = closest_corner:match("top") or closest_corner:match("bottom")
|
corner = corner:match("top") or corner:match("bottom")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Use p0 (mouse), p1 and p2 to create a rectangle
|
-- Use p0 (mouse), p1 and p2 to create a rectangle
|
||||||
local pts = resize_to_point_map[closest_corner]
|
local pts = resize_to_point_map[corner]
|
||||||
local p1 = pts.p1 and rect_to_point(ngeo, pts.p1[1], pts.p1[2]) or coords
|
local p1 = pts.p1 and rect_to_point(ngeo, pts.p1[1], pts.p1[2]) or coords
|
||||||
local p2 = pts.p2 and rect_to_point(ngeo, pts.p2[1], pts.p2[2]) or coords
|
local p2 = pts.p2 and rect_to_point(ngeo, pts.p2[1], pts.p2[2]) or coords
|
||||||
|
|
||||||
|
|
|
@ -315,6 +315,60 @@ lua_class_t client_class;
|
||||||
* @classsignal
|
* @classsignal
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/** Emitted when something requests for a client to be moved by the mouse.
|
||||||
|
*
|
||||||
|
* This is used to allow clients to manage their own "grabbable" areas, such
|
||||||
|
* as in custom title bars, but to then delegate the task of actually moving
|
||||||
|
* the client to the window manager.
|
||||||
|
*
|
||||||
|
* **Contexts are:**
|
||||||
|
* * *ewmh*: When the client requests the movement (via _NET_WM_MOVERESIZE)
|
||||||
|
*
|
||||||
|
* @signal request::mouse_move
|
||||||
|
* @tparam client c The client.
|
||||||
|
* @tparam string context Why the mouse movement was requested.
|
||||||
|
* @tparam table args Additional information describing the movement.
|
||||||
|
* @tparam number args.mouse_pos.x The x coordinate of the mouse when grabbed.
|
||||||
|
* @tparam number args.mouse_pos.y The y coordinate of the mouse when grabbed.
|
||||||
|
* @tparam number args.button The mouse button that initiated the movement.
|
||||||
|
* @classsignal
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** Emitted when something requests for a client to be resized by the mouse.
|
||||||
|
*
|
||||||
|
* This is used to allow clients to manage their own "grabbable" areas, such
|
||||||
|
* as in custom window frames, but to then delegate the task of actually
|
||||||
|
* resizing the client to the window manager.
|
||||||
|
*
|
||||||
|
* **Contexts are:**
|
||||||
|
* * *ewmh*: When the client requests the resizing (via _NET_WM_MOVERESIZE)
|
||||||
|
*
|
||||||
|
* @signal request::mouse_resize
|
||||||
|
* @tparam client c The client.
|
||||||
|
* @tparam string context Why the mouse resizing was requested.
|
||||||
|
* @tparam table args Additional information describing the resizing.
|
||||||
|
* @tparam number args.mouse_pos.x The x coordinate of the mouse when grabbed.
|
||||||
|
* @tparam number args.mouse_pos.y The y coordinate of the mouse when grabbed.
|
||||||
|
* @tparam number args.button The mouse button that initiated the resizing.
|
||||||
|
* @tparam string args.corner The corner/side of the window being resized.
|
||||||
|
* @classsignal
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** Emitted when something requests for a grabbed client to be released.
|
||||||
|
*
|
||||||
|
* This is used to allow clients to cancel a mouse movement or resizing
|
||||||
|
* operation that may have been started by an earlier `request::mouse_move`
|
||||||
|
* or `request::mouse_resize` signal.
|
||||||
|
*
|
||||||
|
* **Contexts are:**
|
||||||
|
* * *ewmh*: When the client requests the release (via _NET_WM_MOVERESIZE)
|
||||||
|
*
|
||||||
|
* @signal request::mouse_cancel
|
||||||
|
* @tparam client c The client
|
||||||
|
* @tparam string context Why the mouse release was requested.
|
||||||
|
* @classsignal
|
||||||
|
*/
|
||||||
|
|
||||||
/** Emitted when a client requests to be moved to a tag or needs a new tag.
|
/** Emitted when a client requests to be moved to a tag or needs a new tag.
|
||||||
*
|
*
|
||||||
* @signal request::tag
|
* @signal request::tag
|
||||||
|
|
Loading…
Reference in New Issue