From e900a8d23db1c10a36b8c45327d68038b0607a9b Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Mon, 21 Dec 2009 22:30:08 +0100 Subject: [PATCH] xinerama: correctly put windows which are assigned to a specific screen on that screen when it becomes available (Thanks badboy) --- include/workspace.h | 13 +++++++++- src/commands.c | 4 +-- src/manage.c | 2 +- src/workspace.c | 63 ++++++++++++++++++++++++++++++++++++++++----- src/xinerama.c | 38 ++++++++++----------------- 5 files changed, 86 insertions(+), 34 deletions(-) diff --git a/include/workspace.h b/include/workspace.h index f3bdd565..01e1b6fa 100644 --- a/include/workspace.h +++ b/include/workspace.h @@ -44,6 +44,17 @@ bool workspace_is_visible(Workspace *ws); /** Switches to the given workspace */ void workspace_show(xcb_connection_t *conn, int workspace); +/** + * Assigns the given workspace to the given screen by correctly updating its + * state and reconfiguring all the clients on this workspace. + * + * This is called when initializing a screen and when re-assigning it to a + * different screen which just got available (if you configured it to be on + * screen 1 and you just plugged in screen 1). + * + */ +void workspace_assign_to(Workspace *ws, i3Screen *screen); + /** * Initializes the given workspace if it is not already initialized. The given * screen is to be understood as a fallback, if the workspace itself either @@ -51,7 +62,7 @@ void workspace_show(xcb_connection_t *conn, int workspace); * the screen is not attached at the moment. * */ -void workspace_initialize(Workspace *ws, i3Screen *screen); +void workspace_initialize(Workspace *ws, i3Screen *screen, bool recheck); /** * Gets the first unused workspace for the given screen, taking into account diff --git a/src/commands.c b/src/commands.c index 7cb0bdca..32b7a398 100644 --- a/src/commands.c +++ b/src/commands.c @@ -531,7 +531,7 @@ static void move_floating_window_to_workspace(xcb_connection_t *conn, Client *cl LOG("moving floating\n"); - workspace_initialize(t_ws, c_ws->screen); + workspace_initialize(t_ws, c_ws->screen, false); /* Check if there is already a fullscreen client on the destination workspace and * stop moving if so. */ @@ -592,7 +592,7 @@ static void move_current_window_to_workspace(xcb_connection_t *conn, int workspa if (to_focus == NULL) to_focus = CIRCLEQ_PREV_OR_NULL(&(container->clients), current_client, clients); - workspace_initialize(t_ws, container->workspace->screen); + workspace_initialize(t_ws, container->workspace->screen, false); /* Check if there is already a fullscreen client on the destination workspace and * stop moving if so. */ if (current_client->fullscreen && (t_ws->fullscreen_client != NULL)) { diff --git a/src/manage.c b/src/manage.c index 54e02fe4..40a6f64a 100644 --- a/src/manage.c +++ b/src/manage.c @@ -340,7 +340,7 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child, DLOG("Changing container/workspace and unmapping the client\n"); Workspace *t_ws = workspace_get(assign->workspace-1); - workspace_initialize(t_ws, c_ws->screen); + workspace_initialize(t_ws, c_ws->screen, false); new->container = t_ws->table[t_ws->current_col][t_ws->current_row]; new->workspace = t_ws; diff --git a/src/workspace.c b/src/workspace.c index bca0544c..f8bfcd76 100644 --- a/src/workspace.c +++ b/src/workspace.c @@ -117,7 +117,7 @@ void workspace_show(xcb_connection_t *conn, int workspace) { c_ws->current_col = current_col; /* Check if the workspace has not been used yet */ - workspace_initialize(t_ws, c_ws->screen); + workspace_initialize(t_ws, c_ws->screen, false); if (c_ws->screen != t_ws->screen) { /* We need to switch to the other screen first */ @@ -244,6 +244,49 @@ static i3Screen *get_screen_from_preference(struct screens_head *slist, char *pr return NULL; } +/* + * Assigns the given workspace to the given screen by correctly updating its + * state and reconfiguring all the clients on this workspace. + * + * This is called when initializing a screen and when re-assigning it to a + * different screen which just got available (if you configured it to be on + * screen 1 and you just plugged in screen 1). + * + */ +void workspace_assign_to(Workspace *ws, i3Screen *screen) { + Client *client; + bool empty = true; + + ws->screen = screen; + + /* Copy the dimensions from the virtual screen */ + memcpy(&(ws->rect), &(ws->screen->rect), sizeof(Rect)); + + /* Force reconfiguration for each client on that workspace */ + FOR_TABLE(ws) + CIRCLEQ_FOREACH(client, &(ws->table[cols][rows]->clients), clients) { + client->force_reconfigure = true; + empty = false; + } + + if (empty) + return; + + /* Render the workspace to reconfigure the clients. However, they will be visible now, so… */ + render_workspace(global_conn, screen, ws); + + /* …unless we want to see them at the moment, we should hide that workspace */ + if (workspace_is_visible(ws)) + return; + + workspace_unmap_clients(global_conn, ws); + + if (c_ws == ws) { + DLOG("Need to adjust c_ws...\n"); + c_ws = screen->current_workspace; + } +} + /* * Initializes the given workspace if it is not already initialized. The given * screen is to be understood as a fallback, if the workspace itself either @@ -251,12 +294,16 @@ static i3Screen *get_screen_from_preference(struct screens_head *slist, char *pr * the screen is not attached at the moment. * */ -void workspace_initialize(Workspace *ws, i3Screen *screen) { - if (ws->screen != NULL) { +void workspace_initialize(Workspace *ws, i3Screen *screen, bool recheck) { + i3Screen *old_screen; + + if (ws->screen != NULL && !recheck) { DLOG("Workspace already initialized\n"); return; } + old_screen = ws->screen; + /* If this workspace has no preferred screen or if the screen it wants * to be on is not available at the moment, we initialize it with * the screen which was given */ @@ -264,8 +311,12 @@ void workspace_initialize(Workspace *ws, i3Screen *screen) { (ws->screen = get_screen_from_preference(virtual_screens, ws->preferred_screen)) == NULL) ws->screen = screen; - /* Copy the dimensions from the virtual screen */ - memcpy(&(ws->rect), &(ws->screen->rect), sizeof(Rect)); + DLOG("old_screen = %p, ws->screen = %p\n", old_screen, ws->screen); + /* If the assignment did not change, we do not need to update anything */ + if (old_screen != NULL && ws->screen == old_screen) + return; + + workspace_assign_to(ws, ws->screen); } /* @@ -308,7 +359,7 @@ Workspace *get_first_workspace_for_screen(struct screens_head *slist, i3Screen * result = workspace_get(last_ws + 1); } - workspace_initialize(result, screen); + workspace_initialize(result, screen, false); return result; } diff --git a/src/xinerama.c b/src/xinerama.c index 68c90ac4..81359ec2 100644 --- a/src/xinerama.c +++ b/src/xinerama.c @@ -203,12 +203,14 @@ static void query_screens(xcb_connection_t *conn, struct screens_head *screenlis for (int screen = 0; screen < screens; screen++) { i3Screen *s = get_screen_at(screen_info[screen].x_org, screen_info[screen].y_org, screenlist); if (s != NULL) { + DLOG("Re-used old Xinerama screen %p\n", s); /* This screen already exists. We use the littlest screen so that the user can always see the complete workspace */ s->rect.width = min(s->rect.width, screen_info[screen].width); s->rect.height = min(s->rect.height, screen_info[screen].height); } else { s = calloc(sizeof(i3Screen), 1); + DLOG("Created new Xinerama screen %p\n", s); s->rect.x = screen_info[screen].x_org; s->rect.y = screen_info[screen].y_org; s->rect.width = screen_info[screen].width; @@ -331,7 +333,7 @@ void xinerama_requery_screens(xcb_connection_t *conn) { Rect bar_rect = {screen->rect.x, screen->rect.y + screen->rect.height - (font->height + 6), - screen->rect.x + screen->rect.width, + screen->rect.width, font->height + 6}; DLOG("configuring bar to be at %d x %d with %d x %d\n", @@ -401,34 +403,13 @@ void xinerama_requery_screens(xcb_connection_t *conn) { if (ws->reassigned) continue; - Client *client; - DLOG("Closing bar window (%p)\n", ws->screen->bar); xcb_destroy_window(conn, ws->screen->bar); DLOG("Workspace %d's screen out of bounds, assigning to first screen\n", ws->num + 1); - ws->screen = first; - memcpy(&(ws->rect), &(first->rect), sizeof(Rect)); - - /* Force reconfiguration for each client on that workspace */ - FOR_TABLE(ws) - CIRCLEQ_FOREACH(client, &(ws->table[cols][rows]->clients), clients) - client->force_reconfigure = true; - - /* Render the workspace to reconfigure the clients. However, they will be visible now, so… */ - render_workspace(conn, first, ws); - - /* …unless we want to see them at the moment, we should hide that workspace */ - if (workspace_is_visible(ws)) - continue; - - workspace_unmap_clients(conn, ws); - - if (c_ws == ws) { - DLOG("Need to adjust c_ws...\n"); - c_ws = first->current_workspace; - } + workspace_assign_to(ws, first); } + xcb_flush(conn); /* Free the old list */ @@ -441,6 +422,15 @@ void xinerama_requery_screens(xcb_connection_t *conn) { virtual_screens = new_screens; + /* Check for workspaces which need to be assigned to specific screens + * which may now be available */ + TAILQ_FOREACH(ws, workspaces, workspaces) { + if (ws->preferred_screen == NULL) + continue; + + workspace_initialize(ws, ws->screen, true); + } + DLOG("Current workspace is now: %d\n", first->current_workspace); render_layout(conn);