]> git.sur5r.net Git - i3/i3/commitdiff
Re-add old Xinerama code for the poor nvidia users
authorMichael Stapelberg <michael@stapelberg.de>
Tue, 9 Mar 2010 19:00:56 +0000 (20:00 +0100)
committerMichael Stapelberg <michael@stapelberg.de>
Tue, 9 Mar 2010 20:25:54 +0000 (21:25 +0100)
Add --force-xinerama when starting i3 to use Xinerama instead of RandR.
This should *ONLY* be done if you have no other choice (nvidia’s
binary driver uses twinview and does not expose the monitor information
through RandR).

common.mk
include/i3.h
include/randr.h
include/xinerama.h [new file with mode: 0644]
src/mainx.c
src/randr.c
src/xinerama.c [new file with mode: 0644]

index ab9c050a8ba88cc2b2b163d0a3de776360e16707..39aac5f6fbace7d34c46bc12419ae1bbfd4808d8 100644 (file)
--- a/common.mk
+++ b/common.mk
@@ -37,6 +37,7 @@ LDFLAGS += -lxcb-keysyms
 LDFLAGS += -lxcb-atom
 LDFLAGS += -lxcb-aux
 LDFLAGS += -lxcb-icccm
+LDFLAGS += -lxcb-xinerama
 LDFLAGS += -lxcb-randr
 LDFLAGS += -lxcb
 LDFLAGS += -lX11
index ce86e9244e862306016c38e11f8d4556f9b7bf69..77c792a7897cfffabb4eb0df317bf3ce2c03e3bb 100644 (file)
@@ -32,7 +32,6 @@ extern TAILQ_HEAD(autostarts_head, Autostart) autostarts;
 extern TAILQ_HEAD(assignments_head, Assignment) assignments;
 extern SLIST_HEAD(stack_wins_head, Stack_Window) stack_wins;
 extern xcb_event_handlers_t evenths;
-extern int num_screens;
 extern uint8_t root_depth;
 extern bool xkb_supported;
 extern xcb_atom_t atoms[NUM_ATOMS];
index 35e5f79bd49f1a930d9d12280316d76d49fc0055..4832efe50d9d1b06099b49dbba869ea18c259af9 100644 (file)
@@ -24,6 +24,19 @@ extern struct outputs_head outputs;
  */
 void initialize_randr(xcb_connection_t *conn, int *event_base);
 
+/**
+ * Disables RandR support by creating exactly one output with the size of the
+ * X11 screen.
+ *
+ */
+void disable_randr(xcb_connection_t *conn);
+
+/**
+ * Initializes the specified output, assigning the specified workspace to it.
+ *
+ */
+void initialize_output(xcb_connection_t *conn, Output *output, Workspace *workspace);
+
 /**
  * (Re-)queries the outputs via RandR and stores them in the list of outputs.
  *
diff --git a/include/xinerama.h b/include/xinerama.h
new file mode 100644 (file)
index 0000000..f118234
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * 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"
+
+#ifndef _XINERAMA_H
+#define _XINERAMA_H
+
+/**
+ * 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);
+
+#endif
index 23fd757fd9de63238a2858e1394909a627a11506..8542ab22b59ff928db5e0c38763b995076ae4940 100644 (file)
@@ -45,6 +45,7 @@
 #include "util.h"
 #include "xcb.h"
 #include "randr.h"
+#include "xinerama.h"
 #include "manage.h"
 #include "ipc.h"
 #include "log.h"
@@ -150,6 +151,7 @@ int main(int argc, char *argv[], char *env[]) {
         char *override_configpath = NULL;
         bool autostart = true;
         bool only_check_config = false;
+        bool force_xinerama = false;
         xcb_connection_t *conn;
         xcb_property_handlers_t prophs;
         xcb_intern_atom_cookie_t atom_cookies[NUM_ATOMS];
@@ -158,6 +160,7 @@ int main(int argc, char *argv[], char *env[]) {
                 {"config", required_argument, 0, 'c'},
                 {"version", no_argument, 0, 'v'},
                 {"help", no_argument, 0, 'h'},
+                {"force-xinerama", no_argument, 0, 0},
                 {0, 0, 0, 0}
         };
         int option_index = 0;
@@ -196,6 +199,17 @@ int main(int argc, char *argv[], char *env[]) {
                         case 'l':
                                 /* DEPRECATED, ignored for the next 3 versions (3.e, 3.f, 3.g) */
                                 break;
+                        case 0:
+                                if (strcmp(long_options[option_index].name, "force-xinerama") == 0) {
+                                        force_xinerama = true;
+                                        ELOG("Using Xinerama instead of RandR. This option should be "
+                                             "avoided at all cost because it does not refresh the list "
+                                             "of screens, so you cannot configure displays at runtime. "
+                                             "Please check if your driver really does not support RandR "
+                                             "and disable this option as soon as you can.\n");
+                                        break;
+                                }
+                                /* fall-through */
                         default:
                                 fprintf(stderr, "Usage: %s [-c configfile] [-d loglevel] [-a] [-v] [-V] [-C]\n", argv[0]);
                                 fprintf(stderr, "\n");
