From ab6f7e03ca99fcc6a7fb5fc94b7095a6edf05a4f Mon Sep 17 00:00:00 2001 From: Xinhao Yuan Date: Wed, 2 Feb 2022 16:59:01 -0500 Subject: [PATCH] Relocate a client window as if it is undecorated when reparenting it back. It eliminates the position offset due to re-decorating when a client trys to restore its previous position. (#3253) Add tests for geometry changes when managing/unmanaging clients. Also verified that it fixed issue #2308. --- awesome.c | 3 +- objects/client.c | 24 ++++++++- objects/client.h | 1 + tests/test-geometry.lua | 113 +++++++++++++++++++++++++++++++++++++++- 4 files changed, 138 insertions(+), 3 deletions(-) diff --git a/awesome.c b/awesome.c index 24ee52d8..07d8c82e 100644 --- a/awesome.c +++ b/awesome.c @@ -111,8 +111,9 @@ awesome_atexit(bool restart) /* Move clients where we want them to be and keep the stacking order intact */ foreach(c, globalconf.stack) { + area_t geometry = client_get_undecorated_geometry(*c); xcb_reparent_window(globalconf.connection, (*c)->window, globalconf.screen->root, - (*c)->geometry.x, (*c)->geometry.y); + geometry.x, geometry.y); } /* Save the client order. This is useful also for "hard" restarts. */ diff --git a/objects/client.c b/objects/client.c index 22167064..4edec675 100644 --- a/objects/client.c +++ b/objects/client.c @@ -2267,6 +2267,27 @@ client_add_titlebar_geometry(client_t *c, area_t *geometry) geometry->height += c->titlebar[CLIENT_TITLEBAR_BOTTOM].size; } +area_t +client_get_undecorated_geometry(client_t *c) +{ + area_t geometry = c->geometry; + if (!c->fullscreen) { + int diff_left = c->titlebar[CLIENT_TITLEBAR_LEFT].size; + int diff_right = c->titlebar[CLIENT_TITLEBAR_RIGHT].size; + int diff_top = c->titlebar[CLIENT_TITLEBAR_TOP].size; + int diff_bottom = c->titlebar[CLIENT_TITLEBAR_BOTTOM].size; + geometry.width -= diff_left + diff_right; + geometry.height -= diff_top + diff_bottom; + if (c->size_hints.flags & XCB_ICCCM_SIZE_HINT_P_WIN_GRAVITY) { + xwindow_translate_for_gravity(c->size_hints.win_gravity, + -diff_left - c->border_width, -diff_top - c->border_width, + -diff_right - c->border_width, -diff_bottom - c->border_width, + &geometry.x, &geometry.y); + } + } + return geometry; +} + /** Send a synthetic configure event to a window. */ void @@ -2941,9 +2962,10 @@ client_unmanage(client_t *c, client_unmanage_t reason) if(reason != CLIENT_UNMANAGE_DESTROYED) { + area_t geometry = client_get_undecorated_geometry(c); xcb_unmap_window(globalconf.connection, c->window); xcb_reparent_window(globalconf.connection, c->window, globalconf.screen->root, - c->geometry.x, c->geometry.y); + geometry.x, geometry.y); } if (c->nofocus_window != XCB_NONE) diff --git a/objects/client.h b/objects/client.h index 387e7c2a..70b05d19 100644 --- a/objects/client.h +++ b/objects/client.h @@ -256,6 +256,7 @@ void client_emit_scanned(void); void client_emit_scanning(void); drawable_t *client_get_drawable(client_t *, int, int); drawable_t *client_get_drawable_offset(client_t *, int *, int *); +area_t client_get_undecorated_geometry(client_t *); /** Put client on top of the stack. * \param c The client to raise. diff --git a/tests/test-geometry.lua b/tests/test-geometry.lua index b9ed5ea5..f49dbd13 100644 --- a/tests/test-geometry.lua +++ b/tests/test-geometry.lua @@ -6,9 +6,13 @@ local wibox = require( "wibox" ) local beautiful = require( "beautiful" ) local cruled = require("ruled.client") local gdebug = require("gears.debug") +local test_client = require("_client") +local lgi = require("lgi") +local gears = require("gears") local w = nil local w1_draw, w2_draw +local windowid -- Replacing the rules is not supported anymore. local dep = gdebug.deprecate @@ -257,6 +261,113 @@ local steps = { end } -runner.run_steps(steps) +-- Tests for unmanaged client geometries with different gravity settings. +local test_data = { + -- gravity => expectation of unmanaged x, y, w, h. + ["NORTH_WEST"] = {0, 0, 100, 80}, + ["NORTH"] = {10, 0, 100, 80}, + ["NORTH_EAST"] = {20, 0, 100, 80}, + ["WEST"] = {0, 20, 100, 80}, + ["CENTER"] = {10, 20, 100, 80}, + ["EAST"] = {20, 20, 100, 80}, + ["SOUTH_WEST"] = {0, 40, 100, 80}, + ["SOUTH"] = {10, 40, 100, 80}, + ["SOUTH_EAST"] = {20, 40, 100, 80}, + ["STATIC"] = {10, 30, 100, 80} +} +for gravity, expectation in pairs(test_data) do + gears.table.merge(steps, { + function() + set_rules { } + -- Wait for the previous cleanup to be done + if #client.get() == 0 then + return true + end + end, + function(count) + if count == 1 then + print("testing gravity " .. gravity) + test_client(nil,nil,nil,nil,nil,{gravity=lgi.Gdk.Gravity[gravity]}) + else + local c = client.get()[1] + if c then + assert(c.size_hints.win_gravity == gravity:lower()) + c.border_width = 10 + c:titlebar_top(20) + c:geometry({ + x = 0, + y = 0, + width = 100, + height = 100 + }) + windowid = c.window + return true + end + end + end, + -- For some reason unmanage needs to happen in a separate step to pass the tests.. + function() + local c = client.get()[1] + c:unmanage() + awesome.sync() + return true + end, + function() + local display = lgi.Gdk.Display.open(os.getenv("DISPLAY")) + local window = lgi.GdkX11.X11Window.foreign_new_for_display(display, windowid) + local x, y, width, height = window:get_geometry() + print(gravity, x, y, width, height) + assert(x == expectation[1]) + assert(y == expectation[2]) + assert(width == expectation[3]) + assert(height == expectation[4]) + window:destroy() + return true + end, + function() + local display = lgi.Gdk.Display.open(os.getenv("DISPLAY")) + local window = lgi.GdkX11.X11Window.foreign_new_for_display(display, windowid) + if window then return end + return true + end, + -- Additionally, checks our expectations are correct by adding decoration back. + function(count) + if count == 1 then + test_client(nil,nil,nil,nil,nil,{gravity=lgi.Gdk.Gravity[gravity]}) + else + local c = client.get()[1] + if c then + assert(c.size_hints.win_gravity == gravity:lower()) + c.border_width = 0 + c:titlebar_top(0) + c:geometry({ + x = expectation[1], + y = expectation[2], + width = expectation[3], + height = expectation[4] + }) + return true + end + end + end, + function() + local c = client.get()[1] + c:titlebar_top(20) + c.border_width = 10 + return true + end, + function() + local c = client.get()[1] + assert(c.border_width == 10 ) + assert(c:geometry().x == 0 ) + assert(c:geometry().y == 0 ) + assert(c:geometry().height == 100 ) + assert(c:geometry().width == 100 ) + c:kill() + return true + end, + }) +end +runner.run_steps(steps) -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80