Bugfix: Force reconfiguration of all windows on workspaces which needed to be re-assigned (Thanks Mirko)
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
This commit is contained in:
parent
8c6c4270a9
commit
d24e47a0f3
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
32
src/layout.c
32
src/layout.c
|
@ -481,23 +481,11 @@ void ignore_enter_notify_forall(xcb_connection_t *conn, Workspace *workspace, bo
|
|||
}
|
||||
|
||||
/*
|
||||
* Renders the whole layout, that is: Go through each screen, each workspace, each container
|
||||
* and render each client. This also renders the bars.
|
||||
*
|
||||
* If you don’t need to render *everything*, you should call render_container on the container
|
||||
* you want to refresh.
|
||||
* Renders the given workspace on the given screen
|
||||
*
|
||||
*/
|
||||
void render_layout(xcb_connection_t *conn) {
|
||||
i3Screen *screen;
|
||||
void render_workspace(xcb_connection_t *conn, i3Screen *screen, Workspace *r_ws) {
|
||||
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;
|
||||
|
||||
|
@ -562,6 +550,22 @@ void render_layout(xcb_connection_t *conn) {
|
|||
|
||||
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.
|
||||
*
|
||||
* If you don’t need to render *everything*, you should call render_container on the container
|
||||
* you want to refresh.
|
||||
*
|
||||
*/
|
||||
void render_layout(xcb_connection_t *conn) {
|
||||
i3Screen *screen;
|
||||
|
||||
TAILQ_FOREACH(screen, virtual_screens, screens) {
|
||||
LOG("Rendering screen %d\n", screen->num);
|
||||
render_workspace(conn, screen, &(workspaces[screen->current_workspace]));
|
||||
}
|
||||
|
||||
xcb_flush(conn);
|
||||
|
|
36
src/util.c
36
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
|
||||
|
|
|
@ -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)) {
|
||||
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, workspaces[c].screen->bar);
|
||||
xcb_destroy_window(conn, f_ws->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));
|
||||
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)) {
|
||||
|
|
Loading…
Reference in New Issue