]> git.sur5r.net Git - i3/i3/commitdiff
huge change: implement RandR instead of Xinerama
authorMichael Stapelberg <michael@stapelberg.de>
Tue, 2 Mar 2010 11:47:21 +0000 (12:47 +0100)
committerMichael Stapelberg <michael@stapelberg.de>
Tue, 2 Mar 2010 11:47:21 +0000 (12:47 +0100)
Thanks to Merovius for doing a proof of concept on this one and
being a driving force behind the idea.

Using RandR instead of Xinerama means that we are now able to use
the full potential of the modern way of configuring screens. That
means, i3 now has an idea of the outputs your graphic driver
provides, which allowed us to get rid of the ugly way of detecting
changes in the screen configuration which we used before. Now, your
workspaces should not be confused when changing output modes anymore.

Also, instead of having ugly heuristics to assign your workspaces
to (the screen at position X or the second screen in the list of
screens) you will be able to just specify an output name.

As this change basically touches everything, you should be prepared
for bugs. Please test and report them!

21 files changed:
common.mk
include/data.h
include/handlers.h
include/layout.h
include/randr.h [new file with mode: 0644]
include/workspace.h
include/xinerama.h [deleted file]
src/click.c
src/commands.c
src/debug.c
src/floating.c
src/handlers.c
src/layout.c
src/mainx.c
src/manage.c
src/randr.c [new file with mode: 0644]
src/resize.c
src/sighandler.c
src/util.c
src/workspace.c
src/xinerama.c [deleted file]

index 775b592daedc988640a564bba53616172b4063ac..3b88b6fe4da29b30eb5b655dd90b2a8e1c0bcb9d 100644 (file)
--- 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
index 31cef493d78bed76cf50ff57105867d857293d0e..10242f2067e18b1127050413faea436cb328a1e4 100644 (file)
@@ -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 <xcb/xcb.h>
+#include <xcb/randr.h>
 #include <xcb/xcb_atom.h>
 #include <stdbool.h>
 
  *
  * 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
index 95194c145c294d4a2854977d6b852437ee8393cb..5f0586f8786ffbc8a879d2b00ae3b3d9967fe4fb 100644 (file)
@@ -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 <xcb/randr.h>
+
 /**
  * Due to bindings like Mode_switch + <a>, 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.
index a96aabc38432a8ff9f8d9daf46c84541d3ce7d99..1cbb783721f91f1bb26e97257c2595b770ec34ce 100644 (file)
@@ -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 (file)
index 0000000..50e1993
--- /dev/null
@@ -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 <xcb/randr.h>
+
+#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
index 01e1b6fae97975087682e943660aca7327d5bf16..f22ca8e4be61ce0a11795a258ffab08be0aed8dc 100644 (file)
@@ -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 <xcb/xcb.h>
 
 #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 (file)
index 135ab1a..0000000
+++ /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
index 2ec071c4f9eb52ede875033ef040f7f5011dec14..d792ce2336baedb2d6abf907e41751ca3518616f 100644 (file)
@@ -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);
index 2a2399d74f6e5cd4cf6bf6bdaba1cb0d2fc14194..cdd82513a6ddb17062b00c7d0d23054b007456d0 100644 (file)
@@ -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. */
index 1be47269d4fe25050ca695435e1d7a4c85e10037..cd89b2967cfd123562f37b1d4424cd33f80d5e86 100644 (file)
@@ -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,
index ca294cc6db1627a414adfdc5fef571fdd7056f1e..02397bfc40d4d1c926b29149998372dffc9875f7 100644 (file)
@@ -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:
index 627744f4cafe817be5af35d1ec2c758a88da44e7..bc484c92b890f81936841c832f071f76b92733db 100644 (file)
@@ -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 <xcb/xcb.h>
 #include <xcb/xcb_atom.h>
 #include <xcb/xcb_icccm.h>
+#include <xcb/randr.h>
 
 #include <X11/XKBlib.h>
 
@@ -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);
         }
 
index 19babe2fd5ca21f9a1d72fa2f0adab0904611113..c189a9503137c64fdaadefe3da37dfb33fa0252c 100644 (file)
@@ -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);
 }
index f79beae2913d48b0f4399e88504c4116376aa27e..8130c0744b2272cbbeaea0ae2eea9c51d0c46e82 100644 (file)
@@ -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 <xcb/xcb_property.h>
 #include <xcb/xcb_keysyms.h>
 #include <xcb/xcb_icccm.h>
-#include <xcb/xinerama.h>
 
 #include <ev.h>
 
@@ -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);
index b80c94e0ce930757b06904730138f300ddac3cb4..d685a8d24208be598e13e4cd4794a78ca181d01e 100644 (file)
@@ -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 (file)
index 0000000..f63b2f0
--- /dev/null
@@ -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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include <assert.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <xcb/xcb.h>
+#include <xcb/randr.h>
+
+#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);
+}
index db1ac073499afb57f183b82aa19ac27894da3ab3..7b1f7bd825d5da5c316a11e8587841fc9c2734b7 100644 (file)
@@ -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);
index b013c60d8c21f2be05b1f4caa78aebad00a542b7..8330232ab55f289da8c26afb5bb81f319c31ae39 100644 (file)
@@ -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 */
index f14052b020bd341292689e79de1cea079457790b..2f8225c0f493707625bfca9f85eef446206fe654 100644 (file)
@@ -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;
index 7c29e6f7f8d731fd828f6c03e409acecb7be39e9..5a4902ca2034cdb46df350126580ac03e35bd656 100644 (file)
@@ -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 (file)
index f6af933..0000000
+++ /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 <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdbool.h>
-#include <assert.h>
-#include <time.h>
-#include <unistd.h>
-
-#include <xcb/xcb.h>
-#include <xcb/xinerama.h>
-
-#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);
-}