]> git.sur5r.net Git - i3/i3/commitdiff
Bugfix: Force reconfiguration of all windows on workspaces which needed to be re...
authorMichael Stapelberg <michael+x200@stapelberg.de>
Sat, 9 May 2009 11:01:23 +0000 (13:01 +0200)
committerMichael Stapelberg <michael+x200@stapelberg.de>
Sat, 9 May 2009 11:04:34 +0000 (13:04 +0200)
When you disable a Xinerama screen (think of removing a video projector),
the workspaces of that screen need to be re-assigned to another screen.
Previously, the clients affected by this re-assignment did not get re-
configured, which made them appear on the next screen which got configured
at the position of the old one again if you did not switch to the reassigned
workspace before.

So, to reproduce it:
xrandr --output VGA --mode 1280x1024 --right-of LVDS
move windows to the new workspace
xrandr --output VGA --off
xrandr --output VGA --mode 1280x1024 --right-of LVDS

This fixes ticket #36

include/layout.h
include/util.h
src/commands.c
src/layout.c
src/util.c
src/xinerama.c

index 40df0ad1bee8d6d9e644b6008e4185caea011523..7750e75f4813485bc848dce3ef0400a2437cb3c6 100644 (file)
@@ -51,6 +51,12 @@ void render_container(xcb_connection_t *conn, Container *container);
  */
 void ignore_enter_notify_forall(xcb_connection_t *conn, Workspace *workspace, bool ignore_enter_notify);
 
+/**
+ * Renders the given workspace on the given screen
+ *
+ */
+void render_workspace(xcb_connection_t *conn, i3Screen *screen, Workspace *r_ws);
+
 /**
  * Renders the whole layout, that is: Go through each screen, each workspace, each container
  * and render each client. This also renders the bars.
index fba1206f916a29eb6f0d949e3074a60633fe03e5..889dcf122f175b53d2b10815b23d425ee81cbaa2 100644 (file)
@@ -137,6 +137,17 @@ void remove_client_from_container(xcb_connection_t *conn, Client *client, Contai
  */
 Client *get_last_focused_client(xcb_connection_t *conn, Container *container, Client *exclude);
 
+/**
+ * Unmaps all clients (and stack windows) of the given workspace.
+ *
+ * This needs to be called separately when temporarily rendering
+ * a workspace which is not the active workspace to force
+ * reconfiguration of all clients, like in src/xinerama.c when
+ * re-assigning a workspace to another screen.
+ *
+ */
+void unmap_workspace(xcb_connection_t *conn, Workspace *u_ws);
+
 /**
  * Sets the given client as focused by updating the data structures correctly,
  * updating the X input focus and finally re-decorating both windows (to signalize
index c0f5ffdd562042d7aab1388d656e9a2a161daf52..bb8a3a89a90fa153dce931f0523d2df5184eb194 100644 (file)
@@ -527,30 +527,8 @@ void show_workspace(xcb_connection_t *conn, int workspace) {
 
         t_ws->screen->current_workspace = workspace-1;
 
-        /* TODO: does grabbing the server actually bring us any (speed)advantages? */
-        //xcb_grab_server(conn);
-
-        ignore_enter_notify_forall(conn, c_ws, true);
-
         /* Unmap all clients of the current workspace */
-        int unmapped_clients = 0;
-        FOR_TABLE(c_ws)
-                CIRCLEQ_FOREACH(client, &(c_ws->table[cols][rows]->clients), clients) {
-                        xcb_unmap_window(conn, client->frame);
-                        unmapped_clients++;
-                }
-
-        /* If we did not unmap any clients, the workspace is empty and we can destroy it */
-        if (unmapped_clients == 0)
-                c_ws->screen = NULL;
-
-        /* Unmap the stack windows on the current workspace, if any */
-        struct Stack_Window *stack_win;
-        SLIST_FOREACH(stack_win, &stack_wins, stack_windows)
-                if (stack_win->container->workspace == c_ws)
-                        xcb_unmap_window(conn, stack_win->window);
-
-        ignore_enter_notify_forall(conn, c_ws, false);
+        unmap_workspace(conn, c_ws);
 
         c_ws = &workspaces[workspace-1];
         current_row = c_ws->current_row;
@@ -565,6 +543,7 @@ void show_workspace(xcb_connection_t *conn, int workspace) {
                         xcb_map_window(conn, client->frame);
 
         /* Map all stack windows, if any */
+        struct Stack_Window *stack_win;
         SLIST_FOREACH(stack_win, &stack_wins, stack_windows)
                 if (stack_win->container->workspace == c_ws)
                         xcb_map_window(conn, stack_win->window);
@@ -580,8 +559,6 @@ void show_workspace(xcb_connection_t *conn, int workspace) {
                 }
         } else xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, root, XCB_CURRENT_TIME);
 
-        //xcb_ungrab_server(conn);
-
         render_layout(conn);
 }
 
