From bd76e994b8b0c21defc0fecdff510277a970acd2 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Tue, 9 Mar 2010 20:00:56 +0100 Subject: [PATCH] Re-add old Xinerama code for the poor nvidia users MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 | 1 + include/i3.h | 1 - include/randr.h | 13 +++++ include/xinerama.h | 23 ++++++++ src/mainx.c | 33 +++++++++--- src/randr.c | 9 ++-- src/xinerama.c | 128 +++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 198 insertions(+), 10 deletions(-) create mode 100644 include/xinerama.h create mode 100644 src/xinerama.c diff --git a/common.mk b/common.mk index ab9c050a..39aac5f6 100644 --- 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 diff --git a/include/i3.h b/include/i3.h index ce86e924..77c792a7 100644 --- a/include/i3.h +++ b/include/i3.h @@ -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]; diff --git a/include/randr.h b/include/randr.h index 35e5f79b..4832efe5 100644 --- a/include/randr.h +++ b/include/randr.h @@ -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 index 00000000..f1182349 --- /dev/null +++ b/include/xinerama.h @@ -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 diff --git a/src/mainx.c b/src/mainx.c index 23fd757f..8542ab22 100644 --- a/src/mainx.c +++ b/src/mainx.c @@ -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 : enable debug loglevel \n"); fprintf(stderr, "-c : 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); + 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_event_set_handler(&evenths, + randr_base + XCB_RANDR_SCREEN_CHANGE_NOTIFY, + handle_screen_change, + NULL); + } xcb_flush(conn); diff --git a/src/randr.c b/src/randr.c index 1d440c2a..7cf9d514 100644 --- a/src/randr.c +++ b/src/randr.c @@ -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 index 00000000..d7efff0d --- /dev/null +++ b/src/xinerama.c @@ -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 +#include +#include + +#include +#include + +#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); + } +}