Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement support for EWMH _NET_WM_MOVERESIZE client messages #3859

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions awesomerc.lua
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,7 @@ ruled.client.connect_signal("request::rules", function()
-- @DOC_CSD_TITLEBARS@
id = "titlebars",
rule_any = { type = { "normal", "dialog" } },
except = { requests_no_titlebar = true },
phantamanta44 marked this conversation as resolved.
Show resolved Hide resolved
properties = { titlebars_enabled = true }
}

Expand Down
1 change: 1 addition & 0 deletions common/atoms.list
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ _NET_DESKTOP_NAMES
_NET_ACTIVE_WINDOW
_NET_SUPPORTING_WM_CHECK
_NET_CLOSE_WINDOW
_NET_WM_MOVERESIZE
_NET_FRAME_EXTENTS
_NET_WM_NAME
_NET_WM_STRUT_PARTIAL
Expand Down
73 changes: 73 additions & 0 deletions ewmh.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@
#define _NET_WM_STATE_ADD 1
#define _NET_WM_STATE_TOGGLE 2

#define _NET_WM_MOVERESIZE_MOVE 8
#define _NET_WM_MOVERESIZE_CANCEL 11

#define ALL_DESKTOPS 0xffffffff

/** Update client EWMH hints.
Expand Down Expand Up @@ -141,6 +144,7 @@
_NET_DESKTOP_NAMES,
_NET_ACTIVE_WINDOW,
_NET_CLOSE_WINDOW,
_NET_WM_MOVERESIZE,
_NET_FRAME_EXTENTS,
_NET_WM_NAME,
_NET_WM_STRUT_PARTIAL,
Expand Down Expand Up @@ -455,6 +459,34 @@
}
}

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
ewmh_process_client_message(xcb_client_message_event_t *ev)
{
Expand Down Expand Up @@ -511,6 +543,47 @@
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;

Check warning on line 581 in ewmh.c

View check run for this annotation

Codecov / codecov/patch

ewmh.c#L576-L581

Added lines #L576 - L581 were not covered by tests
/* Simply ignore the _NET_WM_MOVERESIZE_*_KEYBOARD cases like i3 does */
}
}
}
}

return 0;
}
Expand Down
10 changes: 8 additions & 2 deletions lib/awful/mouse/resize.lua
Original file line number Diff line number Diff line change
Expand Up @@ -188,8 +188,14 @@ local function handler(_, client, context, args) --luacheck: no unused_args
end

-- Quit when the button is released
for _,v in pairs(coords.buttons) do
if v then return true end
if args.mouse_buttons and #args.mouse_buttons > 0 then
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

-- Only resize after the mouse is released, this avoids losing content
Expand Down
99 changes: 99 additions & 0 deletions lib/awful/permissions/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
local timer = require("gears.timer")
local gtable = require("gears.table")
local aclient = require("awful.client")
local mresize = require("awful.mouse.resize")
local aplace = require("awful.placement")
local asuit = require("awful.layout.suit")
local beautiful = require("beautiful")
Expand Down Expand Up @@ -567,6 +568,101 @@
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)
phantamanta44 marked this conversation as resolved.
Show resolved Hide resolved
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

Check warning on line 594 in lib/awful/permissions/init.lua

View check run for this annotation

Codecov / codecov/patch

lib/awful/permissions/init.lua#L594

Added line #L594 was not covered by tests
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

Check warning on line 632 in lib/awful/permissions/init.lua

View check run for this annotation

Codecov / codecov/patch

lib/awful/permissions/init.lua#L632

Added line #L632 was not covered by tests
end

local corner_pos = aplace[args.corner](mouse, {parent = c, pretend = true})
mresize(c, "mouse.resize", {
corner = args.corner,
corner_lock = true,
phantamanta44 marked this conversation as resolved.
Show resolved Hide resolved
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.
permissions.add_activate_filter(function(c)
if alayout.get(c.screen) ~= alayout.suit.magnifier
Expand Down Expand Up @@ -799,6 +895,9 @@
client.connect_signal("request::geometry" , permissions.geometry)
client.connect_signal("request::geometry" , permissions.merge_maximization)
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::screen" , repair_geometry)
client.connect_signal("request::unmanage" , check_focus_delayed)
Expand Down
40 changes: 29 additions & 11 deletions lib/awful/placement.lua
Original file line number Diff line number Diff line change
Expand Up @@ -1123,6 +1123,11 @@
--
-- * *axis*: The axis (vertical or horizontal). If none is
-- 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@
-- @tparam drawable d A drawable (like `client`, `mouse` or `wibox`)
Expand All @@ -1138,25 +1143,38 @@
local h_only = args.axis == "horizontal"
local v_only = args.axis == "vertical"

-- 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,
})
if args.mouse_offset then
coords = {
x = coords.x - args.mouse_offset.x,
y = coords.y - args.mouse_offset.y
}
end

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
-- with the 2 axis. If only one axis is needed, adjust the result
if h_only then
closest_corner = closest_corner:match("left") or closest_corner:match("right")
corner = corner:match("left") or corner:match("right")

Check warning on line 1171 in lib/awful/placement.lua

View check run for this annotation

Codecov / codecov/patch

lib/awful/placement.lua#L1171

Added line #L1171 was not covered by tests
elseif v_only then
closest_corner = closest_corner:match("top") or closest_corner:match("bottom")
corner = corner:match("top") or corner:match("bottom")

Check warning on line 1173 in lib/awful/placement.lua

View check run for this annotation

Codecov / codecov/patch

lib/awful/placement.lua#L1173

Added line #L1173 was not covered by tests
end

-- 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 p2 = pts.p2 and rect_to_point(ngeo, pts.p2[1], pts.p2[2]) or coords

Expand Down
54 changes: 54 additions & 0 deletions objects/client.c
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,60 @@ lua_class_t client_class;
* @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.
*
* @signal request::tag
Expand Down
Loading
Loading