diff --git a/common.mk b/common.mk index 775b592d..3b88b6fe 100644 --- a/common.mk +++ b/common.mk @@ -35,7 +35,7 @@ LDFLAGS += -lxcb-keysyms LDFLAGS += -lxcb-atom LDFLAGS += -lxcb-aux LDFLAGS += -lxcb-icccm -LDFLAGS += -lxcb-xinerama +LDFLAGS += -lxcb-randr LDFLAGS += -lxcb LDFLAGS += -lX11 LDFLAGS += -lev diff --git a/include/data.h b/include/data.h index 31cef493..10242f20 100644 --- a/include/data.h +++ b/include/data.h @@ -3,7 +3,7 @@ * * i3 - an improved dynamic tiling window manager * - * © 2009 Michael Stapelberg and contributors + * © 2009-2010 Michael Stapelberg and contributors * * See file LICENSE for license information. * @@ -11,6 +11,7 @@ * */ #include +#include #include #include @@ -25,11 +26,12 @@ * * Let’s start from the biggest to the smallest: * - * - An i3Screen is a virtual screen (Xinerama). This can be a single one, - * though two monitors might be connected, if you’re running clone - * mode. There can also be multiple of them. + * - An Output is a physical output on your graphics driver. Outputs which + * are currently in use have (output->active == true). Each output has a + * position and a mode. An output usually corresponds to one connected + * screen (except if you are running multiple screens in clone mode). * - * - Each i3Screen contains Workspaces. The concept is known from various + * - Each Output contains Workspaces. The concept is known from various * other window managers. Basically, a workspace is a specific set of * windows, usually grouped thematically (irc, www, work, …). You can switch * between these. @@ -54,7 +56,7 @@ typedef struct Client Client; typedef struct Binding Binding; typedef struct Workspace Workspace; typedef struct Rect Rect; -typedef struct Screen i3Screen; +typedef struct xoutput Output; /****************************************************************************** * Helper types @@ -228,8 +230,8 @@ struct Workspace { * appended) */ TAILQ_HEAD(floating_clients_head, Client) floating_clients; - /** Backpointer to the screen this workspace is on */ - i3Screen *screen; + /** Backpointer to the output this workspace is on */ + Output *output; /** This is a two-dimensional dynamic array of * Container-pointers. I’ve always wanted to be a three-star @@ -496,14 +498,21 @@ struct Container { }; /** - * This is a virtual screen (Xinerama). This can be a single one, though two - * monitors might be connected, if you’re running clone mode. There can also - * be multiple of them. + * An Output is a physical output on your graphics driver. Outputs which + * are currently in use have (output->active == true). Each output has a + * position and a mode. An output usually corresponds to one connected + * screen (except if you are running multiple screens in clone mode). * */ -struct Screen { - /** Virtual screen number */ - int num; +struct xoutput { + /** Output id, so that we can requery the output directly later */ + xcb_randr_output_t id; + /** Name of the output */ + char *name; + + /** Whether the output is currently (has a CRTC attached with a valid + * mode) */ + bool active; /** Current workspace selected on this virtual screen */ Workspace *current_workspace; @@ -519,7 +528,7 @@ struct Screen { * _NET_WM_WINDOW_TYPE_DOCK */ SLIST_HEAD(dock_clients_head, Client) dock_clients; - TAILQ_ENTRY(Screen) screens; + TAILQ_ENTRY(xoutput) outputs; }; #endif diff --git a/include/handlers.h b/include/handlers.h index 95194c14..5f0586f8 100644 --- a/include/handlers.h +++ b/include/handlers.h @@ -3,7 +3,7 @@ * * i3 - an improved dynamic tiling window manager * - * (c) 2009 Michael Stapelberg and contributors + * © 2009-2010 Michael Stapelberg and contributors * * See file LICENSE for license information. * @@ -11,6 +11,8 @@ #ifndef _HANDLERS_H #define _HANDLERS_H +#include + /** * Due to bindings like Mode_switch + , we need to bind some keys in * XCB_GRAB_MODE_SYNC. Therefore, we just replay all key presses. @@ -74,6 +76,14 @@ int handle_map_request(void *prophs, xcb_connection_t *conn, */ int handle_configure_event(void *prophs, xcb_connection_t *conn, xcb_configure_notify_event_t *event); +/** + * Gets triggered upon a RandR screen change event, that is when the user + * changes the screen configuration in any way (mode, position, …) + * + */ +int handle_screen_change(void *prophs, xcb_connection_t *conn, + xcb_generic_event_t *e); + /** * Configure requests are received when the application wants to resize * windows on their own. diff --git a/include/layout.h b/include/layout.h index a96aabc3..1cbb7837 100644 --- a/include/layout.h +++ b/include/layout.h @@ -79,7 +79,7 @@ void ignore_enter_notify_forall(xcb_connection_t *conn, Workspace *workspace, * Renders the given workspace on the given screen * */ -void render_workspace(xcb_connection_t *conn, i3Screen *screen, Workspace *r_ws); +void render_workspace(xcb_connection_t *conn, Output *output, Workspace *r_ws); /** * Renders the whole layout, that is: Go through each screen, each workspace, diff --git a/include/randr.h b/include/randr.h new file mode 100644 index 00000000..50e19938 --- /dev/null +++ b/include/randr.h @@ -0,0 +1,62 @@ +/* + * vim:ts=8:expandtab + * + * i3 - an improved dynamic tiling window manager + * + * © 2009-2010 Michael Stapelberg and contributors + * + * See file LICENSE for license information. + * + */ +#include "data.h" +#include + +#ifndef _RANDR_H +#define _RANDR_H + +TAILQ_HEAD(outputs_head, xoutput); +extern struct outputs_head outputs; + +/** + * Returns true if both screen objects describe the same screen (checks their + * size and position). + * + */ +bool screens_are_equal(Output *screen1, Output *screen2); + +/** + * We have just established a connection to the X server and need the initial + * XRandR information to setup workspaces for each screen. + * + */ +void initialize_randr(xcb_connection_t *conn, int *event_base); + +/** + * (Re-)queries the outputs via RandR and stores them in the list of outputs. + * + */ +void randr_query_screens(xcb_connection_t *conn); + +/** + * Returns the first output which is active. + * + */ +Output *get_first_output(); + +/** + * Looks in virtual_screens for the i3Screen which contains coordinates x, y + * + */ +Output *get_screen_containing(int x, int y); + +/** + * Gets the screen which is the last one in the given direction, for example + * the screen on the most bottom when direction == D_DOWN, the screen most + * right when direction == D_RIGHT and so on. + * + * This function always returns a screen. + * + */ +Output *get_screen_most(direction_t direction, Output *current); + +#endif diff --git a/include/workspace.h b/include/workspace.h index 01e1b6fa..f22ca8e4 100644 --- a/include/workspace.h +++ b/include/workspace.h @@ -3,7 +3,7 @@ * * i3 - an improved dynamic tiling window manager * - * © 2009 Michael Stapelberg and contributors + * © 2009-2010 Michael Stapelberg and contributors * * See file LICENSE for license information. * @@ -11,7 +11,7 @@ #include #include "data.h" -#include "xinerama.h" +#include "randr.h" #ifndef _WORKSPACE_H #define _WORKSPACE_H @@ -53,7 +53,7 @@ void workspace_show(xcb_connection_t *conn, int workspace); * screen 1 and you just plugged in screen 1). * */ -void workspace_assign_to(Workspace *ws, i3Screen *screen); +void workspace_assign_to(Workspace *ws, Output *screen); /** * Initializes the given workspace if it is not already initialized. The given @@ -62,14 +62,14 @@ void workspace_assign_to(Workspace *ws, i3Screen *screen); * the screen is not attached at the moment. * */ -void workspace_initialize(Workspace *ws, i3Screen *screen, bool recheck); +void workspace_initialize(Workspace *ws, Output *screen, bool recheck); /** * 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 *get_first_workspace_for_screen(Output *screen); /** * Unmaps all clients (and stack windows) of the given workspace. diff --git a/include/xinerama.h b/include/xinerama.h deleted file mode 100644 index 135ab1ab..00000000 --- a/include/xinerama.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * vim:ts=8:expandtab - * - * i3 - an improved dynamic tiling window manager - * - * (c) 2009 Michael Stapelberg and contributors - * - * See file LICENSE for license information. - * - */ -#include "data.h" - -#ifndef _XINERAMA_H -#define _XINERAMA_H - -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. - * - */ -void initialize_xinerama(xcb_connection_t *conn); - -/** - * This is called when the rootwindow receives a configure_notify event and - * therefore the number/position of the Xinerama screens could have changed. - * - */ -void xinerama_requery_screens(xcb_connection_t *conn); - -/** - * Looks in virtual_screens for the i3Screen whose start coordinates are x, y - * - */ -i3Screen *get_screen_at(int x, int y, struct screens_head *screenlist); - -/** - * Looks in virtual_screens for the i3Screen which contains coordinates x, y - * - */ -i3Screen *get_screen_containing(int x, int y); - -/** - * Gets the screen which is the last one in the given direction, for example - * the screen on the most bottom when direction == D_DOWN, the screen most - * right when direction == D_RIGHT and so on. - * - * This function always returns a screen. - * - */ -i3Screen *get_screen_most(direction_t direction, i3Screen *current); - -#endif diff --git a/src/click.c b/src/click.c index 2ec071c4..d792ce23 100644 --- a/src/click.c +++ b/src/click.c @@ -3,7 +3,7 @@ * * i3 - an improved dynamic tiling window manager * - * © 2009 Michael Stapelberg and contributors + * © 2009-2010 Michael Stapelberg and contributors * * See file LICENSE for license information. * @@ -37,6 +37,7 @@ #include "floating.h" #include "resize.h" #include "log.h" +#include "randr.h" static struct Stack_Window *get_stack_window(xcb_window_t window_id) { struct Stack_Window *current; @@ -125,9 +126,9 @@ static bool button_press_stackwin(xcb_connection_t *conn, xcb_button_press_event * */ static bool button_press_bar(xcb_connection_t *conn, xcb_button_press_event_t *event) { - i3Screen *screen; - TAILQ_FOREACH(screen, virtual_screens, screens) { - if (screen->bar != event->event) + Output *output; + TAILQ_FOREACH(output, &outputs, outputs) { + if (output->bar != event->event) continue; DLOG("Click on a bar\n"); @@ -137,14 +138,14 @@ static bool button_press_bar(xcb_connection_t *conn, xcb_button_press_event_t *e Workspace *ws = c_ws; if (event->detail == XCB_BUTTON_INDEX_5) { while ((ws = TAILQ_NEXT(ws, workspaces)) != TAILQ_END(workspaces_head)) { - if (ws->screen == screen) { + if (ws->output == output) { workspace_show(conn, ws->num + 1); return true; } } } else { while ((ws = TAILQ_PREV(ws, workspaces_head, workspaces)) != TAILQ_END(workspaces)) { - if (ws->screen == screen) { + if (ws->output == output) { workspace_show(conn, ws->num + 1); return true; } @@ -153,11 +154,11 @@ static bool button_press_bar(xcb_connection_t *conn, xcb_button_press_event_t *e return true; } int drawn = 0; - /* Because workspaces can be on different screens, we need to loop - through all of them and decide to count it based on its ->screen */ + /* Because workspaces can be on different outputs, we need to loop + through all of them and decide to count it based on its ->output */ Workspace *ws; TAILQ_FOREACH(ws, workspaces, workspaces) { - if (ws->screen != screen) + if (ws->output != output) continue; DLOG("Checking if click was on workspace %d with drawn = %d, tw = %d\n", ws->num, drawn, ws->text_width); diff --git a/src/commands.c b/src/commands.c index 2a2399d7..cdd82513 100644 --- a/src/commands.c +++ b/src/commands.c @@ -3,7 +3,7 @@ * * i3 - an improved dynamic tiling window manager * - * © 2009 Michael Stapelberg and contributors + * © 2009-2010 Michael Stapelberg and contributors * * See file LICENSE for license information. * @@ -22,7 +22,7 @@ #include "table.h" #include "layout.h" #include "i3.h" -#include "xinerama.h" +#include "randr.h" #include "client.h" #include "floating.h" #include "xcb.h" @@ -108,7 +108,7 @@ static void focus_thing(xcb_connection_t *conn, direction_t direction, thing_t t * right/left/bottom/top and just switch to the workspace on * the target screen. */ if (thing == THING_SCREEN) { - i3Screen *cs = c_ws->screen; + Output *cs = c_ws->output; assert(cs != NULL); Rect bounds = cs->rect; @@ -120,9 +120,9 @@ static void focus_thing(xcb_connection_t *conn, direction_t direction, thing_t t bounds.y -= bounds.height; else bounds.y += bounds.height; - i3Screen *target = get_screen_containing(bounds.x, bounds.y); + Output *target = get_screen_containing(bounds.x, bounds.y); if (target == NULL) { - DLOG("Target screen NULL\n"); + DLOG("Target output NULL\n"); /* Wrap around if the target screen is out of bounds */ if (direction == D_RIGHT) target = get_screen_most(D_LEFT, cs); @@ -162,14 +162,14 @@ static void focus_thing(xcb_connection_t *conn, direction_t direction, thing_t t } else { /* Let’s see if there is a screen down/up there to which we can switch */ DLOG("container is at %d with height %d\n", container->y, container->height); - i3Screen *screen; + Output *output; int destination_y = (direction == D_UP ? (container->y - 1) : (container->y + container->height + 1)); - if ((screen = get_screen_containing(container->x, destination_y)) == NULL) { + if ((output = get_screen_containing(container->x, destination_y)) == NULL) { DLOG("Wrapping screen around vertically\n"); /* No screen found? Then wrap */ - screen = get_screen_most((direction == D_UP ? D_DOWN : D_UP), container->workspace->screen); + output = get_screen_most((direction == D_UP ? D_DOWN : D_UP), container->workspace->output); } - t_ws = screen->current_workspace; + t_ws = output->current_workspace; new_row = (direction == D_UP ? (t_ws->rows - 1) : 0); } @@ -205,13 +205,13 @@ static void focus_thing(xcb_connection_t *conn, direction_t direction, thing_t t } else { /* Let’s see if there is a screen left/right here to which we can switch */ DLOG("container is at %d with width %d\n", container->x, container->width); - i3Screen *screen; + Output *output; int destination_x = (direction == D_LEFT ? (container->x - 1) : (container->x + container->width + 1)); - if ((screen = get_screen_containing(destination_x, container->y)) == NULL) { + if ((output = get_screen_containing(destination_x, container->y)) == NULL) { DLOG("Wrapping screen around horizontally\n"); - screen = get_screen_most((direction == D_LEFT ? D_RIGHT : D_LEFT), container->workspace->screen); + output = get_screen_most((direction == D_LEFT ? D_RIGHT : D_LEFT), container->workspace->output); } - t_ws = screen->current_workspace; + t_ws = output->current_workspace; new_col = (direction == D_LEFT ? (t_ws->cols - 1) : 0); } @@ -359,7 +359,7 @@ static void move_current_window(xcb_connection_t *conn, direction_t direction) { /* Fix colspan/rowspan if it’d overlap */ fix_colrowspan(conn, workspace); - render_workspace(conn, workspace->screen, workspace); + render_workspace(conn, workspace->output, workspace); xcb_flush(conn); set_focus(conn, current_client, true); @@ -532,7 +532,7 @@ static void move_floating_window_to_workspace(xcb_connection_t *conn, Client *cl LOG("moving floating\n"); - workspace_initialize(t_ws, c_ws->screen, false); + workspace_initialize(t_ws, c_ws->output, false); /* Check if there is already a fullscreen client on the destination workspace and * stop moving if so. */ @@ -593,7 +593,7 @@ static void move_current_window_to_workspace(xcb_connection_t *conn, int workspa if (to_focus == NULL) to_focus = CIRCLEQ_PREV_OR_NULL(&(container->clients), current_client, clients); - workspace_initialize(t_ws, container->workspace->screen, false); + workspace_initialize(t_ws, container->workspace->output, false); /* Check if there is already a fullscreen client on the destination workspace and * stop moving if so. */ if (current_client->fullscreen && (t_ws->fullscreen_client != NULL)) { @@ -779,7 +779,7 @@ static void next_previous_workspace(xcb_connection_t *conn, int direction) { if (ws == c_ws) return; - if (ws->screen == NULL) + if (ws->output == NULL) continue; workspace_show(conn, ws->num + 1); @@ -795,7 +795,7 @@ static void next_previous_workspace(xcb_connection_t *conn, int direction) { if (ws == c_ws) return; - if (ws->screen == NULL) + if (ws->output == NULL) continue; workspace_show(conn, ws->num + 1); @@ -1113,7 +1113,7 @@ void parse_command(xcb_connection_t *conn, const char *command) { /* Fix colspan/rowspan if it’d overlap */ fix_colrowspan(conn, ws); - render_workspace(conn, ws->screen, ws); + render_workspace(conn, ws->output, ws); /* Re-focus the client because cleanup_table sets the focus to the last * focused client inside a container only. */ diff --git a/src/debug.c b/src/debug.c index 1be47269..cd89b296 100644 --- a/src/debug.c +++ b/src/debug.c @@ -225,6 +225,8 @@ int format_event(xcb_generic_event_t *e) { labelRequest[*((uint8_t *) e + 10)]); break; default: + if (e->response_type > sizeof(labelEvent) / sizeof(char*)) + break; printf("Event %s following seqnum %d%s.\n", labelEvent[e->response_type], seqnum, diff --git a/src/floating.c b/src/floating.c index ca294cc6..02397bfc 100644 --- a/src/floating.c +++ b/src/floating.c @@ -412,7 +412,7 @@ void floating_move(xcb_connection_t *conn, Client *currently_focused, direction_ DLOG("floating move\n"); Rect destination = currently_focused->rect; - Rect *screen = &(currently_focused->workspace->screen->rect); + Rect *screen = &(currently_focused->workspace->output->rect); switch (direction) { case D_LEFT: diff --git a/src/handlers.c b/src/handlers.c index 627744f4..bc484c92 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -3,7 +3,7 @@ * * i3 - an improved dynamic tiling window manager * - * © 2009 Michael Stapelberg and contributors + * © 2009-2010 Michael Stapelberg and contributors * * See file LICENSE for license information. * @@ -17,6 +17,7 @@ #include #include #include +#include #include @@ -28,7 +29,7 @@ #include "data.h" #include "xcb.h" #include "util.h" -#include "xinerama.h" +#include "randr.h" #include "config.h" #include "queue.h" #include "resize.h" @@ -163,21 +164,21 @@ int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press_event_ * */ static void check_crossing_screen_boundary(uint32_t x, uint32_t y) { - i3Screen *screen; + Output *output; - if ((screen = get_screen_containing(x, y)) == NULL) { + if ((output = get_screen_containing(x, y)) == NULL) { ELOG("ERROR: No such screen\n"); return; } - if (screen == c_ws->screen) + if (output == c_ws->output) return; c_ws->current_row = current_row; c_ws->current_col = current_col; - c_ws = screen->current_workspace; + c_ws = output->current_workspace; current_row = c_ws->current_row; current_col = c_ws->current_col; - DLOG("We're now on virtual screen number %d\n", screen->num); + DLOG("We're now on output %p\n", output); } /* @@ -228,7 +229,7 @@ int handle_enter_notify(void *ignored, xcb_connection_t *conn, xcb_enter_notify_ return 1; } - if (client->workspace != c_ws && client->workspace->screen == c_ws->screen) { + if (client->workspace != c_ws && client->workspace->output == c_ws->output) { /* This can happen when a client gets assigned to a different workspace than * the current one (see src/mainx.c:reparent_window). Shortly after it was created, * an enter_notify will follow. */ @@ -395,7 +396,7 @@ int handle_configure_request(void *prophs, xcb_connection_t *conn, xcb_configure } client->desired_height = event->height; - render_workspace(conn, c_ws->screen, c_ws); + render_workspace(conn, c_ws->output, c_ws); xcb_flush(conn); return 1; @@ -417,26 +418,28 @@ int handle_configure_request(void *prophs, xcb_connection_t *conn, xcb_configure } /* - * Configuration notifies are only handled because we need to set up ignore for the following - * enter notify events + * Configuration notifies are only handled because we need to set up ignore for + * the following enter notify events. * */ int handle_configure_event(void *prophs, xcb_connection_t *conn, xcb_configure_notify_event_t *event) { - xcb_window_t root = xcb_setup_roots_iterator(xcb_get_setup(conn)).data->root; - /* We ignore this sequence twice because events for child and frame should be ignored */ add_ignore_event(event->sequence); add_ignore_event(event->sequence); - if (event->event == root) { - DLOG("event->x = %d, ->y = %d, ->width = %d, ->height = %d\n", event->x, event->y, event->width, event->height); - DLOG("reconfigure of the root window, need to xinerama\n"); - /* FIXME: Somehow, this is occuring too often. Therefore, we check for 0/0, - but is there a better way? */ - if (event->x == 0 && event->y == 0) - xinerama_requery_screens(conn); - return 1; - } + return 1; +} + +/* + * Gets triggered upon a RandR screen change event, that is when the user + * changes the screen configuration in any way (mode, position, …) + * + */ +int handle_screen_change(void *prophs, xcb_connection_t *conn, + xcb_generic_event_t *e) { + DLOG("RandR screen change\n"); + + randr_query_screens(conn); return 1; } @@ -500,7 +503,7 @@ int handle_unmap_notify_event(void *data, xcb_connection_t *conn, xcb_unmap_noti if (client->dock) { DLOG("Removing from dock clients\n"); - SLIST_REMOVE(&(client->workspace->screen->dock_clients), client, Client, dock_clients); + SLIST_REMOVE(&(client->workspace->output->dock_clients), client, Client, dock_clients); } DLOG("child of 0x%08x.\n", client->frame); @@ -524,8 +527,8 @@ int handle_unmap_notify_event(void *data, xcb_connection_t *conn, xcb_unmap_noti Client *to_focus = (!workspace_empty ? SLIST_FIRST(&(client->workspace->focus_stack)) : NULL); /* If this workspace is currently active, we don’t delete it */ - i3Screen *screen; - TAILQ_FOREACH(screen, virtual_screens, screens) + Output *screen; + TAILQ_FOREACH(screen, &outputs, outputs) if (screen->current_workspace == client->workspace) { workspace_active = true; workspace_empty = false; @@ -533,7 +536,7 @@ int handle_unmap_notify_event(void *data, xcb_connection_t *conn, xcb_unmap_noti } if (workspace_empty) - client->workspace->screen = NULL; + client->workspace->output = NULL; /* Remove the urgency flag if set */ client->urgent = false; @@ -739,9 +742,9 @@ int handle_expose_event(void *data, xcb_connection_t *conn, xcb_expose_event_t * } /* …or one of the bars? */ - i3Screen *screen; - TAILQ_FOREACH(screen, virtual_screens, screens) - if (screen->bar == event->window) + Output *output; + TAILQ_FOREACH(output, &outputs, outputs) + if (output->bar == event->window) render_layout(conn); return 1; } @@ -973,8 +976,8 @@ int handle_hints(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t /* If the workspace this client is on is not visible, we need to redraw * the workspace bar */ if (!workspace_is_visible(client->workspace)) { - i3Screen *screen = client->workspace->screen; - render_workspace(conn, screen, screen->current_workspace); + Output *output = client->workspace->output; + render_workspace(conn, output, output->current_workspace); xcb_flush(conn); } diff --git a/src/layout.c b/src/layout.c index 19babe2f..c189a950 100644 --- a/src/layout.c +++ b/src/layout.c @@ -22,7 +22,7 @@ #include "xcb.h" #include "table.h" #include "util.h" -#include "xinerama.h" +#include "randr.h" #include "layout.h" #include "client.h" #include "floating.h" @@ -206,7 +206,7 @@ void decorate_window(xcb_connection_t *conn, Client *client, xcb_drawable_t draw * */ void reposition_client(xcb_connection_t *conn, Client *client) { - i3Screen *screen; + Output *output; DLOG("frame 0x%08x needs to be pushed to %dx%d\n", client->frame, client->rect.x, client->rect.y); /* Note: We can use a pointer to client->x like an array of uint32_ts @@ -217,19 +217,19 @@ void reposition_client(xcb_connection_t *conn, Client *client) { return; /* If the client is floating, we need to check if we moved it to a different workspace */ - screen = get_screen_containing(client->rect.x + (client->rect.width / 2), + output = get_screen_containing(client->rect.x + (client->rect.width / 2), client->rect.y + (client->rect.height / 2)); - if (client->workspace->screen == screen) + if (client->workspace->output == output) return; - if (screen == NULL) { - DLOG("Boundary checking disabled, no screen found for (%d, %d)\n", client->rect.x, client->rect.y); + if (output == NULL) { + DLOG("Boundary checking disabled, no output found for (%d, %d)\n", client->rect.x, client->rect.y); return; } - DLOG("Client is on workspace %p with screen %p\n", client->workspace, client->workspace->screen); - DLOG("but screen at %d, %d is %p\n", client->rect.x, client->rect.y, screen); - floating_assign_to_workspace(client, screen->current_workspace); + DLOG("Client is on workspace %p with output %p\n", client->workspace, client->workspace->output); + DLOG("but output at %d, %d is %p\n", client->rect.x, client->rect.y, output); + floating_assign_to_workspace(client, output->current_workspace); set_focus(conn, client, true); } @@ -566,7 +566,7 @@ void render_container(xcb_connection_t *conn, Container *container) { static void render_bars(xcb_connection_t *conn, Workspace *r_ws, int width, int *height) { Client *client; - SLIST_FOREACH(client, &(r_ws->screen->dock_clients), dock_clients) { + SLIST_FOREACH(client, &(r_ws->output->dock_clients), dock_clients) { DLOG("client is at %d, should be at %d\n", client->rect.y, *height); if (client->force_reconfigure | update_if_necessary(&(client->rect.x), r_ws->rect.x) | @@ -586,48 +586,48 @@ static void render_bars(xcb_connection_t *conn, Workspace *r_ws, int width, int static void render_internal_bar(xcb_connection_t *conn, Workspace *r_ws, int width, int height) { i3Font *font = load_font(conn, config.font); - i3Screen *screen = r_ws->screen; + Output *output = r_ws->output; enum { SET_NORMAL = 0, SET_FOCUSED = 1 }; /* Fill the whole bar in black */ - xcb_change_gc_single(conn, screen->bargc, XCB_GC_FOREGROUND, get_colorpixel(conn, "#000000")); + xcb_change_gc_single(conn, output->bargc, XCB_GC_FOREGROUND, get_colorpixel(conn, "#000000")); xcb_rectangle_t rect = {0, 0, width, height}; - xcb_poly_fill_rectangle(conn, screen->bar, screen->bargc, 1, &rect); + xcb_poly_fill_rectangle(conn, output->bar, output->bargc, 1, &rect); /* Set font */ - xcb_change_gc_single(conn, screen->bargc, XCB_GC_FONT, font->id); + xcb_change_gc_single(conn, output->bargc, XCB_GC_FONT, font->id); int drawn = 0; Workspace *ws; TAILQ_FOREACH(ws, workspaces, workspaces) { - if (ws->screen != screen) + if (ws->output != output) continue; struct Colortriple *color; - if (screen->current_workspace == ws) + if (output->current_workspace == ws) color = &(config.bar.focused); else if (ws->urgent) color = &(config.bar.urgent); else color = &(config.bar.unfocused); /* Draw the outer rect */ - xcb_draw_rect(conn, screen->bar, screen->bargc, color->border, + xcb_draw_rect(conn, output->bar, output->bargc, color->border, drawn, /* x */ 1, /* y */ ws->text_width + 5 + 5, /* width = text width + 5 px left + 5px right */ height - 2 /* height = max. height - 1 px upper and 1 px bottom border */); /* Draw the background of this rect */ - xcb_draw_rect(conn, screen->bar, screen->bargc, color->background, + xcb_draw_rect(conn, output->bar, output->bargc, color->background, drawn + 1, 2, ws->text_width + 4 + 4, height - 4); - xcb_change_gc_single(conn, screen->bargc, XCB_GC_FOREGROUND, color->text); - xcb_change_gc_single(conn, screen->bargc, XCB_GC_BACKGROUND, color->background); - xcb_image_text_16(conn, ws->name_len, screen->bar, screen->bargc, drawn + 5 /* X */, + xcb_change_gc_single(conn, output->bargc, XCB_GC_FOREGROUND, color->text); + xcb_change_gc_single(conn, output->bargc, XCB_GC_BACKGROUND, color->background); + xcb_image_text_16(conn, ws->name_len, output->bar, output->bargc, drawn + 5 /* X */, font->height + 1 /* Y = baseline of font */, (xcb_char2b_t*)ws->name); drawn += ws->text_width + 12; @@ -668,14 +668,14 @@ 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) { +void render_workspace(xcb_connection_t *conn, Output *output, 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) + SLIST_FOREACH(client, &(output->dock_clients), dock_clients) height -= client->desired_height; /* Space for the internal bar */ @@ -748,14 +748,11 @@ void render_workspace(xcb_connection_t *conn, i3Screen *screen, Workspace *r_ws) * */ void render_layout(xcb_connection_t *conn) { - i3Screen *screen; + Output *output; - if (virtual_screens == NULL) - return; - - TAILQ_FOREACH(screen, virtual_screens, screens) - if (screen->current_workspace != NULL) - render_workspace(conn, screen, screen->current_workspace); + TAILQ_FOREACH(output, &outputs, outputs) + if (output->current_workspace != NULL) + render_workspace(conn, output, output->current_workspace); xcb_flush(conn); } diff --git a/src/mainx.c b/src/mainx.c index f79beae2..8130c074 100644 --- a/src/mainx.c +++ b/src/mainx.c @@ -3,7 +3,7 @@ * * i3 - an improved dynamic tiling window manager * - * © 2009 Michael Stapelberg and contributors + * © 2009-2010 Michael Stapelberg and contributors * * See file LICENSE for license information. * @@ -30,7 +30,6 @@ #include #include #include -#include #include @@ -45,7 +44,7 @@ #include "table.h" #include "util.h" #include "xcb.h" -#include "xinerama.h" +#include "randr.h" #include "manage.h" #include "ipc.h" #include "log.h" @@ -470,9 +469,14 @@ int main(int argc, char *argv[], char *env[]) { } } - /* check for Xinerama */ - DLOG("Checking for Xinerama...\n"); - initialize_xinerama(conn); + DLOG("Checking for XRandR...\n"); + int randr_base; + initialize_randr(conn, &randr_base); + + xcb_event_set_handler(&evenths, + randr_base + XCB_RANDR_SCREEN_CHANGE_NOTIFY, + handle_screen_change, + NULL); xcb_flush(conn); @@ -483,14 +487,14 @@ int main(int argc, char *argv[], char *env[]) { return 1; } - i3Screen *screen = get_screen_containing(reply->root_x, reply->root_y); + Output *screen = get_screen_containing(reply->root_x, reply->root_y); if (screen == NULL) { ELOG("ERROR: No screen at %d x %d, starting on the first screen\n", reply->root_x, reply->root_y); - screen = TAILQ_FIRST(virtual_screens); + screen = get_first_output(); } - DLOG("Starting on %d\n", screen->current_workspace); + DLOG("Starting on %p\n", screen->current_workspace); c_ws = screen->current_workspace; manage_existing_windows(conn, &prophs, root); diff --git a/src/manage.c b/src/manage.c index b80c94e0..d685a8d2 100644 --- a/src/manage.c +++ b/src/manage.c @@ -255,7 +255,7 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child, new->titlebar_position = TITLEBAR_OFF; new->force_reconfigure = true; new->container = NULL; - SLIST_INSERT_HEAD(&(c_ws->screen->dock_clients), new, dock_clients); + SLIST_INSERT_HEAD(&(c_ws->output->dock_clients), new, dock_clients); /* If it’s a dock we can’t make it float, so we break */ new->floating = FLOATING_AUTO_OFF; break; @@ -334,14 +334,14 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child, LOG("Assignment \"%s\" matches, so putting it on workspace %d\n", assign->windowclass_title, assign->workspace); - if (c_ws->screen->current_workspace->num == (assign->workspace-1)) { + if (c_ws->output->current_workspace->num == (assign->workspace-1)) { DLOG("We are already there, no need to do anything\n"); break; } DLOG("Changing container/workspace and unmapping the client\n"); Workspace *t_ws = workspace_get(assign->workspace-1); - workspace_initialize(t_ws, c_ws->screen, false); + workspace_initialize(t_ws, c_ws->output, false); new->container = t_ws->table[t_ws->current_col][t_ws->current_row]; new->workspace = t_ws; diff --git a/src/randr.c b/src/randr.c new file mode 100644 index 00000000..f63b2f0c --- /dev/null +++ b/src/randr.c @@ -0,0 +1,455 @@ +/* + * vim:ts=8:expandtab + * + * i3 - an improved dynamic tiling window manager + * + * © 2009-2010 Michael Stapelberg and contributors + * + * See file LICENSE for license information. + * + * For more information on RandR, please see the X.org RandR specification at + * http://cgit.freedesktop.org/xorg/proto/randrproto/tree/randrproto.txt + * (take your time to read it completely, it answers all questions). + * + */ +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "queue.h" +#include "i3.h" +#include "data.h" +#include "table.h" +#include "util.h" +#include "layout.h" +#include "xcb.h" +#include "config.h" +#include "workspace.h" +#include "log.h" +#include "ewmh.h" + +/* While a clean namespace is usually a pretty good thing, we really need + * to use shorter names than the whole xcb_randr_* default names. */ +typedef xcb_randr_get_crtc_info_reply_t crtc_info; +typedef xcb_randr_mode_info_t mode_info; +typedef xcb_randr_get_screen_resources_current_reply_t resources_reply; + +/* Stores all outputs available in your current session. */ +struct outputs_head outputs = TAILQ_HEAD_INITIALIZER(outputs); + +/* + * Returns true if both screen objects describe the same screen (checks their + * size and position). + * + */ +bool screens_are_equal(Output *screen1, Output *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 and position - other properties are not relevant + * to determine if a screen is equal to another one */ + return (memcmp(&(screen1->rect), &(screen2->rect), sizeof(Rect)) == 0); +} + +/* + * Get a specific output by its internal X11 id. Used by randr_query_screens + * to check if the output is new (only in the first scan) or if we are + * re-scanning. + * + */ +static Output *get_output_by_id(xcb_randr_output_t id) { + Output *screen; + TAILQ_FOREACH(screen, &outputs, outputs) + if (screen->id == id) + return screen; + + return NULL; +} + +/* + * Returns the first output which is active. + * + */ +Output *get_first_output() { + Output *screen; + + TAILQ_FOREACH(screen, &outputs, outputs) { + if (screen->active) + return screen; + } + + return NULL; +} + +/* + * Looks in virtual_screens for the Output which contains coordinates x, y + * + */ +Output *get_screen_containing(int x, int y) { + Output *screen; + TAILQ_FOREACH(screen, &outputs, outputs) { + if (!screen->active) + continue; + DLOG("comparing x=%d y=%d with x=%d and y=%d width %d height %d\n", + x, y, screen->rect.x, screen->rect.y, screen->rect.width, screen->rect.height); + if (x >= screen->rect.x && x < (screen->rect.x + screen->rect.width) && + y >= screen->rect.y && y < (screen->rect.y + screen->rect.height)) + return screen; + } + + return NULL; +} + +/* + * Gets the screen which is the last one in the given direction, for example the screen + * on the most bottom when direction == D_DOWN, the screen most right when direction == D_RIGHT + * and so on. + * + * This function always returns a screen. + * + */ +Output *get_screen_most(direction_t direction, Output *current) { + Output *screen, *candidate = NULL; + int position = 0; + TAILQ_FOREACH(screen, &outputs, outputs) { + /* Repeated calls of WIN determine the winner of the comparison */ + #define WIN(variable, condition) \ + if (variable condition) { \ + candidate = screen; \ + position = variable; \ + } \ + break; + + if (((direction == D_UP) || (direction == D_DOWN)) && + (current->rect.x != screen->rect.x)) + continue; + + if (((direction == D_LEFT) || (direction == D_RIGHT)) && + (current->rect.y != screen->rect.y)) + continue; + + switch (direction) { + case D_UP: + WIN(screen->rect.y, <= position); + case D_DOWN: + WIN(screen->rect.y, >= position); + case D_LEFT: + WIN(screen->rect.x, <= position); + case D_RIGHT: + WIN(screen->rect.x, >= position); + } + } + + assert(candidate != NULL); + + return candidate; +} + +static void initialize_output(xcb_connection_t *conn, Output *output, + Workspace *workspace) { + i3Font *font = load_font(conn, config.font); + + workspace->output = output; + output->current_workspace = workspace; + + /* Create a xoutput for each output */ + Rect bar_rect = {output->rect.x, + output->rect.y + output->rect.height - (font->height + 6), + output->rect.x + output->rect.width, + font->height + 6}; + uint32_t mask = XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK; + uint32_t values[] = {1, XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_BUTTON_PRESS}; + output->bar = create_window(conn, bar_rect, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_CURSOR_LEFT_PTR, true, mask, values); + output->bargc = xcb_generate_id(conn); + xcb_create_gc(conn, output->bargc, output->bar, 0, 0); + + SLIST_INIT(&(output->dock_clients)); + + DLOG("initialized output at (%d, %d) with %d x %d\n", + output->rect.x, output->rect.y, output->rect.width, output->rect.height); +} + +/* + * Fills virtual_screens with exactly one screen with width/height of the + * whole X screen. + * + */ +static void disable_randr(xcb_connection_t *conn) { + xcb_screen_t *root_screen = xcb_setup_roots_iterator(xcb_get_setup(conn)).data; + + DLOG("RandR extension not found, disabling.\n"); + + Output *s = scalloc(sizeof(Output)); + + s->active = true; + s->rect.x = 0; + s->rect.y = 0; + s->rect.width = root_screen->width_in_pixels; + s->rect.height = root_screen->height_in_pixels; + + TAILQ_INSERT_TAIL(&outputs, s, outputs); +} + +/* + * Searches for a mode in the current RandR configuration by the mode id. + * Returns NULL if no such mode could be found (should never happen). + * + */ +static mode_info *get_mode_by_id(resources_reply *reply, xcb_randr_mode_t mode) { + xcb_randr_mode_info_iterator_t it; + + for (it = xcb_randr_get_screen_resources_current_modes_iterator(reply); + it.rem > 0; + xcb_randr_mode_info_next(&it)) { + if (it.data->id == mode) + return it.data; + } + + return NULL; +} + +/* + * This function needs to be called when changing the mode of an output when + * it already has some workspaces (or a bar window) assigned. + * + * It reconfigures the bar window for the new mode, copies the new rect into + * each workspace on this output and forces all windows on the affected + * workspaces to be reconfigured. + * + * It is necessary to call render_layout() afterwards. + * + */ +static void output_change_mode(xcb_connection_t *conn, Output *output) { + i3Font *font = load_font(conn, config.font); + Workspace *ws; + Client *client; + + DLOG("Output mode changed, reconfiguring bar, updating workspaces\n"); + Rect bar_rect = {output->rect.x, + output->rect.y + output->rect.height - (font->height + 6), + output->rect.x + output->rect.width, + font->height + 6}; + + xcb_set_window_rect(conn, output->bar, bar_rect); + + /* go through all workspaces and set force_reconfigure */ + TAILQ_FOREACH(ws, workspaces, workspaces) { + if (ws->output != output) + continue; + + /* Update dimensions from output */ + memcpy(&(ws->rect), &(ws->output->rect), sizeof(Rect)); + + SLIST_FOREACH(client, &(ws->focus_stack), focus_clients) + client->force_reconfigure = true; + } +} + +/* + * Gets called by randr_query_screens() for each output. The function adds new + * outputs to the list of outputs, checks if the mode of existing outputs has + * been changed or if an existing output has been disabled. + * + */ +static void handle_output(xcb_connection_t *conn, xcb_randr_output_t id, + xcb_randr_get_output_info_reply_t *output, + xcb_timestamp_t cts, resources_reply *res) { + Workspace *ws; + + /* each CRT controller has a position in which we are interested in */ + crtc_info *crtc; + + /* the CRTC runs in a specific mode, while the position is stored in + * the output itself */ + mode_info *mode; + + Output *new = get_output_by_id(id); + bool existing = (new != NULL); + if (!existing) + new = scalloc(sizeof(Output)); + new->id = id; + asprintf(&new->name, "%.*s", + xcb_randr_get_output_info_name_length(output), + xcb_randr_get_output_info_name(output)); + + DLOG("found output with name %s\n", new->name); + + /* Even if no CRTC is used at the moment, we store the output so that + * we do not need to change the list ever again (we only update the + * position/size) */ + if (output->crtc == XCB_NONE) { + if (!existing) + TAILQ_INSERT_TAIL(&outputs, new, outputs); + else if (new->active) { + new->active = false; + new->current_workspace = NULL; + DLOG("Output %s disabled (no CRTC)\n", new->name); + TAILQ_FOREACH(ws, workspaces, workspaces) { + if (ws->output != new) + continue; + + workspace_assign_to(ws, get_first_output()); + } + + } + free(output); + return; + } + + xcb_randr_get_crtc_info_cookie_t icookie; + icookie = xcb_randr_get_crtc_info(conn, output->crtc, cts); + if ((crtc = xcb_randr_get_crtc_info_reply(conn, icookie, NULL)) == NULL || + (mode = get_mode_by_id(res, crtc->mode)) == NULL) { + DLOG("Skipping output %s: could not get CRTC/mode (%p/%p)\n", + new->name, crtc, mode); + free(new); + free(output); + return; + } + + new->active = true; + bool updated = update_if_necessary(&(new->rect.x), crtc->x) | + update_if_necessary(&(new->rect.y), crtc->y) | + update_if_necessary(&(new->rect.width), mode->width) | + update_if_necessary(&(new->rect.height), mode->height); + + DLOG("mode: %dx%d+%d+%d\n", new->rect.width, new->rect.height, + new->rect.x, new->rect.y); + + /* If we don’t need to change an existing output or if the output + * does not exist in the first place, the case is simple: we either + * need to insert the new output or we are done. */ + if (!updated || !existing) { + if (!existing) + TAILQ_INSERT_TAIL(&outputs, new, outputs); + free(output); + return; + } + + output_change_mode(conn, new); +} + +/* + * (Re-)queries the outputs via RandR and stores them in the list of outputs. + * + */ +void randr_query_screens(xcb_connection_t *conn) { + xcb_randr_get_screen_resources_current_cookie_t rcookie; + resources_reply *res; + /* timestamp of the configuration so that we get consistent replies to all + * requests (if the configuration changes between our different calls) */ + xcb_timestamp_t cts; + + /* an output is VGA-1, LVDS-1, etc. (usually physical video outputs) */ + xcb_randr_output_t *randr_outputs; + + /* Get screen resources (crtcs, outputs, modes) */ + rcookie = xcb_randr_get_screen_resources_current(conn, root); + if ((res = xcb_randr_get_screen_resources_current_reply(conn, rcookie, NULL)) == NULL) + die("Could not get RandR screen resources\n"); + cts = res->config_timestamp; + + int len = xcb_randr_get_screen_resources_current_outputs_length(res); + randr_outputs = xcb_randr_get_screen_resources_current_outputs(res); + + /* Request information for each output */ + xcb_randr_get_output_info_cookie_t ocookie[len]; + for (int i = 0; i < len; i++) + ocookie[i] = xcb_randr_get_output_info(conn, randr_outputs[i], cts); + + /* Loop through all outputs available for this X11 screen */ + xcb_randr_get_output_info_reply_t *output; + for (int i = 0; i < len; i++) { + if ((output = xcb_randr_get_output_info_reply(conn, ocookie[i], NULL)) == NULL) + continue; + + handle_output(conn, randr_outputs[i], output, cts, res); + } + + free(res); + Output *screen, *oscreen; + /* Check for clones and reduce the mode to the lowest common mode */ + TAILQ_FOREACH(screen, &outputs, outputs) { + if (!screen->active) + continue; + DLOG("screen %p, position (%d, %d), checking for clones\n", + screen, screen->rect.x, screen->rect.y); + + TAILQ_FOREACH(oscreen, &outputs, outputs) { + if (oscreen == screen || !oscreen->active) + continue; + + if (oscreen->rect.x != screen->rect.x || + oscreen->rect.y != screen->rect.y) + continue; + + DLOG("screen %p has the same position, his mode = %d x %d\n", + oscreen, oscreen->rect.width, oscreen->rect.height); + uint32_t width = min(oscreen->rect.width, screen->rect.width); + uint32_t height = min(oscreen->rect.height, screen->rect.height); + + if (update_if_necessary(&(screen->rect.width), width) | + update_if_necessary(&(screen->rect.height), height)) + output_change_mode(conn, screen); + + if (update_if_necessary(&(oscreen->rect.width), width) | + update_if_necessary(&(oscreen->rect.height), height)) + output_change_mode(conn, oscreen); + + + DLOG("new screen mode %d x %d, oscreen mode %d x %d\n", + screen->rect.width, screen->rect.height, + oscreen->rect.width, oscreen->rect.height); + } + } + + ewmh_update_workarea(); + + /* Just go through each workspace and associate as many screens as we can. */ + TAILQ_FOREACH(screen, &outputs, outputs) { + if (!screen->active || screen->current_workspace != NULL) + continue; + Workspace *ws = get_first_workspace_for_screen(screen); + initialize_output(conn, screen, ws); + } + + /* render_layout flushes */ + render_layout(conn); +} + +/* + * We have just established a connection to the X server and need the initial + * XRandR information to setup workspaces for each screen. + * + */ +void initialize_randr(xcb_connection_t *conn, int *event_base) { + const xcb_query_extension_reply_t *extreply; + + extreply = xcb_get_extension_data(conn, &xcb_randr_id); + if (!extreply->present) + disable_randr(conn); + else randr_query_screens(conn); + + if (event_base != NULL) + *event_base = extreply->first_event; + + xcb_randr_select_input(conn, root, + XCB_RANDR_NOTIFY_MASK_SCREEN_CHANGE | + XCB_RANDR_NOTIFY_MASK_OUTPUT_CHANGE | + XCB_RANDR_NOTIFY_MASK_CRTC_CHANGE | + XCB_RANDR_NOTIFY_MASK_OUTPUT_PROPERTY); + + xcb_flush(conn); +} diff --git a/src/resize.c b/src/resize.c index db1ac073..7b1f7bd8 100644 --- a/src/resize.c +++ b/src/resize.c @@ -24,7 +24,7 @@ #include "xcb.h" #include "debug.h" #include "layout.h" -#include "xinerama.h" +#include "randr.h" #include "config.h" #include "floating.h" #include "workspace.h" @@ -38,7 +38,7 @@ int resize_graphical_handler(xcb_connection_t *conn, Workspace *ws, int first, int second, resize_orientation_t orientation, xcb_button_press_event_t *event) { int new_position; - i3Screen *screen = get_screen_containing(event->root_x, event->root_y); + struct xoutput *screen = get_screen_containing(event->root_x, event->root_y); if (screen == NULL) { ELOG("BUG: No screen found at this position (%d, %d)\n", event->root_x, event->root_y); return 1; @@ -49,7 +49,7 @@ int resize_graphical_handler(xcb_connection_t *conn, Workspace *ws, int first, i * screens during runtime. Instead, we just use the most right and most * bottom Xinerama screen and use their position + width/height to get * the area of pixels currently in use */ - i3Screen *most_right = get_screen_most(D_RIGHT, screen), + struct xoutput *most_right = get_screen_most(D_RIGHT, screen), *most_bottom = get_screen_most(D_DOWN, screen); DLOG("event->event_x = %d, event->root_x = %d\n", event->event_x, event->root_x); diff --git a/src/sighandler.c b/src/sighandler.c index b013c60d..8330232a 100644 --- a/src/sighandler.c +++ b/src/sighandler.c @@ -31,7 +31,7 @@ #include "xcb.h" #include "log.h" #include "config.h" -#include "xinerama.h" +#include "randr.h" static xcb_gcontext_t pixmap_gc; static xcb_pixmap_t pixmap; @@ -170,9 +170,9 @@ void handle_signal(int sig, siginfo_t *info, void *data) { int width = font_width + 20; /* Open a popup window on each virtual screen */ - i3Screen *screen; + Output *screen; xcb_window_t win; - TAILQ_FOREACH(screen, virtual_screens, screens) { + TAILQ_FOREACH(screen, &outputs, outputs) { win = open_input_window(conn, screen->rect, width, height); /* Create pixmap */ diff --git a/src/util.c b/src/util.c index f14052b0..2f8225c0 100644 --- a/src/util.c +++ b/src/util.c @@ -454,7 +454,7 @@ Client *get_matching_client(xcb_connection_t *conn, const char *window_classtitl DLOG("Getting clients for class \"%s\" / title \"%s\"\n", to_class, to_title); Workspace *ws; TAILQ_FOREACH(ws, workspaces, workspaces) { - if (ws->screen == NULL) + if (ws->output == NULL) continue; Client *client; diff --git a/src/workspace.c b/src/workspace.c index 7c29e6f7..5a4902ca 100644 --- a/src/workspace.c +++ b/src/workspace.c @@ -3,7 +3,7 @@ * * i3 - an improved dynamic tiling window manager * - * © 2009 Michael Stapelberg and contributors + * © 2009-2010 Michael Stapelberg and contributors * * See file LICENSE for license information. * @@ -22,7 +22,7 @@ #include "config.h" #include "xcb.h" #include "table.h" -#include "xinerama.h" +#include "randr.h" #include "layout.h" #include "workspace.h" #include "client.h" @@ -100,7 +100,7 @@ void workspace_set_name(Workspace *ws, const char *name) { * */ bool workspace_is_visible(Workspace *ws) { - return (ws->screen->current_workspace == ws); + return (ws->output->current_workspace == ws); } /* @@ -120,22 +120,22 @@ void workspace_show(xcb_connection_t *conn, int workspace) { c_ws->current_col = current_col; /* Check if the workspace has not been used yet */ - workspace_initialize(t_ws, c_ws->screen, false); + workspace_initialize(t_ws, c_ws->output, false); - if (c_ws->screen != t_ws->screen) { - /* We need to switch to the other screen first */ - DLOG("moving over to other screen.\n"); + if (c_ws->output != t_ws->output) { + /* We need to switch to the other output first */ + DLOG("moving over to other output.\n"); /* Store the old client */ Client *old_client = CUR_CELL->currently_focused; - c_ws = t_ws->screen->current_workspace; + c_ws = t_ws->output->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); + Rect *dims = &(c_ws->output->rect); xcb_warp_pointer(conn, XCB_NONE, root, 0, 0, 0, 0, dims->x + (dims->width / 2), dims->y + (dims->height / 2)); } @@ -147,7 +147,7 @@ void workspace_show(xcb_connection_t *conn, int workspace) { } /* Check if we need to change something or if we’re already there */ - if (c_ws->screen->current_workspace->num == (workspace-1)) { + if (c_ws->output->current_workspace->num == (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); @@ -160,7 +160,7 @@ void workspace_show(xcb_connection_t *conn, int workspace) { } Workspace *old_workspace = c_ws; - c_ws = t_ws->screen->current_workspace = workspace_get(workspace-1); + c_ws = t_ws->output->current_workspace = workspace_get(workspace-1); /* Unmap all clients of the old workspace */ workspace_unmap_clients(conn, old_workspace); @@ -173,7 +173,7 @@ void workspace_show(xcb_connection_t *conn, int workspace) { /* POTENTIAL TO IMPROVE HERE: due to the call to _map_clients first and * render_layout afterwards, there is a short flickering on the source - * workspace (assign ws 3 to screen 0, ws 4 to screen 1, create single + * workspace (assign ws 3 to output 0, ws 4 to output 1, create single * client on ws 4, move it to ws 3, switch to ws 3, you’ll see the * flickering). */ @@ -188,7 +188,7 @@ void workspace_show(xcb_connection_t *conn, int workspace) { /* We can warp the pointer only after the window has been * reconfigured in render_layout, otherwise the pointer will * be warped to the old position, which will not work when we - * moved it to another screen. */ + * moved it to another output. */ if (last_focused != SLIST_END(&(c_ws->focus_stack)) && need_warp) { client_warp_pointer_into(conn, last_focused); xcb_flush(conn); @@ -205,8 +205,8 @@ void workspace_show(xcb_connection_t *conn, int workspace) { * ("1280x800"). * */ -static i3Screen *get_screen_from_preference(struct screens_head *slist, char *preference) { - i3Screen *screen; +static Output *get_screen_from_preference(char *preference) { + Output *screen; char *rest; int preferred_screen = strtol(preference, &rest, 10); @@ -228,7 +228,7 @@ static i3Screen *get_screen_from_preference(struct screens_head *slist, char *pr DLOG("Looking for screen at %d x %d\n", x, y); - TAILQ_FOREACH(screen, slist, screens) + TAILQ_FOREACH(screen, &outputs, outputs) if ((x == INT_MAX || screen->rect.x == x) && (y == INT_MAX || screen->rect.y == y)) { DLOG("found %p\n", screen); @@ -239,7 +239,7 @@ static i3Screen *get_screen_from_preference(struct screens_head *slist, char *pr return NULL; } else { int c = 0; - TAILQ_FOREACH(screen, slist, screens) + TAILQ_FOREACH(screen, &outputs, outputs) if (c++ == preferred_screen) return screen; } @@ -248,37 +248,36 @@ static i3Screen *get_screen_from_preference(struct screens_head *slist, char *pr } /* - * Assigns the given workspace to the given screen by correctly updating its + * Assigns the given workspace to the given output by correctly updating its * state and reconfiguring all the clients on this workspace. * - * This is called when initializing a screen and when re-assigning it to a - * different screen which just got available (if you configured it to be on - * screen 1 and you just plugged in screen 1). + * This is called when initializing a output and when re-assigning it to a + * different output which just got available (if you configured it to be on + * output 1 and you just plugged in output 1). * */ -void workspace_assign_to(Workspace *ws, i3Screen *screen) { +void workspace_assign_to(Workspace *ws, Output *output) { Client *client; bool empty = true; - ws->screen = screen; + ws->output = output; - /* Copy the dimensions from the virtual screen */ - memcpy(&(ws->rect), &(ws->screen->rect), sizeof(Rect)); + /* Copy the dimensions from the virtual output */ + memcpy(&(ws->rect), &(ws->output->rect), sizeof(Rect)); ewmh_update_workarea(); /* Force reconfiguration for each client on that workspace */ - FOR_TABLE(ws) - CIRCLEQ_FOREACH(client, &(ws->table[cols][rows]->clients), clients) { - client->force_reconfigure = true; - empty = false; - } + SLIST_FOREACH(client, &(ws->focus_stack), focus_clients) { + client->force_reconfigure = true; + empty = false; + } if (empty) return; /* Render the workspace to reconfigure the clients. However, they will be visible now, so… */ - render_workspace(global_conn, screen, ws); + render_workspace(global_conn, output, ws); /* …unless we want to see them at the moment, we should hide that workspace */ if (workspace_is_visible(ws)) @@ -288,7 +287,7 @@ void workspace_assign_to(Workspace *ws, i3Screen *screen) { if (c_ws == ws) { DLOG("Need to adjust c_ws...\n"); - c_ws = screen->current_workspace; + c_ws = output->current_workspace; } } @@ -299,29 +298,29 @@ void workspace_assign_to(Workspace *ws, i3Screen *screen) { * the screen is not attached at the moment. * */ -void workspace_initialize(Workspace *ws, i3Screen *screen, bool recheck) { - i3Screen *old_screen; +void workspace_initialize(Workspace *ws, Output *output, bool recheck) { + Output *old_output; - if (ws->screen != NULL && !recheck) { + if (ws->output != NULL && !recheck) { DLOG("Workspace already initialized\n"); return; } - old_screen = ws->screen; + old_output = ws->output; - /* If this workspace has no preferred screen or if the screen it wants + /* If this workspace has no preferred output or if the output it wants * to be on is not available at the moment, we initialize it with - * the screen which was given */ + * the output which was given */ if (ws->preferred_screen == NULL || - (ws->screen = get_screen_from_preference(virtual_screens, ws->preferred_screen)) == NULL) - ws->screen = screen; + (ws->output = get_screen_from_preference(ws->preferred_screen)) == NULL) + ws->output = output; - DLOG("old_screen = %p, ws->screen = %p\n", old_screen, ws->screen); + DLOG("old_output = %p, ws->output = %p\n", old_output, ws->output); /* If the assignment did not change, we do not need to update anything */ - if (old_screen != NULL && ws->screen == old_screen) + if (old_output != NULL && ws->output == old_output) return; - workspace_assign_to(ws, ws->screen); + workspace_assign_to(ws, ws->output); } /* @@ -329,13 +328,13 @@ void workspace_initialize(Workspace *ws, i3Screen *screen, bool recheck) { * the preferred_screen setting of every workspace (workspace assignments). * */ -Workspace *get_first_workspace_for_screen(struct screens_head *slist, i3Screen *screen) { +Workspace *get_first_workspace_for_screen(Output *output) { Workspace *result = NULL; Workspace *ws; TAILQ_FOREACH(ws, workspaces, workspaces) { if (ws->preferred_screen == NULL || - !screens_are_equal(get_screen_from_preference(slist, ws->preferred_screen), screen)) + !screens_are_equal(get_screen_from_preference(ws->preferred_screen), output)) continue; result = ws; @@ -346,7 +345,7 @@ Workspace *get_first_workspace_for_screen(struct screens_head *slist, i3Screen * /* No assignment found, returning first unused workspace */ Workspace *ws; TAILQ_FOREACH(ws, workspaces, workspaces) { - if (ws->screen != NULL) + if (ws->output != NULL) continue; result = ws; @@ -364,7 +363,7 @@ Workspace *get_first_workspace_for_screen(struct screens_head *slist, i3Screen * result = workspace_get(last_ws + 1); } - workspace_initialize(result, screen, false); + workspace_initialize(result, output, false); return result; } @@ -438,14 +437,14 @@ void workspace_unmap_clients(xcb_connection_t *conn, Workspace *u_ws) { /* Re-assign the workspace of all dock clients which use this workspace */ Client *dock; DLOG("workspace %p is empty\n", u_ws); - SLIST_FOREACH(dock, &(u_ws->screen->dock_clients), dock_clients) { + SLIST_FOREACH(dock, &(u_ws->output->dock_clients), dock_clients) { if (dock->workspace != u_ws) continue; DLOG("Re-assigning dock client to c_ws (%p)\n", c_ws); dock->workspace = c_ws; } - u_ws->screen = NULL; + u_ws->output = NULL; } /* Unmap the stack windows on the given workspace, if any */ @@ -494,7 +493,7 @@ int workspace_height(Workspace *ws) { /* Reserve space for dock clients */ Client *client; - SLIST_FOREACH(client, &(ws->screen->dock_clients), dock_clients) + SLIST_FOREACH(client, &(ws->output->dock_clients), dock_clients) height -= client->desired_height; /* Space for the internal bar */ diff --git a/src/xinerama.c b/src/xinerama.c deleted file mode 100644 index f6af933e..00000000 --- a/src/xinerama.c +++ /dev/null @@ -1,437 +0,0 @@ -/* - * vim:ts=8:expandtab - * - * i3 - an improved dynamic tiling window manager - * - * © 2009 Michael Stapelberg and contributors - * - * See file LICENSE for license information. - * - */ -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "queue.h" -#include "i3.h" -#include "data.h" -#include "table.h" -#include "util.h" -#include "xinerama.h" -#include "layout.h" -#include "xcb.h" -#include "config.h" -#include "workspace.h" -#include "log.h" - -/* This TAILQ of i3Screens stores the virtual screens, used for handling overlapping screens - * (xrandr --same-as) */ -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 and position - 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 - * - */ -i3Screen *get_screen_at(int x, int y, struct screens_head *screenlist) { - i3Screen *screen; - TAILQ_FOREACH(screen, screenlist, screens) - if (screen->rect.x == x && screen->rect.y == y) - return screen; - - return NULL; -} - -/* - * Looks in virtual_screens for the i3Screen which contains coordinates x, y - * - */ -i3Screen *get_screen_containing(int x, int y) { - i3Screen *screen; - TAILQ_FOREACH(screen, virtual_screens, screens) { - DLOG("comparing x=%d y=%d with x=%d and y=%d width %d height %d\n", - x, y, screen->rect.x, screen->rect.y, screen->rect.width, screen->rect.height); - if (x >= screen->rect.x && x < (screen->rect.x + screen->rect.width) && - y >= screen->rect.y && y < (screen->rect.y + screen->rect.height)) - return screen; - } - - return NULL; -} - -/* - * Gets the screen which is the last one in the given direction, for example the screen - * on the most bottom when direction == D_DOWN, the screen most right when direction == D_RIGHT - * and so on. - * - * This function always returns a screen. - * - */ -i3Screen *get_screen_most(direction_t direction, i3Screen *current) { - i3Screen *screen, *candidate = NULL; - int position = 0; - TAILQ_FOREACH(screen, virtual_screens, screens) { - /* Repeated calls of WIN determine the winner of the comparison */ - #define WIN(variable, condition) \ - if (variable condition) { \ - candidate = screen; \ - position = variable; \ - } \ - break; - - if (((direction == D_UP) || (direction == D_DOWN)) && - (current->rect.x != screen->rect.x)) - continue; - - if (((direction == D_LEFT) || (direction == D_RIGHT)) && - (current->rect.y != screen->rect.y)) - continue; - - switch (direction) { - case D_UP: - WIN(screen->rect.y, <= position); - case D_DOWN: - WIN(screen->rect.y, >= position); - case D_LEFT: - WIN(screen->rect.x, <= position); - case D_RIGHT: - WIN(screen->rect.x, >= position); - } - } - - assert(candidate != NULL); - - return candidate; -} - -static void initialize_screen(xcb_connection_t *conn, i3Screen *screen, Workspace *workspace) { - i3Font *font = load_font(conn, config.font); - - workspace->screen = screen; - screen->current_workspace = workspace; - - /* Create a bar for each screen */ - Rect bar_rect = {screen->rect.x, - screen->rect.y + screen->rect.height - (font->height + 6), - screen->rect.x + screen->rect.width, - font->height + 6}; - uint32_t mask = XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK; - uint32_t values[] = {1, XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_BUTTON_PRESS}; - screen->bar = create_window(conn, bar_rect, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_CURSOR_LEFT_PTR, true, mask, values); - screen->bargc = xcb_generate_id(conn); - xcb_create_gc(conn, screen->bargc, screen->bar, 0, 0); - - SLIST_INIT(&(screen->dock_clients)); - - DLOG("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); -} - -/* - * Fills virtual_screens with exactly one screen with width/height of the whole X server. - * - */ -static void disable_xinerama(xcb_connection_t *conn) { - xcb_screen_t *root_screen = xcb_setup_roots_iterator(xcb_get_setup(conn)).data; - - i3Screen *s = calloc(sizeof(i3Screen), 1); - - s->rect.x = 0; - s->rect.y = 0; - s->rect.width = root_screen->width_in_pixels; - s->rect.height = root_screen->height_in_pixels; - - num_screens = 1; - s->num = 0; - - TAILQ_INSERT_TAIL(virtual_screens, s, screens); - - xinerama_enabled = false; -} - -/* - * Gets the Xinerama screens and converts them to virtual i3Screens (only one screen for two - * Xinerama screen which are configured in clone mode) in the given screenlist - * - */ -static void query_screens(xcb_connection_t *conn, struct screens_head *screenlist) { - xcb_xinerama_query_screens_reply_t *reply; - xcb_xinerama_screen_info_t *screen_info; - time_t before_trying = time(NULL); - - /* Try repeatedly to find screens (there might be short timeframes in - * which the X server does not return any screens, such as when rotating - * screens), but not longer than 5 seconds (strictly speaking, only four - * seconds of trying are guaranteed due to the 1-second-resolution) */ - while ((time(NULL) - before_trying) < 10) { - reply = xcb_xinerama_query_screens_reply(conn, xcb_xinerama_query_screens_unchecked(conn), NULL); - if (!reply) { - ELOG("Couldn't get Xinerama screens\n"); - return; - } - screen_info = xcb_xinerama_query_screens_screen_info(reply); - int screens = xcb_xinerama_query_screens_screen_info_length(reply); - num_screens = 0; - - 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) { - DLOG("Re-used old Xinerama screen %p\n", s); - /* 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); - s->rect.height = min(s->rect.height, screen_info[screen].height); - } else { - s = calloc(sizeof(i3Screen), 1); - DLOG("Created new Xinerama screen %p\n", s); - s->rect.x = screen_info[screen].x_org; - s->rect.y = screen_info[screen].y_org; - s->rect.width = screen_info[screen].width; - s->rect.height = screen_info[screen].height; - /* We always treat the screen at 0x0 as the primary screen */ - if (s->rect.x == 0 && s->rect.y == 0) - TAILQ_INSERT_HEAD(screenlist, s, screens); - else TAILQ_INSERT_TAIL(screenlist, s, screens); - num_screens++; - } - - DLOG("found Xinerama screen: %d x %d at %d x %d\n", - screen_info[screen].width, screen_info[screen].height, - screen_info[screen].x_org, screen_info[screen].y_org); - } - - free(reply); - - if (num_screens == 0) { - ELOG("No screens found. This is weird. Trying again...\n"); - /* Give the scheduler a chance to do something else - * and don’t hog the CPU */ - usleep(250); - continue; - } - - break; - } - - if (num_screens == 0) { - ELOG("No screens found for 10 seconds. Please fix your setup. i3 will exit now.\n"); - exit(0); - } -} - -/* - * We have just established a connection to the X server and need the initial Xinerama - * information to setup workspaces for each screen. - * - */ -void initialize_xinerama(xcb_connection_t *conn) { - virtual_screens = scalloc(sizeof(struct screens_head)); - TAILQ_INIT(virtual_screens); - - if (!xcb_get_extension_data(conn, &xcb_xinerama_id)->present) { - DLOG("Xinerama extension not found, disabling.\n"); - disable_xinerama(conn); - } else { - xcb_xinerama_is_active_reply_t *reply; - reply = xcb_xinerama_is_active_reply(conn, xcb_xinerama_is_active(conn), NULL); - - if (reply == NULL || !reply->state) { - DLOG("Xinerama is not active (in your X-Server), disabling.\n"); - disable_xinerama(conn); - } else - query_screens(conn, virtual_screens); - - FREE(reply); - } - - i3Screen *screen; - num_screens = 0; - /* Just go through each workspace and associate as many screens as we can. */ - 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); - } -} - -/* - * This is called when the rootwindow receives a configure_notify event and therefore the - * number/position of the Xinerama screens could have changed. - * - */ -void xinerama_requery_screens(xcb_connection_t *conn) { - i3Font *font = load_font(conn, config.font); - - /* POSSIBLE PROBLEM: Is the order of the Xinerama screens always constant? That is, can - it change when I move the --right-of video projector to --left-of? */ - - if (!xinerama_enabled) { - DLOG("Xinerama is disabled\n"); - return; - } - - /* We use a separate copy to diff with the previous set of screens */ - struct screens_head *new_screens = scalloc(sizeof(struct screens_head)); - TAILQ_INIT(new_screens); - - query_screens(conn, new_screens); - - i3Screen *first = TAILQ_FIRST(new_screens), - *screen, - *old_screen; - int screen_count = 0; - /* Mark each workspace which currently is assigned to a screen, so we - * can garbage-collect afterwards */ - Workspace *ws; - TAILQ_FOREACH(ws, workspaces, workspaces) - ws->reassigned = (ws->screen == NULL); - - TAILQ_FOREACH(screen, new_screens, screens) { - screen->num = screen_count; - screen->current_workspace = NULL; - - TAILQ_FOREACH(old_screen, virtual_screens, screens) { - if (old_screen->num != screen_count) - continue; - - DLOG("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; - DLOG("old_screen->bar = %p\n", old_screen->bar); - - Rect bar_rect = {screen->rect.x, - screen->rect.y + screen->rect.height - (font->height + 6), - screen->rect.width, - font->height + 6}; - - DLOG("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; - SLIST_INIT(&(old_screen->dock_clients)); - - /* Update the dimensions */ - Workspace *ws; - TAILQ_FOREACH(ws, workspaces, workspaces) { - if (ws->screen != old_screen) - continue; - - DLOG("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 == NULL) { - /* Find the first unused workspace, preferring the ones - * which are assigned to this screen and initialize - * the screen with it. */ - DLOG("getting first ws for screen %p\n", screen); - Workspace *ws = get_first_workspace_for_screen(new_screens, screen); - initialize_screen(conn, screen, ws); - ws->reassigned = true; - - /* 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 dock_clients which are out of bounds */ - TAILQ_FOREACH(old_screen, virtual_screens, screens) { - if (SLIST_EMPTY(&(old_screen->dock_clients))) - continue; - - DLOG("dock_clients out of bounds at screen %p, reassigning\n", old_screen); - if (SLIST_EMPTY(&(first->dock_clients))) { - first->dock_clients = old_screen->dock_clients; - continue; - } - - /* We need to merge the lists */ - Client *dock_client; - - while (!SLIST_EMPTY(&(old_screen->dock_clients))) { - dock_client = SLIST_FIRST(&(old_screen->dock_clients)); - SLIST_INSERT_HEAD(&(first->dock_clients), dock_client, dock_clients); - SLIST_REMOVE_HEAD(&(old_screen->dock_clients), dock_clients); - } - } - - /* Check for workspaces which are out of bounds */ - TAILQ_FOREACH(ws, workspaces, workspaces) { - if (ws->reassigned) - continue; - - DLOG("Closing bar window (%p)\n", ws->screen->bar); - xcb_destroy_window(conn, ws->screen->bar); - - DLOG("Workspace %d's screen out of bounds, assigning to first screen\n", ws->num + 1); - workspace_assign_to(ws, first); - } - - xcb_flush(conn); - - /* Free the old list */ - while (!TAILQ_EMPTY(virtual_screens)) { - screen = TAILQ_FIRST(virtual_screens); - TAILQ_REMOVE(virtual_screens, screen, screens); - free(screen); - } - free(virtual_screens); - - virtual_screens = new_screens; - - /* Check for workspaces which need to be assigned to specific screens - * which may now be available */ - TAILQ_FOREACH(ws, workspaces, workspaces) { - if (ws->preferred_screen == NULL || ws->screen == NULL) - continue; - - workspace_initialize(ws, ws->screen, true); - } - - DLOG("Current workspace is now: %d\n", first->current_workspace); - - render_layout(conn); -}