From 09cd7bd2d0093c7312fafc0956640fb89338fd38 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 15 Feb 2009 01:58:09 +0100 Subject: [PATCH] Implement Xinerama (workspaces have a specific screen) --- include/data.h | 35 ++++++++++---- include/table.h | 1 + include/util.h | 3 ++ include/xinerama.h | 23 +++++++++ src/commands.c | 52 ++++++++++++++++----- src/handlers.c | 12 ++++- src/layout.c | 77 +++++++++++++++--------------- src/mainx.c | 73 ++++++++++++----------------- src/table.c | 1 + src/util.c | 36 +++++++++++++-- src/xinerama.c | 113 +++++++++++++++++++++++++++++++++++++++++++++ 11 files changed, 319 insertions(+), 107 deletions(-) create mode 100644 include/xinerama.h create mode 100644 src/xinerama.c diff --git a/include/data.h b/include/data.h index 96a57b29..6171f8b2 100644 --- a/include/data.h +++ b/include/data.h @@ -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 diff --git a/include/table.h b/include/table.h index a0b79f6a..a57b392f 100644 --- a/include/table.h +++ b/include/table.h @@ -9,6 +9,7 @@ * */ #include + #include "data.h" #ifndef _TABLE_H diff --git a/include/util.h b/include/util.h index f194da86..c05b91cf 100644 --- a/include/util.h +++ b/include/util.h @@ -15,9 +15,12 @@ #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 index 00000000..99ee0161 --- /dev/null +++ b/include/xinerama.h @@ -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 diff --git a/src/commands.c b/src/commands.c index 7f63f9b1..deaa8868 100644 --- a/src/commands.c +++ b/src/commands.c @@ -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); diff --git a/src/handlers.c b/src/handlers.c index 6de25e73..5aef05e9 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -26,6 +26,7 @@ #include "font.h" #include "xcb.h" #include "util.h" +#include "xinerama.h" /* * Due to bindings like Mode_switch + , 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; } diff --git a/src/layout.c b/src/layout.c index 7efc5798..75b66c80 100644 --- a/src/layout.c +++ b/src/layout.c @@ -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); } diff --git a/src/mainx.c b/src/mainx.c index ef371f55..a2fc3551 100644 --- a/src/mainx.c +++ b/src/mainx.c @@ -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 */ diff --git a/src/table.c b/src/table.c index 76928f90..c3f8bea7 100644 --- a/src/table.c +++ b/src/table.c @@ -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])); } diff --git a/src/util.c b/src/util.c index 6a33ffbe..7ce4d55f 100644 --- a/src/util.c +++ b/src/util.c @@ -19,6 +19,14 @@ #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 index 00000000..91258a2f --- /dev/null +++ b/src/xinerama.c @@ -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 +#include +#include + +#include +#include + +#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); +} -- 2.39.5