diff --git a/awesome.c b/awesome.c index 3513cecf46..153f031a33 100644 --- a/awesome.c +++ b/awesome.c @@ -79,8 +79,7 @@ awesome_atexit(bool restart) /* Move clients where we want them to be and keep the stacking order intact */ foreach(c, globalconf.stack) { - xcb_reparent_window(globalconf.connection, (*c)->window, globalconf.screen->root, - (*c)->geometry.x, (*c)->geometry.y); + client_reparent_to_root(*c); } /* Save the client order. This is useful also for "hard" restarts. */ diff --git a/objects/client.c b/objects/client.c index 76f9af7936..5c6467054f 100644 --- a/objects/client.c +++ b/objects/client.c @@ -2291,8 +2291,7 @@ client_unmanage(client_t *c, bool window_valid) if(window_valid) { xcb_unmap_window(globalconf.connection, c->window); - xcb_reparent_window(globalconf.connection, c->window, globalconf.screen->root, - c->geometry.x, c->geometry.y); + client_reparent_to_root(c); } if (c->nofocus_window != XCB_NONE) @@ -2318,6 +2317,30 @@ client_unmanage(client_t *c, bool window_valid) luaA_object_unref(L, c); } +void +client_reparent_to_root(client_t *c) +{ + int16_t x = c->geometry.x; + int16_t y = c->geometry.y; + + /* Move the window inversely for its gravity */ + if(c->size_hints.flags & XCB_ICCCM_SIZE_HINT_P_WIN_GRAVITY) + { + int16_t diff_x = 0, diff_y = 0; + uint16_t bw = c->border_width; + xwindow_translate_for_gravity(c->size_hints.win_gravity, + bw + c->titlebar[CLIENT_TITLEBAR_LEFT].size, + bw + c->titlebar[CLIENT_TITLEBAR_TOP].size, + bw + c->titlebar[CLIENT_TITLEBAR_RIGHT].size, + bw + c->titlebar[CLIENT_TITLEBAR_BOTTOM].size, + &diff_x, &diff_y); + x -= diff_x; + y -= diff_y; + } + + xcb_reparent_window(globalconf.connection, c->window, globalconf.screen->root, x, y); +} + /** Kill a client via a WM_DELETE_WINDOW request or KillClient if not * supported. * \param c The client to kill. diff --git a/objects/client.h b/objects/client.h index f9115961f6..52a2b63ddc 100644 --- a/objects/client.h +++ b/objects/client.h @@ -199,6 +199,7 @@ void client_refresh_partial(client_t *, int16_t, int16_t, uint16_t, uint16_t); void client_class_setup(lua_State *); void client_send_configure(client_t *); void client_find_transient_for(client_t *); +void client_reparent_to_root(client_t *); drawable_t *client_get_drawable(client_t *, int, int); drawable_t *client_get_drawable_offset(client_t *, int *, int *); diff --git a/tests/test-gravity.c b/tests/test-gravity.c index 281bcb4846..160896c70a 100644 --- a/tests/test-gravity.c +++ b/tests/test-gravity.c @@ -46,6 +46,11 @@ * - Move the window. * [Wait for synthetic configure notify] * - Check that the window moves to the expected position. + * - Unmap the window + * [Wait for WM_STATE property notify] + * - Map the window + * [Wait for expose event] + * - Check that the window is mapped in the correct place. * * This needs a WM that supports _NET_FRAME_EXTENTS. * Also, this assumes that the WM allows and applies window moves and resizes by @@ -56,6 +61,8 @@ enum test_state { TEST_STATE_RESIZED1, TEST_STATE_RESIZED2, TEST_STATE_MOVED, + TEST_STATE_UNMAPPED, + TEST_STATE_MAPPED, TEST_STATE_DONE, TEST_STATE_DONE_GOT_CONFIGURE, }; @@ -72,6 +79,8 @@ enum test_state { static int32_t state_difference[][2] = { [TEST_STATE_CREATED] = { 0, 0 }, + [TEST_STATE_UNMAPPED] = { 0, 0 }, + [TEST_STATE_MAPPED] = { 0, 0 }, [TEST_STATE_RESIZED1] = { WIN_WIDTH1 - WIN_WIDTH2, WIN_HEIGHT1 - WIN_HEIGHT2 }, [TEST_STATE_RESIZED2] = { 0, 0 }, [TEST_STATE_MOVED] = { 0, 0 }, @@ -89,7 +98,9 @@ struct window_state { static xcb_connection_t *c = NULL; static xcb_screen_t *screen; static struct window_state window_state; +static xcb_window_t last_window = XCB_NONE; static xcb_atom_t net_frame_extents; +static xcb_atom_t wm_state; static bool done; static bool had_error = false; @@ -229,6 +240,10 @@ static const char *state_to_string(enum test_state state) { case TEST_STATE_CREATED: return "CREATED"; + case TEST_STATE_UNMAPPED: + return "UNMAPPED"; + case TEST_STATE_MAPPED: + return "MAPPED"; case TEST_STATE_RESIZED1: return "RESIZED1"; case TEST_STATE_RESIZED2: @@ -349,7 +364,7 @@ static void init(xcb_gravity_t gravity) XCB_WINDOW_CLASS_INPUT_OUTPUT, screen->root_visual, XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK, (uint32_t[]) { screen->white_pixel, - XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_EXPOSURE + XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_PROPERTY_CHANGE }); xcb_change_property(c, XCB_PROP_MODE_REPLACE, window_state.window, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8, strlen(name), name); @@ -359,10 +374,11 @@ static void init(xcb_gravity_t gravity) static void handle_expose(xcb_expose_event_t *ev) { - if (window_state.window != ev->window || window_state.state != TEST_STATE_CREATED) + if (window_state.window != ev->window + || (window_state.state != TEST_STATE_CREATED && window_state.state != TEST_STATE_MAPPED)) return; - do_log("Window in state CREATED was exposed, going to next state"); + do_log("Window in state %s was exposed, going to next state", state_to_string(window_state.state)); delayed_proceed_with_window(); } @@ -375,6 +391,8 @@ static void handle_configure_notify(xcb_configure_notify_event_t *ev) switch (window_state.state) { case TEST_STATE_CREATED: + case TEST_STATE_UNMAPPED: + case TEST_STATE_MAPPED: return; case TEST_STATE_RESIZED1: if (synthetic) @@ -493,7 +511,20 @@ static void handle_client_message(xcb_client_message_event_t *event) case TEST_STATE_MOVED: check_geometry(WIN_POS_X2, WIN_POS_Y2, WIN_WIDTH1, WIN_HEIGHT1); + do_log("Unmapping window"); + xcb_unmap_window(c, window_state.window); + window_state.state = TEST_STATE_UNMAPPED; + return; + case TEST_STATE_UNMAPPED: + do_log("Mapping window"); + xcb_map_window(c, window_state.window); + window_state.state = TEST_STATE_MAPPED; + return; + case TEST_STATE_MAPPED: + check_geometry(WIN_POS_X2, WIN_POS_Y2, WIN_WIDTH1, WIN_HEIGHT1); + xcb_destroy_window(c, window_state.window); + last_window = window_state.window; window_state.window = XCB_NONE; window_state.state = TEST_STATE_DONE; done = true; @@ -501,6 +532,15 @@ static void handle_client_message(xcb_client_message_event_t *event) } } +static void handle_property_notify(xcb_property_notify_event_t *event) +{ + check(event->window == window_state.window || event->window == last_window, + "Got property notify on unexpected window 0x%x, expected 0x%x or 0x%x?!", + event->window, window_state.window, last_window); + if (window_state.state == TEST_STATE_UNMAPPED && event->atom == wm_state) + delayed_proceed_with_window(); +} + static void event_loop(void) { xcb_generic_event_t *ev; @@ -520,6 +560,8 @@ static void event_loop(void) break; case XCB_CLIENT_MESSAGE: handle_client_message((xcb_client_message_event_t *) ev); + case XCB_PROPERTY_NOTIFY: + handle_property_notify((xcb_property_notify_event_t *) ev); case XCB_REPARENT_NOTIFY: case XCB_MAP_NOTIFY: case XCB_UNMAP_NOTIFY: @@ -566,6 +608,7 @@ int main() } screen = xcb_aux_get_screen(c, default_screen); net_frame_extents = intern_atom("_NET_FRAME_EXTENTS"); + wm_state = intern_atom("WM_STATE"); run_test(XCB_GRAVITY_NORTH_WEST); run_test(XCB_GRAVITY_NORTH);