]> git.sur5r.net Git - i3/i3/commitdiff
Implement Xinerama (workspaces have a specific screen)
authorMichael Stapelberg <michael+git@stapelberg.de>
Sun, 15 Feb 2009 00:58:09 +0000 (01:58 +0100)
committerMichael Stapelberg <michael+git@stapelberg.de>
Sun, 15 Feb 2009 00:58:09 +0000 (01:58 +0100)
include/data.h
include/table.h
include/util.h
include/xinerama.h [new file with mode: 0644]
src/commands.c
src/handlers.c
src/layout.c
src/mainx.c
src/table.c
src/util.c
src/xinerama.c [new file with mode: 0644]

index 96a57b290102bb26f3ffa9832f03c9210eb13ec1..6171f8b20ce2449483388468bca689de621b5ec2 100644 (file)
@@ -26,6 +26,8 @@ typedef struct Container Container;
 typedef struct Client Client;
 typedef struct Binding Binding;
 typedef struct Workspace Workspace;
+typedef struct Rect Rect;
+typedef struct Screen i3Screen;
 
 /* Helper types */
 typedef enum { D_LEFT, D_RIGHT, D_UP, D_DOWN } direction_t;
@@ -42,13 +44,14 @@ enum {
         BIND_MODE_SWITCH = (1 << 8)
 };
 