index 033e2a4ce85aa0581c3b5c2798ca1022da93bcce..233859879f95d2ea1a0f5d9a749cfc492d42e311 100644 (file)
@@ -480,6 +480,78 @@ void ignore_enter_notify_forall(xcb_connection_t *conn, Workspace *workspace, bo
                 }
 }
 
+/*
+ * Renders the given workspace on the given screen
+ *
+ */
+void render_workspace(xcb_connection_t *conn, i3Screen *screen, Workspace *r_ws) {
+        i3Font *font = load_font(conn, config.font);
+        int width = r_ws->rect.width;
+        int height = r_ws->rect.height;
+
+        /* Reserve space for dock clients */
+        Client *client;
+        SLIST_FOREACH(client, &(screen->dock_clients), dock_clients)
+                height -= client->desired_height;
+
+        /* Space for the internal bar */
+        height -= (font->height + 6);
+
+        LOG("got %d rows and %d cols\n", r_ws->rows, r_ws->cols);
+
+        int xoffset[r_ws->rows];
+        int yoffset[r_ws->cols];
+        /* Initialize offsets */
+        for (int cols = 0; cols < r_ws->cols; cols++)
+                yoffset[cols] = r_ws->rect.y;
+        for (int rows = 0; rows < r_ws->rows; rows++)
+                xoffset[rows] = r_ws->rect.x;
+
+        dump_table(conn, r_ws);
+
+        ignore_enter_notify_forall(conn, r_ws, true);
+
+        /* Go through the whole table and render what’s necessary */
+        FOR_TABLE(r_ws) {
+                Container *container = r_ws->table[cols][rows];
+                int single_width, single_height;
+                LOG("\n");
+                LOG("========\n");
+                LOG("container has %d colspan, %d rowspan\n",
+                                container->colspan, container->rowspan);
+                LOG("container at %d, %d\n", xoffset[rows], yoffset[cols]);
+                /* Update position of the container */
+                container->row = rows;
+                container->col = cols;
+                container->x = xoffset[rows];
+                container->y = yoffset[cols];
+
+                if (container->width_factor == 0)
+                        container->width = (width / r_ws->cols);
+                else container->width = get_unoccupied_x(r_ws, rows) * container->width_factor;
+                single_width = container->width;
+                container->width *= container->colspan;
+
+                if (container->height_factor == 0)
+                        container->height = (height / r_ws->rows);
+                else container->height = get_unoccupied_y(r_ws, cols) * container->height_factor;
+                single_height = container->height;
+                container->height *= container->rowspan;
+
+                /* Render the container if it is not empty */
+                render_container(conn, container);
+
+                xoffset[rows] += single_width;
+                yoffset[cols] += single_height;
+                LOG("==========\n");
+        }
+
+        ignore_enter_notify_forall(conn, r_ws, false);
+
+        render_bars(conn, r_ws, width, &height);
+        render_internal_bar(conn, r_ws, width, font->height + 6);
+}
+
 /*
  * Renders the whole layout, that is: Go through each screen, each workspace, each container
  * and render each client. This also renders the bars.
@@ -490,78 +562,10 @@ void ignore_enter_notify_forall(xcb_connection_t *conn, Workspace *workspace, bo
  */
 void render_layout(xcb_connection_t *conn) {
         i3Screen *screen;
-        i3Font *font = load_font(conn, config.font);
 
         TAILQ_FOREACH(screen, virtual_screens, screens) {
-                /* r_ws (rendering workspace) is just a shortcut to the Workspace being currently rendered */
-                Workspace *r_ws = &(workspaces[screen->current_workspace]);
-
                 LOG("Rendering screen %d\n", screen->num);
-
-                int width = r_ws->rect.width;
-                int height = r_ws->rect.height;
-
-                /* Reserve space for dock clients */
-                Client *client;
-                SLIST_FOREACH(client, &(screen->dock_clients), dock_clients)
-                        height -= client->desired_height;
-
-                /* Space for the internal bar */
-                height -= (font->height + 6);
-
-                LOG("got %d rows and %d cols\n", r_ws->rows, r_ws->cols);
-
-                int xoffset[r_ws->rows];
-                int yoffset[r_ws->cols];
-                /* Initialize offsets */
-                for (int cols = 0; cols < r_ws->cols; cols++)
-                        yoffset[cols] = r_ws->rect.y;
-                for (int rows = 0; rows < r_ws->rows; rows++)
-                        xoffset[rows] = r_ws->rect.x;
-
-                dump_table(conn, r_ws);
-
-                ignore_enter_notify_forall(conn, r_ws, true);
-
-                /* Go through the whole table and render what’s necessary */
-                FOR_TABLE(r_ws) {
-                        Container *container = r_ws->table[cols][rows];
-                        int single_width, single_height;
-                        LOG("\n");
-                        LOG("========\n");
-                        LOG("container has %d colspan, %d rowspan\n",
-                                        container->colspan, container->rowspan);
-                        LOG("container at %d, %d\n", xoffset[rows], yoffset[cols]);
-                        /* Update position of the container */
-                        container->row = rows;
-                        container->col = cols;
-                        container->x = xoffset[rows];
-                        container->y = yoffset[cols];
-
-                        if (container->width_factor == 0)
-                                container->width = (width / r_ws->cols);
-                        else container->width = get_unoccupied_x(r_ws, rows) * container->width_factor;
-                        single_width = container->width;
-                        container->width *= container->colspan;
-
-                        if (container->height_factor == 0)
-                                container->height = (height / r_ws->rows);
-                        else container->height = get_unoccupied_y(r_ws, cols) * container->height_factor;
-                        single_height = container->height;
-                        container->height *= container->rowspan;
-
-                        /* Render the container if it is not empty */
-                        render_container(conn, container);
-
-                        xoffset[rows] += single_width;
-                        yoffset[cols] += single_height;
-                        LOG("==========\n");
-                }
-
-                ignore_enter_notify_forall(conn, r_ws, false);
-
-                render_bars(conn, r_ws, width, &height);
-                render_internal_bar(conn, r_ws, width, font->height + 6);
+                render_workspace(conn, screen, &(workspaces[screen->current_workspace]));
         }
 
         xcb_flush(conn);
index d4203261eadaa55bf014db402b69bcfaeda0296f..c0e74c75b216c7d199dc393de168234c66cdb787 100644 (file)
@@ -256,6 +256,42 @@ Client *get_last_focused_client(xcb_connection_t *conn, Container *container, Cl
         return NULL;
 }
 
+/*
+ * Unmaps all clients (and stack windows) of the given workspace.
+ *
+ * This needs to be called separately when temporarily rendering
+ * a workspace which is not the active workspace to force
+ * reconfiguration of all clients, like in src/xinerama.c when
+ * re-assigning a workspace to another screen.
+ *
+ */
+void unmap_workspace(xcb_connection_t *conn, Workspace *u_ws) {
+        Client *client;
+        struct Stack_Window *stack_win;
+
+        /* Ignore notify events because they would cause focus to be changed */
+        ignore_enter_notify_forall(conn, u_ws, true);
+
+        /* Unmap all clients of the current workspace */
+        int unmapped_clients = 0;
+        FOR_TABLE(u_ws)
+                CIRCLEQ_FOREACH(client, &(u_ws->table[cols][rows]->clients), clients) {
+                        xcb_unmap_window(conn, client->frame);
+                        unmapped_clients++;
+                }
+
+        /* If we did not unmap any clients, the workspace is empty and we can destroy it */
+        if (unmapped_clients == 0)
+                u_ws->screen = NULL;
+
+        /* Unmap the stack windows on the current workspace, if any */
+        SLIST_FOREACH(stack_win, &stack_wins, stack_windows)
+                if (stack_win->container->workspace == u_ws)
+                        xcb_unmap_window(conn, stack_win->window);
+
+        ignore_enter_notify_forall(conn, u_ws, false);
+}
+
 /*
  * Sets the given client as focused by updating the data structures correctly,
  * updating the X input focus and finally re-decorating both windows (to signalize
index 3a1b19a797265beb2959e192f57a848f35fd14f4..818df00abf70114e1c5fb8bfbf9a5da2efec493a 100644 (file)
@@ -304,16 +304,36 @@ void xinerama_requery_screens(xcb_connection_t *conn) {
         }
 
         /* Check for workspaces which are out of bounds */
-        for (int c = 0; c < 10; c++)
-                if ((workspaces[c].screen != NULL) &&
-                    (workspaces[c].screen->num >= num_screens)) {
-                        LOG("Closing bar window\n");
-                        xcb_destroy_window(conn, workspaces[c].screen->bar);
-
-                        LOG("Workspace %d's screen out of bounds, assigning to first screen\n", c+1);
-                        workspaces[c].screen = first;
-                        memcpy(&(workspaces[c].rect), &(first->rect), sizeof(Rect));
-                }
+        for (int c = 0; c < 10; c++) {
+                if ((workspaces[c].screen == NULL) || (workspaces[c].screen->num < num_screens))
+                        continue;
+
+                /* f_ws is a shortcut to the workspace to fix */
+                Workspace *f_ws = &(workspaces[c]);
+                Client *client;
+
+                LOG("Closing bar window\n");
+                xcb_destroy_window(conn, f_ws->screen->bar);
+
+                LOG("Workspace %d's screen out of bounds, assigning to first screen\n", c+1);
+                f_ws->screen = first;
+                memcpy(&(f_ws->rect), &(first->rect), sizeof(Rect));
+
+                /* Force reconfiguration for each client on that workspace */
+                FOR_TABLE(f_ws)
+                        CIRCLEQ_FOREACH(client, &(f_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, f_ws);
+
+                /* …unless we want to see them at the moment, we should hide that workspace */
+                if (first->current_workspace == c)
+                        continue;
+
+                unmap_workspace(conn, f_ws);
+        }
+        xcb_flush(conn);
 
         /* Free the old list */
         while (!TAILQ_EMPTY(virtual_screens)) {