diff --git a/include/layout.h b/include/layout.h index 40df0ad1..7750e75f 100644 --- a/include/layout.h +++ b/include/layout.h @@ -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. diff --git a/include/util.h b/include/util.h index 3ea2b3a7..889dcf12 100644 --- a/include/util.h +++ b/include/util.h @@ -22,8 +22,8 @@ #define CIRCLEQ_PREV_OR_NULL(head, elm, field) (CIRCLEQ_PREV(elm, field) != CIRCLEQ_END(head) ? \ CIRCLEQ_PREV(elm, field) : NULL) #define FOR_TABLE(workspace) \ - for (int cols = 0; cols < workspace->cols; cols++) \ - for (int rows = 0; rows < workspace->rows; rows++) + for (int cols = 0; cols < (workspace)->cols; cols++) \ + for (int rows = 0; rows < (workspace)->rows; rows++) #define FREE(pointer) do { \ if (pointer == NULL) { \ free(pointer); \ @@ -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 diff --git a/src/commands.c b/src/commands.c index 09c21b34..4e973405 100644 --- a/src/commands.c +++ b/src/commands.c @@ -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); } diff --git a/src/layout.c b/src/layout.c index 033e2a4c..23385987 100644 --- a/src/layout.c +++ b/src/layout.c @@ -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); diff --git a/src/util.c b/src/util.c index 6a4b5fa2..47d8c542 100644 --- a/src/util.c +++ b/src/util.c @@ -254,6 +254,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 diff --git a/src/xinerama.c b/src/xinerama.c index 3a1b19a7..818df00a 100644 --- a/src/xinerama.c +++ b/src/xinerama.c @@ -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); + for (int c = 0; c < 10; c++) { + if ((workspaces[c].screen == NULL) || (workspaces[c].screen->num < num_screens)) + continue; - 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)); - } + /* 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)) {