+struct Rect {
+        uint32_t x, y;
+        uint32_t width, height;
+};
+
 struct Workspace {
-        int x;
-        int y;
-        int width;
-        int height;
-        int screen_num;
-        int num;
+        /* x, y, width, height */
+        Rect rect;
 
         /* table dimensions */
         int cols;
@@ -60,6 +63,9 @@ struct Workspace {
 
         Client *fullscreen_client;
 
+        /* Backpointer to the screen this workspace is on */
+        i3Screen *screen;
+
         /* This is a two-dimensional dynamic array of Container-pointers. I’ve always wanted
          * to be a three-star programmer :) */
         Container ***table;
@@ -114,8 +120,8 @@ struct Client {
         /* Backpointer. A client is inside a container */
         Container *container;
 
-        uint32_t x, y;
-        uint32_t width, height;
+        /* x, y, width, height */
+        Rect rect;
 
         /* Name */
         char *name;
@@ -170,4 +176,17 @@ struct Container {
         CIRCLEQ_HEAD(client_head, Client) clients;
 };
 
+struct Screen {
+        /* Virtual screen number */
+        int num;
+
+        /* Current workspace selected on this virtual screen */
+        int current_workspace;
+
+        /* x, y, width, height */
+        Rect rect;
+
+        TAILQ_ENTRY(Screen) screens;
+};
+
 #endif
index a0b79f6ad3f2b0881f404fb83ccf458293410de0..a57b392fe1d50ffceead702316fc42bd63a0f07a 100644 (file)
@@ -9,6 +9,7 @@
  *
  */
 #include <stdbool.h>
+
 #include "data.h"
 
 #ifndef _TABLE_H
index f194da8624783c9a214de559d731a8cdcbcd294b..c05b91cf531f1a4ded1b5bfd3763e50478ca2e2d 100644 (file)
 #ifndef _UTIL_H
 #define _UTIL_H
 
+int min(int a, int b);
+int max(int a, int b);
 void start_application(const char *command);
 void check_error(xcb_connection_t *connection, xcb_void_cookie_t cookie, char *err_message);
 void set_focus(xcb_connection_t *conn, Client *client);
+void warp_pointer_into(xcb_connection_t *connection, Client *client);
 void toggle_fullscreen(xcb_connection_t *conn, Client *client);
 
 #endif
diff --git a/include/xinerama.h b/include/xinerama.h
new file mode 100644 (file)
index 0000000..99ee016
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * 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;
+
+void initialize_xinerama(xcb_connection_t *conn);
+i3Screen *get_screen_at(int x, int y);
+i3Screen *get_screen_containing(int x, int y);
+
+#endif
index 7f63f9b130aba2bd32f4cff1d297582707b48192..deaa8868eae3044af845f3b26d14a03a42df8233 100644 (file)
@@ -29,20 +29,19 @@ static bool focus_window_in_container(xcb_connection_t *connection, Container *c
         if (container->currently_focused == NULL)
                 return false;
 
-        Client *candidad;
+        Client *candidad = NULL;
         if (direction == D_UP)
                 candidad = CIRCLEQ_PREV(container->currently_focused, clients);
         else if (direction == D_DOWN)
                 candidad = CIRCLEQ_NEXT(container->currently_focused, clients);
+        else printf("Direction not implemented!\n");
 
         /* If we don’t have anything to select, we’re done */
         if (candidad == CIRCLEQ_END(&(container->clients)))
                 return false;
 
         /* Set focus if we could successfully move */
-        container->currently_focused = candidad;
-        xcb_set_input_focus(connection, XCB_INPUT_FOCUS_NONE, candidad->child, XCB_CURRENT_TIME);
-        render_layout(connection);
+        set_focus(connection, candidad);
 
         return true;
 }
@@ -69,7 +68,7 @@ static void focus_window(xcb_connection_t *connection, direction_t direction) {
                         return;
                 }
                 if (CUR_CELL->currently_focused != NULL) {
-                        xcb_set_input_focus(connection, XCB_INPUT_FOCUS_NONE,
+                        xcb_set_input_focus(connection, XCB_INPUT_FOCUS_POINTER_ROOT,
                                         CUR_CELL->currently_focused->child, XCB_CURRENT_TIME);
                         render_layout(connection);
                 }
@@ -112,7 +111,7 @@ static void move_current_window(xcb_connection_t *connection, direction_t direct
         printf("moving window to direction %d\n", direction);
         /* Get current window */
         Container *container = CUR_CELL,
-                  *new;
+                  *new = NULL;
 
         /* There has to be a container, see focus_window() */
         assert(container != NULL);
@@ -244,18 +243,49 @@ static void snap_current_container(xcb_connection_t *connection, direction_t dir
 static void show_workspace(xcb_connection_t *conn, int workspace) {
         int cols, rows;
         Client *client;
-        printf("show_workspace(%d)\n", workspace);
-
         xcb_window_t root = xcb_setup_roots_iterator(xcb_get_setup(conn)).data->root;
+        /* t_ws (to workspace) is just a convenience pointer to the workspace we’re switching to */
+        Workspace *t_ws = &(workspaces[workspace-1]);
+
+        printf("show_workspace(%d)\n", workspace);
 
         /* Store current_row/current_col */
         c_ws->current_row = current_row;
         c_ws->current_col = current_col;
 
+        /* Check if the workspace has not been used yet */
+        if (t_ws->screen == NULL) {
+                printf("initializing new workspace, setting num to %d\n", workspace);
+                t_ws->screen = c_ws->screen;
+                /* Copy the dimensions from the virtual screen */
+               memcpy(&(t_ws->rect), &(t_ws->screen->rect), sizeof(Rect));
+        }
+
+        if (c_ws->screen != t_ws->screen) {
+                /* We need to switch to the other screen first */
+                printf("Just moving over to other screen.\n");
+                c_ws = &(workspaces[t_ws->screen->current_workspace]);
+                current_col = c_ws->current_col;
+                current_row = c_ws->current_row;
+                if (CUR_CELL->currently_focused != NULL)
+                        warp_pointer_into(conn, CUR_CELL->currently_focused);
+                else {
+                        Rect *dims = &(c_ws->screen->rect);
+                        xcb_warp_pointer(conn, XCB_NONE, root, 0, 0, 0, 0,
+                                         dims->x + (dims->width / 2), dims->y + (dims->height / 2));
+                }
+        }
+
+        /* Check if we need to change something or if we’re already there */
+        if (c_ws->screen->current_workspace == (workspace-1))
+                return;
+
+        t_ws->screen->current_workspace = workspace-1;
+
         /* TODO: does grabbing the server actually bring us any (speed)advantages? */
         //xcb_grab_server(conn);
 
-        /* Unmap all clients */
+        /* Unmap all clients of the current workspace */
         for (cols = 0; cols < c_ws->cols; cols++)
                 for (rows = 0; rows < c_ws->rows; rows++) {
                         CIRCLEQ_FOREACH(client, &(c_ws->table[cols][rows]->clients), clients)
@@ -276,8 +306,8 @@ static void show_workspace(xcb_connection_t *conn, int workspace) {
 
         /* Restore focus on the new workspace */
         if (CUR_CELL->currently_focused != NULL)
-                xcb_set_input_focus(conn, XCB_INPUT_FOCUS_NONE, CUR_CELL->currently_focused->child, XCB_CURRENT_TIME);
-        else xcb_set_input_focus(conn, XCB_INPUT_FOCUS_NONE, root, XCB_CURRENT_TIME);
+                xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, CUR_CELL->currently_focused->child, XCB_CURRENT_TIME);
+        else xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, root, XCB_CURRENT_TIME);
 
         //xcb_ungrab_server(conn);
 
index 6de25e73e21cc8e6f034d4a686a43289ebe02b3c..5aef05e975ab1f5e3d2ac326c10b7db1cfe075a9 100644 (file)
@@ -26,6 +26,7 @@
 #include "font.h"
 #include "xcb.h"
 #include "util.h"
+#include "xinerama.h"
 
 /*
  * Due to bindings like Mode_switch + <a>, we need to bind some keys in XCB_GRAB_MODE_SYNC.
@@ -105,9 +106,16 @@ int handle_enter_notify(void *ignored, xcb_connection_t *conn, xcb_enter_notify_
         if (client == NULL)
                 client = table_get(byChild, event->event);
 
-        /* If not, then this event is not interesting. This should not happen */
+        /* If not, then the user moved his cursor to the root window. In that case, we adjust c_ws */
         if (client == NULL) {
-                printf("DEBUG: Uninteresting enter_notify-event?\n");
+                printf("Getting screen at %d x %d\n", event->root_x, event->root_y);
+                i3Screen *screen = get_screen_containing(event->root_x, event->root_y);
+                if (screen == NULL) {
+                        printf("ERROR: No such screen\n");
+                        return 0;
+                }
+                c_ws = &workspaces[screen->current_workspace];
+                printf("We're now on virtual screen number %d\n", screen->num);
                 return 1;
         }
 
index 7efc579812048bfa35c81b6bc7b30b31a07db896..75b66c8080d85e45aaf4f9ea99dba5b9bc8385e8 100644 (file)
@@ -19,6 +19,7 @@
 #include "xcb.h"
 #include "table.h"
 #include "util.h"
+#include "xinerama.h"
 
 /* All functions handling layout/drawing of window decorations */
 
@@ -57,7 +58,7 @@ void decorate_window(xcb_connection_t *conn, Client *client) {
         values[0] = background_color;
         xcb_change_gc(conn, client->titlegc, mask, values);
 
-        xcb_rectangle_t rect = {0, 0, client->width, client->height};
+        xcb_rectangle_t rect = {0, 0, client->rect.width, client->rect.height};
         xcb_poly_fill_rectangle(conn, client->frame, client->titlegc, 1, &rect);
 
         /* Draw the lines */
@@ -70,8 +71,8 @@ void decorate_window(xcb_connection_t *conn, Client *client) {
                 xcb_poly_line(conn, XCB_COORD_MODE_ORIGIN, client->frame, client->titlegc, 2, points); \
         }
 
-        DRAW_LINE(border_color, 2, 0, client->width, 0);
-        DRAW_LINE(border_color, 2, font->height + 3, 2 + client->width, font->height + 3);
+        DRAW_LINE(border_color, 2, 0, client->rect.width, 0);
+        DRAW_LINE(border_color, 2, font->height + 3, 2 + client->rect.width, font->height + 3);
 
         /* Draw the font */
         mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND | XCB_GC_FONT;
@@ -111,23 +112,23 @@ static void render_container(xcb_connection_t *connection, Container *container)
                         /* Check if we changed client->x or client->y by updating it…
                          * Note the bitwise OR instead of logical OR to force evaluation of both statements */
                         if (client->force_reconfigure |
-                            (client->x != (client->x = container->x + (container->col * container->width))) |
-                            (client->y != (client->y = container->y + (container->row * container->height +
+                            (client->rect.x != (client->rect.x = container->x + (container->col * container->width))) |
+                            (client->rect.y != (client->rect.y = container->y + (container->row * container->height +
                                           (container->height / num_clients) * current_client)))) {
-                                printf("frame needs to be pushed to %dx%d\n", client->x, client->y);
+                                printf("frame needs to be pushed to %dx%d\n", client->rect.x, client->rect.y);
                                 /* Note: We can use a pointer to client->x like an array of uint32_ts
                                    because it is followed by client->y by definition */
                                 xcb_configure_window(connection, client->frame,
-                                                XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, &(client->x));
+                                                XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, &(client->rect.x));
                         }
 
                         /* TODO: vertical default layout */
                         if (client->force_reconfigure |
-                            (client->width != (client->width = container->width)) |
-                            (client->height != (client->height = container->height / num_clients))) {
+                            (client->rect.width != (client->rect.width = container->width)) |
+                            (client->rect.height != (client->rect.height = container->height / num_clients))) {
                                 xcb_configure_window(connection, client->frame,
                                                 XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT,
-                                                &(client->width));
+                                                &(client->rect.width));
 
                                 /* Adjust the position of the child inside its frame.
                                  * The coordinates of the child are relative to its frame, we
@@ -136,12 +137,12 @@ static void render_container(xcb_connection_t *connection, Container *container)
                                                 XCB_CONFIG_WINDOW_Y |
                                                 XCB_CONFIG_WINDOW_WIDTH |
                                                 XCB_CONFIG_WINDOW_HEIGHT;
-                                uint32_t values[4] = {2,                                              /* x */
-                                                      font->height + 2 + 2,                           /* y */
-                                                      client->width - (2 + 2),                        /* width */
-                                                      client->height - ((font->height + 2 + 2) + 2)}; /* height */
+                                uint32_t values[4] = {2,                                                   /* x */
+                                                      font->height + 2 + 2,                                /* y */
+                                                      client->rect.width - (2 + 2),                        /* width */
+                                                      client->rect.height - ((font->height + 2 + 2) + 2)}; /* height */
 
-                                printf("fullscreen frame/child will be at %dx%d with size %dx%d\n",
+                                printf("child will be at %dx%d with size %dx%d\n",
                                                 values[0], values[1], values[2], values[3]);
 
                                 xcb_configure_window(connection, client->child, mask, values);
@@ -157,40 +158,42 @@ static void render_container(xcb_connection_t *connection, Container *container)
         }
 }
 
-void render_layout(xcb_connection_t *conn) {
+void render_layout(xcb_connection_t *connection) {
         int cols, rows;
-        int screen;
-        for (screen = 0; screen < num_screens; screen++) {
-                printf("Rendering screen %d\n", screen);
-                if (workspaces[screen].fullscreen_client != NULL)
+        i3Screen *screen;
+        TAILQ_FOREACH(screen, &virtual_screens, screens) {
+                /* r_ws (rendering workspace) is just a shortcut to the Workspace being currently rendered */
+                Workspace *r_ws = &(workspaces[screen->current_workspace]);
+
+                printf("Rendering screen %d\n", screen->num);
+                if (r_ws->fullscreen_client != NULL)
                         /* This is easy: A client has entered fullscreen mode, so we don’t render at all */
                         continue;
-                /* TODO: get the workspace which is active on the screen */
-                int width = workspaces[screen].width;
-                int height = workspaces[screen].height;
+                int width = r_ws->rect.width;
+                int height = r_ws->rect.height;
 
-                printf("got %d rows and %d cols\n", c_ws->rows, c_ws->cols);
+                printf("got %d rows and %d cols\n", r_ws->rows, r_ws->cols);
                 printf("each of them therefore is %d px width and %d px height\n",
-                                width / c_ws->cols, height / c_ws->rows);
+                                width / r_ws->cols, height / r_ws->rows);
 
                 /* Go through the whole table and render what’s necessary */
-                for (cols = 0; cols < c_ws->cols; cols++)
-                        for (rows = 0; rows < c_ws->rows; rows++) {
-                                Container *con = CUR_TABLE[cols][rows];
+                for (cols = 0; cols < r_ws->cols; cols++)
+                        for (rows = 0; rows < r_ws->rows; rows++) {
+                                Container *container = r_ws->table[cols][rows];
                                 printf("container has %d colspan, %d rowspan\n",
-                                                con->colspan, con->rowspan);
+                                                container->colspan, container->rowspan);
                                 /* Update position of the container */
-                                con->row = rows;
-                                con->col = cols;
-                                con->x = workspaces[screen].x;
-                                con->y = workspaces[screen].y;
-                                con->width = (width / c_ws->cols) * con->colspan;
-                                con->height = (height / c_ws->rows) * con->rowspan;
+                                container->row = rows;
+                                container->col = cols;
+                                container->x = r_ws->rect.x;
+                                container->y = r_ws->rect.y;
+                                container->width = (width / r_ws->cols) * container->colspan;
+                                container->height = (height / r_ws->rows) * container->rowspan;
 
                                 /* Render it */
-                                render_container(conn, CUR_TABLE[cols][rows]);
+                                render_container(connection, container);
                         }
         }
 
-        xcb_flush(conn);
+        xcb_flush(connection);
 }
index ef371f559b4169d8619c5434d3942e88897c0015..a2fc3551346c751976f9b61d96b40bb56aca2598 100644 (file)
@@ -41,6 +41,7 @@
 #include "handlers.h"
 #include "util.h"
 #include "xcb.h"
+#include "xinerama.h"
 
 #define TERMINAL "/usr/pkg/bin/urxvt"
 
@@ -129,8 +130,8 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child,
                 new = calloc(sizeof(Client), 1);
                 /* We initialize x and y with the invalid coordinates -1 so that they will
                    get updated at the next render_layout() at any case */
-                new->x = -1;
-                new->y = -1;
+                new->rect.x = -1;
+                new->rect.y = -1;
         }
         uint32_t mask = 0;
         uint32_t values[3];
@@ -144,8 +145,8 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child,
 
         new->frame = xcb_generate_id(conn);
         new->child = child;
-        new->width = width;
-        new->height = height;
+        new->rect.width = width;
+        new->rect.height = height;
 
         /* Don’t generate events for our new window, it should *not* be managed */
         mask |= XCB_CW_OVERRIDE_REDIRECT;
@@ -162,6 +163,9 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child,
 
         i3Font *font = load_font(conn, pattern);
 
+        height = min(height, c_ws->rect.y + c_ws->rect.height);
+        width = min(width, c_ws->rect.x + c_ws->rect.width);
+
         /* Yo dawg, I heard you like windows, so I create a window around your window… */
         xcb_void_cookie_t cookie = xcb_create_window_checked(conn,
                         depth,
@@ -176,6 +180,7 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child,
                         XCB_WINDOW_CLASS_COPY_FROM_PARENT,
                         mask,
                         values);
+        printf("x = %d, y = %d, width = %d, height = %d\n", x, y, width, height);
         check_error(conn, cookie, "Could not create frame");
         xcb_change_save_set(conn, XCB_SET_MODE_INSERT, child);
 
@@ -210,7 +215,7 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child,
                         XCB_BUTTON_MASK_ANY /* don’t filter for any modifiers */);
 
         /* Focus the new window */
-        xcb_set_input_focus(conn, XCB_INPUT_FOCUS_NONE, new->child, XCB_CURRENT_TIME);
+        xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, new->child, XCB_CURRENT_TIME);
 
         render_layout(conn);
 }
@@ -244,44 +249,6 @@ void manage_existing_windows(xcb_connection_t *c, xcb_property_handlers_t *proph
         free(rep);
 }
 
-static void initialize_xinerama(xcb_connection_t *conn) {
-        xcb_xinerama_query_screens_reply_t *reply;
-        xcb_xinerama_screen_info_t *screen_info;
-        int screen;
-
-        if (!xcb_get_extension_data(conn, &xcb_xinerama_id)->present) {
-                printf("Xinerama extension not found, disabling.\n");
-                return;
-        }
-
-        if (!xcb_xinerama_is_active_reply(conn, xcb_xinerama_is_active(conn), NULL)->state) {
-                printf("Xinerama is not active (in your X-Server), disabling.\n");
-                return;
-        }
-
-
-        reply = xcb_xinerama_query_screens_reply(conn, xcb_xinerama_query_screens_unchecked(conn), NULL);
-        /* TODO: error check */
-        screen_info = xcb_xinerama_query_screens_screen_info(reply);
-
-        num_screens = xcb_xinerama_query_screens_screen_info_length(reply);
-
-        /* Just go through each workspace and associate as many screens as we can. */
-        for (screen = 0; screen < num_screens; screen++) {
-                workspaces[screen].x = screen_info[screen].x_org;
-                workspaces[screen].y = screen_info[screen].y_org;
-                workspaces[screen].width = screen_info[screen].width;
-                workspaces[screen].height = screen_info[screen].height;
-                workspaces[screen].screen_num = screen;
-
-                printf("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(screen_info);
-}
-
 int main(int argc, char *argv[], char *env[]) {
         int i, screens;
         xcb_connection_t *c;
@@ -355,7 +322,7 @@ int main(int argc, char *argv[], char *env[]) {
         root_win = root;
 
         uint32_t mask = XCB_CW_EVENT_MASK;
-        uint32_t values[] = { XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_PROPERTY_CHANGE };
+        uint32_t values[] = { XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_ENTER_WINDOW};
         xcb_change_window_attributes(c, root, mask, values);
 
         /* Setup NetWM atoms */
@@ -442,6 +409,24 @@ int main(int argc, char *argv[], char *env[]) {
 
         manage_existing_windows(c, &prophs, root);
 
+        /* Get pointer position to see on which screen we’re starting */
+        xcb_query_pointer_cookie_t pointer_cookie = xcb_query_pointer(c, root);
+        xcb_query_pointer_reply_t *reply = xcb_query_pointer_reply(c, pointer_cookie, NULL);
+        if (reply == NULL) {
+                printf("Could not get pointer position\n");
+                return 1;
+        }
+
+        i3Screen *screen = get_screen_containing(reply->root_x, reply->root_y);
+        if (screen == NULL) {
+                printf("ERROR: No such screen\n");
+                return 0;
+        }
+        if (screen->current_workspace != 0) {
+                printf("Ok, I need to go to the other workspace\n");
+                c_ws = &workspaces[screen->current_workspace];
+        }
+
         xcb_event_wait_for_event_loop(&evenths);
 
         /* not reached */
index 76928f9079bbf5b103beddde081dd4e60911d4ec..c3f8bea7dc15685182b7b84fe4cda83da3f2d733 100644 (file)
@@ -40,6 +40,7 @@ void init_table() {
         memset(workspaces, 0, sizeof(workspaces));
 
         for (i = 0; i < 10; i++) {
+                workspaces[i].screen = NULL;
                 expand_table_cols(&(workspaces[i]));
                 expand_table_rows(&(workspaces[i]));
         }
index 6a33ffbe230e658f2526cfd195773083b8e9fd60..7ce4d55f47fd088918a5e5bc5666e894d1f2d1ca 100644 (file)
 #include "table.h"
 #include "layout.h"
 
+int min(int a, int b) {
+        return (a < b ? a : b);
+}
+
+int max(int a, int b) {
+        return (a > b ? a : b);
+}
+
 /*
  * Starts the given application by passing it through a shell. We use double fork
  * to avoid zombie processes. As the started application’s parent exits (immediately),
@@ -70,6 +78,12 @@ void check_error(xcb_connection_t *connection, xcb_void_cookie_t cookie, char *e
  *
  */
 void set_focus(xcb_connection_t *conn, Client *client) {
+        /* TODO: check if the focus needs to be changed at all */
+        /* Store current_row/current_col */
+        c_ws->current_row = current_row;
+        c_ws->current_col = current_col;
+        c_ws = client->container->workspace;
+
         /* Update container */
         Client *old_client = client->container->currently_focused;
         client->container->currently_focused = client;
@@ -78,7 +92,8 @@ void set_focus(xcb_connection_t *conn, Client *client) {
         current_row = client->container->row;
 
         /* Set focus to the entered window, and flush xcb buffer immediately */
-        xcb_set_input_focus(conn, XCB_INPUT_FOCUS_NONE, client->child, XCB_CURRENT_TIME);
+        xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, client->child, XCB_CURRENT_TIME);
+        //xcb_warp_pointer(conn, XCB_NONE, client->child, 0, 0, 0, 0, 10, 10);
         /* Update last/current client’s titlebar */
         if (old_client != NULL)
                 decorate_window(conn, old_client);
@@ -86,6 +101,17 @@ void set_focus(xcb_connection_t *conn, Client *client) {
         xcb_flush(conn);
 }
 
+/*
+ * Warps the pointer into the given client (in the middle of it, to be specific), therefore
+ * selecting it
+ *
+ */
+void warp_pointer_into(xcb_connection_t *connection, Client *client) {
+        int mid_x = client->rect.width / 2,
+            mid_y = client->rect.height / 2;
+        xcb_warp_pointer(connection, XCB_NONE, client->child, 0, 0, 0, 0, mid_x, mid_y);
+}
+
 /*
  * Toggles fullscreen mode for the given client. It updates the data structures and
  * reconfigures (= resizes/moves) the client and its frame to the full size of the
@@ -106,10 +132,10 @@ void toggle_fullscreen(xcb_connection_t *conn, Client *client) {
                                  XCB_CONFIG_WINDOW_Y |
                                  XCB_CONFIG_WINDOW_WIDTH |
                                  XCB_CONFIG_WINDOW_HEIGHT;
-                uint32_t values[4] = {workspace->x,
-                                      workspace->y,
-                                      workspace->width,
-                                      workspace->height};
+                uint32_t values[4] = {workspace->rect.x,
+                                      workspace->rect.y,
+                                      workspace->rect.width,
+                                      workspace->rect.height};
 
                 printf("child itself will be at %dx%d with size %dx%d\n",
                                 values[0], values[1], values[2], values[3]);
diff --git a/src/xinerama.c b/src/xinerama.c
new file mode 100644 (file)
index 0000000..91258a2
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * vim:ts=8:expandtab
+ *
+ * i3 - an improved dynamic tiling window manager
+ *
+ * (c) 2009 Michael Stapelberg and contributors
+ *
+ * See file LICENSE for license information.
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.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"
+
+/* This TAILQ of Rects stores the virtual screens, used for handling overlapping screens
+ * (xrandr --same-as) */
+struct screens_head virtual_screens;
+
+i3Screen *get_screen_at(int x, int y) {
+        i3Screen *screen;
+        TAILQ_FOREACH(screen, &virtual_screens, screens)
+                if (screen->rect.x == x && screen->rect.y == y)
+                        return screen;
+
+        return NULL;
+}
+
+i3Screen *get_screen_containing(int x, int y) {
+        i3Screen *screen;
+        TAILQ_FOREACH(screen, &virtual_screens, screens)
+                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;
+}
+
+
+/*
+ * 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) {
+        xcb_xinerama_query_screens_reply_t *reply;
+        xcb_xinerama_screen_info_t *screen_info;
+        int screen;
+
+        if (!xcb_get_extension_data(conn, &xcb_xinerama_id)->present) {
+                printf("Xinerama extension not found, disabling.\n");
+                return;
+        }
+
+        if (!xcb_xinerama_is_active_reply(conn, xcb_xinerama_is_active(conn), NULL)->state) {
+                printf("Xinerama is not active (in your X-Server), disabling.\n");
+                return;
+        }
+
+        reply = xcb_xinerama_query_screens_reply(conn, xcb_xinerama_query_screens_unchecked(conn), NULL);
+        if (!reply) {
+                printf("Couldn’t get active Xinerama screens\n");
+                return;
+        }
+        screen_info = xcb_xinerama_query_screens_screen_info(reply);
+        num_screens = xcb_xinerama_query_screens_screen_info_length(reply);
+
+        TAILQ_INIT(&virtual_screens);
+
+        /* Just go through each workspace and associate as many screens as we can. */
+        for (screen = 0; screen < num_screens; screen++) {
+                i3Screen *s = get_screen_at(screen_info[screen].x_org, screen_info[screen].y_org);
+                if (s!= NULL) {
+                        /* This screen already exists. We use the littlest screen so that the user
+                           can always see the complete workspace */
+                        s->rect.width = min(s->rect.width, screen_info[screen].width);
+                        s->rect.height = min(s->rect.height, screen_info[screen].height);
+                } else {
+                        s = calloc(sizeof(Screen), 1);
+                        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;
+                        TAILQ_INSERT_TAIL(&virtual_screens, s, screens);
+                }
+
+                printf("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);
+        }
+
+        i3Screen *s;
+        num_screens = 0;
+        TAILQ_FOREACH(s, &virtual_screens, screens) {
+                s->num = num_screens;
+                s->current_workspace = num_screens;
+                workspaces[num_screens].screen = s;
+                memcpy(&(workspaces[num_screens++].rect), &(s->rect), sizeof(Rect));
+                printf("that is virtual screen at %d x %d with %d x %d\n",
+                                s->rect.x, s->rect.y, s->rect.width, s->rect.height);
+        }
+
+        free(screen_info);
+}