bool focus_window_in_container(xcb_connection_t *conn, Container *container,
direction_t direction);
-/** Switches to the given workspace */
-void show_workspace(xcb_connection_t *conn, int workspace);
-
/** Parses a command, see file CMDMODE for more information */
void parse_command(xcb_connection_t *conn, const char *command);
/** Are the floating clients on this workspace currently hidden? */
bool floating_hidden;
+ /** A <screen> specifier on which this workspace would like to be (if
+ * the screen is available). screen := <number> | <position> */
+ char *preferred_screen;
+
+ /** Temporary flag needed for re-querying xinerama screens */
+ bool reassigned;
+
/** the client who is started in fullscreen mode on this workspace,
* NULL if there is none */
Client *fullscreen_client;
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);
-
-/**
- * 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
#include <xcb/xcb.h>
#include "data.h"
+#include "xinerama.h"
#ifndef _WORKSPACE_H
#define _WORKSPACE_H
*/
bool workspace_is_visible(Workspace *ws);
+/** Switches to the given workspace */
+void workspace_show(xcb_connection_t *conn, int 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
+ * was not assigned to a particular screen or cannot be placed there because
+ * the screen is not attached at the moment.
+ *
+ */
+void workspace_initialize(Workspace *ws, i3Screen *screen);
+
+/**
+ * Gets the first unused workspace for the given screen, taking into account
+ * the preferred_screen setting of every workspace (workspace assignments).
+ *
+ */
+Workspace *get_first_workspace_for_screen(struct screens_head *slist, i3Screen *screen);
+
+/**
+ * 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 workspace_unmap_clients(xcb_connection_t *conn, Workspace *u_ws);
+
+
+void workspace_map_clients(xcb_connection_t *conn, Workspace *ws);
+
#endif
TAILQ_HEAD(screens_head, Screen);
extern struct screens_head *virtual_screens;
+/**
+ * Returns true if both screen objects describe the same screen (checks their
+ * size and position).
+ *
+ */
+bool screens_are_equal(i3Screen *screen1, i3Screen *screen2);
+
/**
* We have just established a connection to the X server and need the initial
* Xinerama information to setup workspaces for each screen.
}
LOG("Switching to ws %d\n", target->current_workspace + 1);
- show_workspace(conn, target->current_workspace + 1);
+ workspace_show(conn, target->current_workspace + 1);
return;
}
set_focus(conn, current_client, true);
}
-/*
- * Switches to the given workspace
- *
- */
-void show_workspace(xcb_connection_t *conn, int workspace) {
- Client *client;
- bool need_warp = false;
- xcb_window_t root = xcb_setup_roots_iterator(xcb_get_setup(conn)).data->root;
- /* t_ws (to workspace) is just a convenience pointer to the workspace we’re switching to */
- Workspace *t_ws = &(workspaces[workspace-1]);
-
- LOG("show_workspace(%d)\n", workspace);
-
- /* Store current_row/current_col */
- c_ws->current_row = current_row;
- c_ws->current_col = current_col;
-
- /* Check if the workspace has not been used yet */
- if (t_ws->screen == NULL) {
- LOG("initializing new workspace, setting num to %d\n", workspace);
- t_ws->screen = c_ws->screen;
- /* Copy the dimensions from the virtual screen */
- memcpy(&(t_ws->rect), &(t_ws->screen->rect), sizeof(Rect));
- }
-
- if (c_ws->screen != t_ws->screen) {
- /* We need to switch to the other screen first */
- LOG("moving over to other screen.\n");
-
- /* Store the old client */
- Client *old_client = CUR_CELL->currently_focused;
-
- c_ws = &(workspaces[t_ws->screen->current_workspace]);
- current_col = c_ws->current_col;
- current_row = c_ws->current_row;
- if (CUR_CELL->currently_focused != NULL)
- need_warp = true;
- else {
- Rect *dims = &(c_ws->screen->rect);
- xcb_warp_pointer(conn, XCB_NONE, root, 0, 0, 0, 0,
- dims->x + (dims->width / 2), dims->y + (dims->height / 2));
- }
-
- /* Re-decorate the old client, it’s not focused anymore */
- if ((old_client != NULL) && !old_client->dock)
- redecorate_window(conn, old_client);
- else xcb_flush(conn);
- }
-
- /* Check if we need to change something or if we’re already there */
- if (c_ws->screen->current_workspace == (workspace-1)) {
- Client *last_focused = SLIST_FIRST(&(c_ws->focus_stack));
- if (last_focused != SLIST_END(&(c_ws->focus_stack))) {
- set_focus(conn, last_focused, true);
- if (need_warp) {
- client_warp_pointer_into(conn, last_focused);
- xcb_flush(conn);
- }
- }
-
- return;
- }
-
- t_ws->screen->current_workspace = workspace-1;
- Workspace *old_workspace = c_ws;
- c_ws = &workspaces[workspace-1];
-
- /* Unmap all clients of the old workspace */
- unmap_workspace(conn, old_workspace);
-
- current_row = c_ws->current_row;
- current_col = c_ws->current_col;
- LOG("new current row = %d, current col = %d\n", current_row, current_col);
-
- ignore_enter_notify_forall(conn, c_ws, true);
-
- /* Map all clients on the new workspace */
- FOR_TABLE(c_ws)
- CIRCLEQ_FOREACH(client, &(c_ws->table[cols][rows]->clients), clients)
- xcb_map_window(conn, client->frame);
-
- /* Map all floating clients */
- if (!c_ws->floating_hidden)
- TAILQ_FOREACH(client, &(c_ws->floating_clients), floating_clients)
- 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);
-
- ignore_enter_notify_forall(conn, c_ws, false);
-
- /* Restore focus on the new workspace */
- Client *last_focused = SLIST_FIRST(&(c_ws->focus_stack));
- if (last_focused != SLIST_END(&(c_ws->focus_stack))) {
- set_focus(conn, last_focused, true);
- if (need_warp) {
- client_warp_pointer_into(conn, last_focused);
- xcb_flush(conn);
- }
- } else xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, root, XCB_CURRENT_TIME);
-
- render_layout(conn);
-}
-
/*
* Jumps to the given window class / title.
* Title is matched using strstr, that is, matches if it appears anywhere
}
/* Move to the target workspace */
- show_workspace(conn, ws);
+ workspace_show(conn, ws);
if (result < 3)
return;
}
if (t_ws->screen != NULL)
- show_workspace(conn, i+1);
+ workspace_show(conn, i+1);
}
/*
if (*rest == '\0') {
/* No rest? This was a workspace number, not a times specification */
- show_workspace(conn, times);
+ workspace_show(conn, times);
return;
}
continue;
}
- /* name "workspace number" "name of the workspace" */
- if (strcasecmp(key, "name") == 0) {
- LOG("name workspace: %s\n",value);
+ /* workspace "workspace number" [screen <screen>] ["name of the workspace"]
+ * with screen := <number> | <position>, e.g. screen 1280 or screen 1 */
+ if (strcasecmp(key, "name") == 0 || strcasecmp(key, "workspace") == 0) {
+ LOG("workspace: %s\n",value);
char *ws_str = sstrdup(value);
char *end = strchr(ws_str, ' ');
if (end == NULL)
char *name = value;
name += strlen(ws_str) + 1;
+ if (strncasecmp(name, "screen ", strlen("screen ")) == 0) {
+ char *screen = strdup(name + strlen("screen "));
+ if ((end = strchr(screen, ' ')) != NULL)
+ *end = '\0';
+ LOG("Setting preferred screen for workspace %d to \"%s\"\n", ws_num, screen);
+ workspaces[ws_num - 1].preferred_screen = screen;
+
+ name += strlen("screen ") + strlen(screen);
+
+ }
+
+ /* Strip leading whitespace */
+ while (*name != '\0' && *name == ' ')
+ name++;
+
+ LOG("rest to parse = %s\n", name);
+
if (name == '\0') {
free(ws_str);
continue;
}
+ LOG("setting name to \"%s\"\n", name);
+
workspace_set_name(&(workspaces[ws_num - 1]), name);
free(ws_str);
continue;
#include "client.h"
#include "manage.h"
#include "floating.h"
+#include "workspace.h"
/* After mapping/unmapping windows, a notify event is generated. However, we don’t want it,
since it’d trigger an infinite loop of switching between the different windows when
int add = (event->detail == XCB_BUTTON_INDEX_4 ? -1 : 1);
for (int i = c_ws->num + add; (i >= 0) && (i < 10); i += add)
if (workspaces[i].screen == screen) {
- show_workspace(conn, i+1);
+ workspace_show(conn, i+1);
return true;
}
return true;
i, drawn, workspaces[i].text_width);
if (event->event_x > (drawn + 1) &&
event->event_x <= (drawn + 1 + workspaces[i].text_width + 5 + 5)) {
- show_workspace(conn, i+1);
+ workspace_show(conn, i+1);
return true;
}
SLIST_FOREACH(client, &(r_ws->screen->dock_clients), dock_clients) {
LOG("client is at %d, should be at %d\n", client->rect.y, *height);
if (client->force_reconfigure |
- update_if_necessary(&(client->rect.x), 0) |
+ update_if_necessary(&(client->rect.x), r_ws->rect.x) |
update_if_necessary(&(client->rect.y), *height))
reposition_client(conn, client);
xcb_flush(conn);
- manage_existing_windows(conn, &prophs, root);
-
/* Get pointer position to see on which screen we’re starting */
xcb_query_pointer_reply_t *reply;
if ((reply = xcb_query_pointer_reply(conn, xcb_query_pointer(conn, root), NULL)) == NULL) {
LOG("ERROR: No screen at %d x %d\n", reply->root_x, reply->root_y);
return 0;
}
- if (screen->current_workspace != 0) {
- LOG("Ok, I need to go to the other workspace\n");
- c_ws = &workspaces[screen->current_workspace];
- }
+
+ LOG("Starting on %d\n", screen->current_workspace);
+ c_ws = &workspaces[screen->current_workspace];
+
+ manage_existing_windows(conn, &prophs, root);
/* Create the UNIX domain socket for IPC */
if (config.ipc_socket_path != NULL) {
LOG("Changing container/workspace and unmapping the client\n");
Workspace *t_ws = &(workspaces[assign->workspace-1]);
- if (t_ws->screen == NULL) {
- LOG("initializing new workspace, setting num to %d\n", assign->workspace);
- t_ws->screen = c_ws->screen;
- /* Copy the dimensions from the virtual screen */
- memcpy(&(t_ws->rect), &(t_ws->screen->rect), sizeof(Rect));
- }
+ workspace_initialize(t_ws, c_ws->screen);
new->container = t_ws->table[t_ws->current_col][t_ws->current_row];
new->workspace = t_ws;
/* Map the window first to avoid flickering */
xcb_map_window(conn, child);
- if (map_frame)
+ if (map_frame) {
+ LOG("Mapping client\n");
xcb_map_window(conn, new->frame);
+ }
if (CUR_CELL->workspace->fullscreen_client == NULL && !new->dock) {
/* Focus the new window if we’re not in fullscreen mode and if it is not a dock window */
if (new->workspace->fullscreen_client == NULL) {
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 given workspace */
- int unmapped_clients = 0;
- FOR_TABLE(u_ws)
- CIRCLEQ_FOREACH(client, &(u_ws->table[cols][rows]->clients), clients) {
- LOG("unmapping normal client %p / %p / %p\n", client, client->frame, client->child);
- xcb_unmap_window(conn, client->frame);
- unmapped_clients++;
- }
-
- /* To find floating clients, we traverse the focus stack */
- SLIST_FOREACH(client, &(u_ws->focus_stack), focus_clients) {
- if (!client_is_floating(client))
- continue;
-
- LOG("unmapping floating client %p / %p / %p\n", client, client->frame, client->child);
-
- xcb_unmap_window(conn, client->frame);
- unmapped_clients++;
- }
-
- /* If we did not unmap any clients, the workspace is empty and we can destroy it, at least
- * if it is not the current workspace. */
- if (unmapped_clients == 0 && u_ws != c_ws) {
- /* Re-assign the workspace of all dock clients which use this workspace */
- Client *dock;
- LOG("workspace %p is empty\n", u_ws);
- SLIST_FOREACH(dock, &(u_ws->screen->dock_clients), dock_clients) {
- if (dock->workspace != u_ws)
- continue;
-
- LOG("Re-assigning dock client to c_ws (%p)\n", c_ws);
- dock->workspace = c_ws;
- }
- u_ws->screen = NULL;
- }
-
- /* Unmap the stack windows on the given 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,
*/
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
#include <err.h>
#include "util.h"
#include "i3.h"
#include "config.h"
#include "xcb.h"
+#include "table.h"
+#include "xinerama.h"
+#include "layout.h"
+#include "workspace.h"
+#include "client.h"
/*
* Sets the name (or just its number) for the given workspace. This has to
bool workspace_is_visible(Workspace *ws) {
return (ws->screen->current_workspace == ws->num);
}
+
+/*
+ * Switches to the given workspace
+ *
+ */
+void workspace_show(xcb_connection_t *conn, int workspace) {
+ bool need_warp = false;
+ xcb_window_t root = xcb_setup_roots_iterator(xcb_get_setup(conn)).data->root;
+ /* t_ws (to workspace) is just a convenience pointer to the workspace we’re switching to */
+ Workspace *t_ws = &(workspaces[workspace-1]);
+
+ LOG("show_workspace(%d)\n", workspace);
+
+ /* Store current_row/current_col */
+ c_ws->current_row = current_row;
+ c_ws->current_col = current_col;
+
+ /* Check if the workspace has not been used yet */
+ workspace_initialize(t_ws, c_ws->screen);
+
+ if (c_ws->screen != t_ws->screen) {
+ /* We need to switch to the other screen first */
+ LOG("moving over to other screen.\n");
+
+ /* Store the old client */
+ Client *old_client = CUR_CELL->currently_focused;
+
+ c_ws = &(workspaces[t_ws->screen->current_workspace]);
+ current_col = c_ws->current_col;
+ current_row = c_ws->current_row;
+ if (CUR_CELL->currently_focused != NULL)
+ need_warp = true;
+ else {
+ Rect *dims = &(c_ws->screen->rect);
+ xcb_warp_pointer(conn, XCB_NONE, root, 0, 0, 0, 0,
+ dims->x + (dims->width / 2), dims->y + (dims->height / 2));
+ }
+
+ /* Re-decorate the old client, it’s not focused anymore */
+ if ((old_client != NULL) && !old_client->dock)
+ redecorate_window(conn, old_client);
+ else xcb_flush(conn);
+ }
+
+ /* Check if we need to change something or if we’re already there */
+ if (c_ws->screen->current_workspace == (workspace-1)) {
+ Client *last_focused = SLIST_FIRST(&(c_ws->focus_stack));
+ if (last_focused != SLIST_END(&(c_ws->focus_stack))) {
+ set_focus(conn, last_focused, true);
+ if (need_warp) {
+ client_warp_pointer_into(conn, last_focused);
+ xcb_flush(conn);
+ }
+ }
+
+ return;
+ }
+
+ t_ws->screen->current_workspace = workspace-1;
+ Workspace *old_workspace = c_ws;
+ c_ws = &workspaces[workspace-1];
+
+ /* Unmap all clients of the old workspace */
+ workspace_unmap_clients(conn, old_workspace);
+
+ current_row = c_ws->current_row;
+ current_col = c_ws->current_col;
+ LOG("new current row = %d, current col = %d\n", current_row, current_col);
+
+ workspace_map_clients(conn, c_ws);
+
+ /* Restore focus on the new workspace */
+ Client *last_focused = SLIST_FIRST(&(c_ws->focus_stack));
+ if (last_focused != SLIST_END(&(c_ws->focus_stack))) {
+ set_focus(conn, last_focused, true);
+ if (need_warp) {
+ client_warp_pointer_into(conn, last_focused);
+ xcb_flush(conn);
+ }
+ } else xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, root, XCB_CURRENT_TIME);
+
+ render_layout(conn);
+}
+
+
+/*
+ * Parses the preferred_screen property of a workspace. You can either specify
+ * the screen number (it is not given that the screen numbering always stays
+ * the same) or the screen coordinates (exact coordinates, e.g. 1280 will match
+ * the screen starting at x=1280, but 1281 will not). For coordinates, you can
+ * either specify an x coordinate ("1280") or an y coordinate ("x800") or both
+ * ("1280x800").
+ *
+ */
+static i3Screen *get_screen_from_preference(struct screens_head *slist, char *preference) {
+ i3Screen *screen;
+ char *rest;
+ int preferred_screen = strtol(preference, &rest, 10);
+
+ LOG("Getting screen for preference \"%s\" (%d)\n", preference, preferred_screen);
+
+ if ((rest == preference) || (preferred_screen >= num_screens)) {
+ int x = INT_MAX, y = INT_MAX;
+ if (strchr(preference, 'x') != NULL) {
+ /* Check if only the y coordinate was specified */
+ if (*preference == 'x')
+ y = atoi(preference+1);
+ else {
+ x = atoi(preference);
+ y = atoi(strchr(preference, 'x') + 1);
+ }
+ } else {
+ x = atoi(preference);
+ }
+
+ LOG("Looking for screen at %d x %d\n", x, y);
+
+ TAILQ_FOREACH(screen, slist, screens)
+ if ((x == INT_MAX || screen->rect.x == x) &&
+ (y == INT_MAX || screen->rect.y == y)) {
+ LOG("found %p\n", screen);
+ return screen;
+ }
+
+ LOG("none found\n");
+ return NULL;
+ } else {
+ int c = 0;
+ TAILQ_FOREACH(screen, slist, screens)
+ if (c++ == preferred_screen)
+ return screen;
+ }
+
+ return NULL;
+}
+
+/*
+ * 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
+ * was not assigned to a particular screen or cannot be placed there because
+ * the screen is not attached at the moment.
+ *
+ */
+void workspace_initialize(Workspace *ws, i3Screen *screen) {
+ if (ws->screen != NULL) {
+ LOG("Workspace already initialized\n");
+ return;
+ }
+
+ /* 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 */
+ if (ws->preferred_screen == NULL ||
+ (ws->screen = get_screen_from_preference(virtual_screens, ws->preferred_screen)) == NULL)
+ ws->screen = screen;
+ else { LOG("yay, found assignment\n"); }
+
+ /* Copy the dimensions from the virtual screen */
+ memcpy(&(ws->rect), &(ws->screen->rect), sizeof(Rect));
+}
+
+/*
+ * Gets the first unused workspace for the given screen, taking into account
+ * the preferred_screen setting of every workspace (workspace assignments).
+ *
+ */
+Workspace *get_first_workspace_for_screen(struct screens_head *slist, i3Screen *screen) {
+ Workspace *result = NULL;
+
+ for (int c = 0; c < 10; c++) {
+ Workspace *ws = &(workspaces[c]);
+ if (ws->preferred_screen == NULL ||
+ !screens_are_equal(get_screen_from_preference(slist, ws->preferred_screen), screen))
+ continue;
+
+ result = ws;
+ break;
+ }
+
+ if (result == NULL) {
+ /* No assignment found, returning first unused workspace */
+ for (int c = 0; c < 10; c++) {
+ if (workspaces[c].screen != NULL)
+ continue;
+
+ result = &(workspaces[c]);
+ break;
+ }
+ }
+
+ if (result != NULL) {
+ workspace_initialize(result, screen);
+ return result;
+ }
+
+ LOG("WARNING: No free workspace found to assign!\n");
+ return NULL;
+}
+
+/*
+ * Maps all clients (and stack windows) of the given workspace.
+ *
+ */
+void workspace_map_clients(xcb_connection_t *conn, Workspace *ws) {
+ Client *client;
+
+ ignore_enter_notify_forall(conn, ws, true);
+
+ /* Map all clients on the new workspace */
+ FOR_TABLE(ws)
+ CIRCLEQ_FOREACH(client, &(ws->table[cols][rows]->clients), clients)
+ xcb_map_window(conn, client->frame);
+
+ /* Map all floating clients */
+ if (!ws->floating_hidden)
+ TAILQ_FOREACH(client, &(ws->floating_clients), floating_clients)
+ 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 == ws)
+ xcb_map_window(conn, stack_win->window);
+
+ ignore_enter_notify_forall(conn, ws, false);
+}
+
+/*
+ * 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 workspace_unmap_clients(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 given workspace */
+ int unmapped_clients = 0;
+ FOR_TABLE(u_ws)
+ CIRCLEQ_FOREACH(client, &(u_ws->table[cols][rows]->clients), clients) {
+ LOG("unmapping normal client %p / %p / %p\n", client, client->frame, client->child);
+ xcb_unmap_window(conn, client->frame);
+ unmapped_clients++;
+ }
+
+ /* To find floating clients, we traverse the focus stack */
+ SLIST_FOREACH(client, &(u_ws->focus_stack), focus_clients) {
+ if (!client_is_floating(client))
+ continue;
+
+ LOG("unmapping floating client %p / %p / %p\n", client, client->frame, client->child);
+
+ xcb_unmap_window(conn, client->frame);
+ unmapped_clients++;
+ }
+
+ /* If we did not unmap any clients, the workspace is empty and we can destroy it, at least
+ * if it is not the current workspace. */
+ if (unmapped_clients == 0 && u_ws != c_ws) {
+ /* Re-assign the workspace of all dock clients which use this workspace */
+ Client *dock;
+ LOG("workspace %p is empty\n", u_ws);
+ SLIST_FOREACH(dock, &(u_ws->screen->dock_clients), dock_clients) {
+ if (dock->workspace != u_ws)
+ continue;
+
+ LOG("Re-assigning dock client to c_ws (%p)\n", c_ws);
+ dock->workspace = c_ws;
+ }
+ u_ws->screen = NULL;
+ }
+
+ /* Unmap the stack windows on the given 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);
+}
+
#include "layout.h"
#include "xcb.h"
#include "config.h"
+#include "workspace.h"
/* This TAILQ of i3Screens stores the virtual screens, used for handling overlapping screens
* (xrandr --same-as) */
static bool xinerama_enabled = true;
+/*
+ * Returns true if both screen objects describe the same screen (checks their
+ * size and position).
+ *
+ */
+bool screens_are_equal(i3Screen *screen1, i3Screen *screen2) {
+ /* If one of both objects (or both) are NULL, we cannot compare them */
+ if (screen1 == NULL || screen2 == NULL)
+ return false;
+
+ /* If the pointers are equal, take the short-circuit */
+ if (screen1 == screen2)
+ return true;
+
+ /* Compare their size - other properties are not relevant to determine
+ * if a screen is equal to another one */
+ return (memcmp(&(screen1->rect), &(screen2->rect), sizeof(Rect)) == 0);
+}
+
/*
* Looks in virtual_screens for the i3Screen whose start coordinates are x, y
*
SLIST_INIT(&(screen->dock_clients));
- /* Copy dimensions */
- memcpy(&(workspace->rect), &(screen->rect), sizeof(Rect));
LOG("that is virtual screen at %d x %d with %d x %d\n",
screen->rect.x, screen->rect.y, screen->rect.width, screen->rect.height);
}
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) {
+ if (s != NULL) {
/* 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);
FREE(reply);
- i3Screen *s;
+ i3Screen *screen;
num_screens = 0;
/* Just go through each workspace and associate as many screens as we can. */
- TAILQ_FOREACH(s, virtual_screens, screens) {
- s->num = num_screens;
- initialize_screen(conn, s, &(workspaces[num_screens]));
+ TAILQ_FOREACH(screen, virtual_screens, screens) {
+ screen->num = num_screens;
num_screens++;
+ Workspace *ws = get_first_workspace_for_screen(virtual_screens, screen);
+ initialize_screen(conn, screen, ws);
}
}
query_screens(conn, new_screens);
i3Screen *first = TAILQ_FIRST(new_screens),
- *screen;
+ *screen,
+ *old_screen;
int screen_count = 0;
+ /* Mark each workspace which currently is assigned to a screen, so we
+ * can garbage-collect afterwards */
+ for (int c = 0; c < 10; c++)
+ workspaces[c].reassigned = (workspaces[c].screen == NULL);
+
TAILQ_FOREACH(screen, new_screens, screens) {
screen->num = screen_count;
screen->current_workspace = -1;
- for (int c = 0; c < 10; c++)
- if ((workspaces[c].screen != NULL) &&
- (workspaces[c].screen->num == screen_count)) {
- LOG("Found a matching screen\n");
- /* Try to use the same workspace, if it’s available */
- if (workspaces[c].screen->current_workspace)
- screen->current_workspace = workspaces[c].screen->current_workspace;
-
- if (screen->current_workspace == -1)
- screen->current_workspace = c;
-
- /* Re-use the old bar window */
- screen->bar = workspaces[c].screen->bar;
- screen->bargc = workspaces[c].screen->bargc;
-
- Rect bar_rect = {screen->rect.x,
- screen->rect.height - (font->height + 6),
- screen->rect.x + screen->rect.width,
- font->height + 6};
-
- xcb_configure_window(conn, screen->bar, XCB_CONFIG_WINDOW_X |
- XCB_CONFIG_WINDOW_Y |
- XCB_CONFIG_WINDOW_WIDTH |
- XCB_CONFIG_WINDOW_HEIGHT, &(bar_rect.x));
-
- /* Copy the list head for the dock clients */
- screen->dock_clients = workspaces[c].screen->dock_clients;
-
- /* Update the dimensions */
- memcpy(&(workspaces[c].rect), &(screen->rect), sizeof(Rect));
- workspaces[c].screen = screen;
+
+ TAILQ_FOREACH(old_screen, virtual_screens, screens) {
+ if (old_screen->num != screen_count)
+ continue;
+
+ LOG("Found a matching screen\n");
+ /* Use the same workspace */
+ screen->current_workspace = old_screen->current_workspace;
+
+ /* Re-use the old bar window */
+ screen->bar = old_screen->bar;
+ screen->bargc = old_screen->bargc;
+ LOG("old_screen->bar = %p\n", old_screen->bar);
+
+ Rect bar_rect = {screen->rect.x,
+ screen->rect.height - (font->height + 6),
+ screen->rect.x + screen->rect.width,
+ font->height + 6};
+
+ LOG("configuring bar to be at %d x %d with %d x %d\n",
+ bar_rect.x, bar_rect.y, bar_rect.height, bar_rect.width);
+ xcb_configure_window(conn, screen->bar, XCB_CONFIG_WINDOW_X |
+ XCB_CONFIG_WINDOW_Y |
+ XCB_CONFIG_WINDOW_WIDTH |
+ XCB_CONFIG_WINDOW_HEIGHT, &(bar_rect.x));
+
+ /* Copy the list head for the dock clients */
+ screen->dock_clients = old_screen->dock_clients;
+
+ /* Update the dimensions */
+ for (int c = 0; c < 10; c++) {
+ Workspace *ws = &(workspaces[c]);
+ if (ws->screen != old_screen)
+ continue;
+
+ LOG("re-assigning ws %d\n", ws->num);
+ memcpy(&(ws->rect), &(screen->rect), sizeof(Rect));
+ ws->screen = screen;
+ ws->reassigned = true;
}
+
+ break;
+ }
if (screen->current_workspace == -1) {
- /* Create a new workspace for this screen, it’s new */
- for (int c = 0; c < 10; c++)
- if (workspaces[c].screen == NULL) {
- LOG("fix: initializing new workspace, setting num to %d\n", c);
- initialize_screen(conn, screen, &(workspaces[c]));
- break;
- }
+ /* Find the first unused workspace, preferring the ones
+ * which are assigned to this screen and initialize
+ * the screen with it. */
+ LOG("getting first ws for screen %p\n", screen);
+ Workspace *ws = get_first_workspace_for_screen(new_screens, screen);
+ initialize_screen(conn, screen, ws);
+
+ /* As this workspace just got visible (we got a new screen
+ * without workspace), we need to map its clients */
+ workspace_map_clients(conn, ws);
}
screen_count++;
}
/* 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))
+ if (workspaces[c].reassigned)
continue;
/* f_ws is a shortcut to the workspace to fix */
Workspace *f_ws = &(workspaces[c]);
Client *client;
- LOG("Closing bar window\n");
+ LOG("Closing bar window (%p)\n", f_ws->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);
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)
+ if (workspace_is_visible(f_ws))
continue;
- unmap_workspace(conn, f_ws);
+ workspace_unmap_clients(conn, f_ws);
+
+ if (c_ws == f_ws) {
+ LOG("Need to adjust c_ws...\n");
+ c_ws = &(workspaces[first->current_workspace]);
+ }
}
xcb_flush(conn);