@@ -205,6 +219,9 @@ int main(int argc, char *argv[], char *env[]) {
                                 fprintf(stderr, "-d <loglevel>: enable debug loglevel <loglevel>\n");
                                 fprintf(stderr, "-c <configfile>: use the provided configfile instead\n");
                                 fprintf(stderr, "-C: check configuration file and exit\n");
+                                fprintf(stderr, "--force-xinerama: Use Xinerama instead of RandR. This "
+                                                "option should only be used if you are stuck with the "
+                                                "nvidia closed source driver which does not support RandR.\n");
                                 exit(EXIT_FAILURE);
                 }
         }
@@ -460,14 +477,18 @@ int main(int argc, char *argv[], char *env[]) {
 
         grab_all_keys(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);
+        if (force_xinerama) {
+                initialize_xinerama(conn);
+        } else {
+                DLOG("Checking for XRandR...\n");
+                initialize_randr(conn, &randr_base);
+
+                xcb_event_set_handler(&evenths,
+                                      randr_base + XCB_RANDR_SCREEN_CHANGE_NOTIFY,
+                                      handle_screen_change,
+                                      NULL);
+        }
 
         xcb_flush(conn);
 
index 1d440c2a9a5eb7ceac152b407eb6b28cb85b865d..7cf9d5147d67fbdb5aef4b7be3541c7a2ab01ab2 100644 (file)
@@ -157,8 +157,11 @@ Output *get_output_most(direction_t direction, Output *current) {
         return candidate;
 }
 
-static void initialize_output(xcb_connection_t *conn, Output *output,
-                              Workspace *workspace) {
+/*
+ * Initializes the specified output, assigning the specified workspace to it.
+ *
+ */
+void initialize_output(xcb_connection_t *conn, Output *output, Workspace *workspace) {
         i3Font *font = load_font(conn, config.font);
 
         workspace->output = output;
@@ -192,7 +195,7 @@ static void initialize_output(xcb_connection_t *conn, Output *output,
  * X11 screen.
  *
  */
-static void disable_randr(xcb_connection_t *conn) {
+void disable_randr(xcb_connection_t *conn) {
         xcb_screen_t *root_screen = xcb_setup_roots_iterator(xcb_get_setup(conn)).data;
 
         DLOG("RandR extension unusable, disabling.\n");
diff --git a/src/xinerama.c b/src/xinerama.c
new file mode 100644 (file)
index 0000000..d7efff0
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * vim:ts=8:expandtab
+ *
+ * i3 - an improved dynamic tiling window manager
+ *
+ * © 2009-2010 Michael Stapelberg and contributors
+ *
+ * See file LICENSE for license information.
+ *
+ * This is LEGACY code (we support RandR, which can do much more than
+ * Xinerama), but necessary for the poor users of the nVidia binary
+ * driver which does not support RandR in 2010 *sigh*.
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <xcb/xcb.h>
+#include <xcb/xinerama.h>
+
+#include "queue.h"
+#include "data.h"
+#include "util.h"
+#include "xinerama.h"
+#include "workspace.h"
+#include "log.h"
+#include "randr.h"
+
+static int num_screens;
+
+/*
+ * Looks in outputs for the Output whose start coordinates are x, y
+ *
+ */
+static Output *get_screen_at(int x, int y) {
+        Output *output;
+        TAILQ_FOREACH(output, &outputs, outputs)
+                if (output->rect.x == x && output->rect.y == y)
+                        return output;
+
+        return NULL;
+}
+
+/*
+ * Gets the Xinerama screens and converts them to virtual Outputs (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) {
+        xcb_xinerama_query_screens_reply_t *reply;
+        xcb_xinerama_screen_info_t *screen_info;
+
+        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);
+
+        for (int screen = 0; screen < screens; screen++) {
+                Output *s = get_screen_at(screen_info[screen].x_org, screen_info[screen].y_org);
+                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 = scalloc(sizeof(Output));
+                        asprintf(&(s->name), "xinerama-%d", num_screens);
+                        DLOG("Created new Xinerama screen %s (%p)\n", s->name, s);
+                        s->active = true;
+                        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(&outputs, s, outputs);
+                        else TAILQ_INSERT_TAIL(&outputs, s, outputs);
+                        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. 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) {
+        if (!xcb_get_extension_data(conn, &xcb_xinerama_id)->present) {
+                DLOG("Xinerama extension not found, disabling.\n");
+                disable_randr(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_randr(conn);
+                } else
+                        query_screens(conn);
+
+                FREE(reply);
+        }
+
+        Output *output;
+        Workspace *ws;
+        /* Just go through each active output and associate one workspace */
+        TAILQ_FOREACH(output, &outputs, outputs) {
+                ws = get_first_workspace_for_output(output);
+                initialize_output(conn, output, ws);
+        }
+}