Implement assignments of workspaces to screens, big cleanup of workspace code

Please test this! Plug in screens, unplug them, use your video projector,
change resolutions, etc.

To use the assignments, use the following syntax:
workspace <number> [screen <screen>] [name]

Where screen can be one of:
<number> (It is not provided that these numbers stay constant, so use with care)
<x>x<y> (Coordinates where the screen starts, so 1280 will be fine to match the
	 screen right of the main screen if your main screen is 1280 pixels
	 width. However, 1281 will not match)
<x>
x<y>

Some examples follow:

 workspace 1 screen 0
 workspace 1 screen 1
 workspace 1 screen 1280x0
 workspace 2 screen 1280
 workspace 3 screen x0
 workspace 3 screen 1 www
 workspace 4 screen 0 mail
This commit is contained in:
Michael Stapelberg
2009-08-08 19:51:51 +02:00
parent 6192975a04
commit 47a798ac4a
14 changed files with 469 additions and 257 deletions

View File

@ -27,6 +27,7 @@
#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) */
@ -34,6 +35,25 @@ struct screens_head *virtual_screens;
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
*
@ -120,8 +140,6 @@ static void initialize_screen(xcb_connection_t *conn, i3Screen *screen, Workspac
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);
}
@ -175,7 +193,7 @@ 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) {
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);
@ -235,13 +253,14 @@ void initialize_xinerama(xcb_connection_t *conn) {
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);
}
}
@ -268,65 +287,85 @@ void xinerama_requery_screens(xcb_connection_t *conn) {
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 its available */
if (workspaces[c].screen->current_workspace)
screen->current_workspace = workspaces[c].screen->current_workspace;
if (screen->current_workspace == -1)
screen->current_workspace = c;
TAILQ_FOREACH(old_screen, virtual_screens, screens) {
if (old_screen->num != screen_count)
continue;
/* Re-use the old bar window */
screen->bar = workspaces[c].screen->bar;
screen->bargc = workspaces[c].screen->bargc;
LOG("Found a matching screen\n");
/* Use the same workspace */
screen->current_workspace = old_screen->current_workspace;
Rect bar_rect = {screen->rect.x,
screen->rect.height - (font->height + 6),
screen->rect.x + screen->rect.width,
font->height + 6};
/* 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);
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));
Rect bar_rect = {screen->rect.x,
screen->rect.height - (font->height + 6),
screen->rect.x + screen->rect.width,
font->height + 6};
/* Copy the list head for the dock clients */
screen->dock_clients = workspaces[c].screen->dock_clients;
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));
/* Update the dimensions */
memcpy(&(workspaces[c].rect), &(screen->rect), sizeof(Rect));
workspaces[c].screen = screen;
/* 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, its 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);
@ -342,10 +381,15 @@ void xinerama_requery_screens(xcb_connection_t *conn) {
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);