Implement Xinerama (workspaces have a specific screen)
This commit is contained in:
parent
feaef42694
commit
09cd7bd2d0
|
@ -26,6 +26,8 @@ typedef struct Container Container;
|
||||||
typedef struct Client Client;
|
typedef struct Client Client;
|
||||||
typedef struct Binding Binding;
|
typedef struct Binding Binding;
|
||||||
typedef struct Workspace Workspace;
|
typedef struct Workspace Workspace;
|
||||||
|
typedef struct Rect Rect;
|
||||||
|
typedef struct Screen i3Screen;
|
||||||
|
|
||||||
/* Helper types */
|
/* Helper types */
|
||||||
typedef enum { D_LEFT, D_RIGHT, D_UP, D_DOWN } direction_t;
|
typedef enum { D_LEFT, D_RIGHT, D_UP, D_DOWN } direction_t;
|
||||||
|
@ -42,13 +44,14 @@ enum {
|
||||||
BIND_MODE_SWITCH = (1 << 8)
|
BIND_MODE_SWITCH = (1 << 8)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Rect {
|
||||||
|
uint32_t x, y;
|
||||||
|
uint32_t width, height;
|
||||||
|
};
|
||||||
|
|
||||||
struct Workspace {
|
struct Workspace {
|
||||||
int x;
|
/* x, y, width, height */
|
||||||
int y;
|
Rect rect;
|
||||||
int width;
|
|
||||||
int height;
|
|
||||||
int screen_num;
|
|
||||||
int num;
|
|
||||||
|
|
||||||
/* table dimensions */
|
/* table dimensions */
|
||||||
int cols;
|
int cols;
|
||||||
|
@ -60,6 +63,9 @@ struct Workspace {
|
||||||
|
|
||||||
Client *fullscreen_client;
|
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
|
/* This is a two-dimensional dynamic array of Container-pointers. I’ve always wanted
|
||||||
* to be a three-star programmer :) */
|
* to be a three-star programmer :) */
|
||||||
Container ***table;
|
Container ***table;
|
||||||
|
@ -114,8 +120,8 @@ struct Client {
|
||||||
/* Backpointer. A client is inside a container */
|
/* Backpointer. A client is inside a container */
|
||||||
Container *container;
|
Container *container;
|
||||||
|
|
||||||
uint32_t x, y;
|
/* x, y, width, height */
|
||||||
uint32_t width, height;
|
Rect rect;
|
||||||
|
|
||||||
/* Name */
|
/* Name */
|
||||||
char *name;
|
char *name;
|
||||||
|
@ -170,4 +176,17 @@ struct Container {
|
||||||
CIRCLEQ_HEAD(client_head, Client) clients;
|
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
|
#endif
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
#include "data.h"
|
#include "data.h"
|
||||||
|
|
||||||
#ifndef _TABLE_H
|
#ifndef _TABLE_H
|
||||||
|
|
|
@ -15,9 +15,12 @@
|
||||||
#ifndef _UTIL_H
|
#ifndef _UTIL_H
|
||||||
#define _UTIL_H
|
#define _UTIL_H
|
||||||
|
|
||||||
|
int min(int a, int b);
|
||||||
|
int max(int a, int b);
|
||||||
void start_application(const char *command);
|
void start_application(const char *command);
|
||||||
void check_error(xcb_connection_t *connection, xcb_void_cookie_t cookie, char *err_message);
|
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 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);
|
void toggle_fullscreen(xcb_connection_t *conn, Client *client);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -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
|
|
@ -29,20 +29,19 @@ static bool focus_window_in_container(xcb_connection_t *connection, Container *c
|
||||||
if (container->currently_focused == NULL)
|
if (container->currently_focused == NULL)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Client *candidad;
|
Client *candidad = NULL;
|
||||||
if (direction == D_UP)
|
if (direction == D_UP)
|
||||||
candidad = CIRCLEQ_PREV(container->currently_focused, clients);
|
candidad = CIRCLEQ_PREV(container->currently_focused, clients);
|
||||||
else if (direction == D_DOWN)
|
else if (direction == D_DOWN)
|
||||||
candidad = CIRCLEQ_NEXT(container->currently_focused, clients);
|
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 we don’t have anything to select, we’re done */
|
||||||
if (candidad == CIRCLEQ_END(&(container->clients)))
|
if (candidad == CIRCLEQ_END(&(container->clients)))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
/* Set focus if we could successfully move */
|
/* Set focus if we could successfully move */
|
||||||
container->currently_focused = candidad;
|
set_focus(connection, candidad);
|
||||||
xcb_set_input_focus(connection, XCB_INPUT_FOCUS_NONE, candidad->child, XCB_CURRENT_TIME);
|
|
||||||
render_layout(connection);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -69,7 +68,7 @@ static void focus_window(xcb_connection_t *connection, direction_t direction) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (CUR_CELL->currently_focused != NULL) {
|
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);
|
CUR_CELL->currently_focused->child, XCB_CURRENT_TIME);
|
||||||
render_layout(connection);
|
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);
|
printf("moving window to direction %d\n", direction);
|
||||||
/* Get current window */
|
/* Get current window */
|
||||||
Container *container = CUR_CELL,
|
Container *container = CUR_CELL,
|
||||||
*new;
|
*new = NULL;
|
||||||
|
|
||||||
/* There has to be a container, see focus_window() */
|
/* There has to be a container, see focus_window() */
|
||||||
assert(container != NULL);
|
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) {
|
static void show_workspace(xcb_connection_t *conn, int workspace) {
|
||||||
int cols, rows;
|
int cols, rows;
|
||||||
Client *client;
|
Client *client;
|
||||||
printf("show_workspace(%d)\n", workspace);
|
|
||||||
|
|
||||||
xcb_window_t root = xcb_setup_roots_iterator(xcb_get_setup(conn)).data->root;
|
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 */
|
/* Store current_row/current_col */
|
||||||
c_ws->current_row = current_row;
|
c_ws->current_row = current_row;
|
||||||
c_ws->current_col = current_col;
|
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? */
|
/* TODO: does grabbing the server actually bring us any (speed)advantages? */
|
||||||
//xcb_grab_server(conn);
|
//xcb_grab_server(conn);
|
||||||
|
|
||||||
/* Unmap all clients */
|
/* Unmap all clients of the current workspace */
|
||||||
for (cols = 0; cols < c_ws->cols; cols++)
|
for (cols = 0; cols < c_ws->cols; cols++)
|
||||||
for (rows = 0; rows < c_ws->rows; rows++) {
|
for (rows = 0; rows < c_ws->rows; rows++) {
|
||||||
CIRCLEQ_FOREACH(client, &(c_ws->table[cols][rows]->clients), clients)
|
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 */
|
/* Restore focus on the new workspace */
|
||||||
if (CUR_CELL->currently_focused != NULL)
|
if (CUR_CELL->currently_focused != NULL)
|
||||||
xcb_set_input_focus(conn, XCB_INPUT_FOCUS_NONE, CUR_CELL->currently_focused->child, 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_NONE, root, XCB_CURRENT_TIME);
|
else xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, root, XCB_CURRENT_TIME);
|
||||||
|
|
||||||
//xcb_ungrab_server(conn);
|
//xcb_ungrab_server(conn);
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
#include "font.h"
|
#include "font.h"
|
||||||
#include "xcb.h"
|
#include "xcb.h"
|
||||||
#include "util.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.
|
* 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)
|
if (client == NULL)
|
||||||
client = table_get(byChild, event->event);
|
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) {
|
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;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
77
src/layout.c
77
src/layout.c
|
@ -19,6 +19,7 @@
|
||||||
#include "xcb.h"
|
#include "xcb.h"
|
||||||
#include "table.h"
|
#include "table.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
#include "xinerama.h"
|
||||||
|
|
||||||
/* All functions handling layout/drawing of window decorations */
|
/* 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;
|
values[0] = background_color;
|
||||||
xcb_change_gc(conn, client->titlegc, mask, values);
|
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);
|
xcb_poly_fill_rectangle(conn, client->frame, client->titlegc, 1, &rect);
|
||||||
|
|
||||||
/* Draw the lines */
|
/* 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); \
|
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, 0, client->rect.width, 0);
|
||||||
DRAW_LINE(border_color, 2, font->height + 3, 2 + client->width, font->height + 3);
|
DRAW_LINE(border_color, 2, font->height + 3, 2 + client->rect.width, font->height + 3);
|
||||||
|
|
||||||
/* Draw the font */
|
/* Draw the font */
|
||||||
mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND | XCB_GC_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…
|
/* 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 */
|
* Note the bitwise OR instead of logical OR to force evaluation of both statements */
|
||||||
if (client->force_reconfigure |
|
if (client->force_reconfigure |
|
||||||
(client->x != (client->x = container->x + (container->col * container->width))) |
|
(client->rect.x != (client->rect.x = container->x + (container->col * container->width))) |
|
||||||
(client->y != (client->y = container->y + (container->row * container->height +
|
(client->rect.y != (client->rect.y = container->y + (container->row * container->height +
|
||||||
(container->height / num_clients) * current_client)))) {
|
(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
|
/* Note: We can use a pointer to client->x like an array of uint32_ts
|
||||||
because it is followed by client->y by definition */
|
because it is followed by client->y by definition */
|
||||||
xcb_configure_window(connection, client->frame,
|
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 */
|
/* TODO: vertical default layout */
|
||||||
if (client->force_reconfigure |
|
if (client->force_reconfigure |
|
||||||
(client->width != (client->width = container->width)) |
|
(client->rect.width != (client->rect.width = container->width)) |
|
||||||
(client->height != (client->height = container->height / num_clients))) {
|
(client->rect.height != (client->rect.height = container->height / num_clients))) {
|
||||||
xcb_configure_window(connection, client->frame,
|
xcb_configure_window(connection, client->frame,
|
||||||
XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT,
|
XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT,
|
||||||
&(client->width));
|
&(client->rect.width));
|
||||||
|
|
||||||
/* Adjust the position of the child inside its frame.
|
/* Adjust the position of the child inside its frame.
|
||||||
* The coordinates of the child are relative to its frame, we
|
* 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_Y |
|
||||||
XCB_CONFIG_WINDOW_WIDTH |
|
XCB_CONFIG_WINDOW_WIDTH |
|
||||||
XCB_CONFIG_WINDOW_HEIGHT;
|
XCB_CONFIG_WINDOW_HEIGHT;
|
||||||
uint32_t values[4] = {2, /* x */
|
uint32_t values[4] = {2, /* x */
|
||||||
font->height + 2 + 2, /* y */
|
font->height + 2 + 2, /* y */
|
||||||
client->width - (2 + 2), /* width */
|
client->rect.width - (2 + 2), /* width */
|
||||||
client->height - ((font->height + 2 + 2) + 2)}; /* height */
|
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]);
|
values[0], values[1], values[2], values[3]);
|
||||||
|
|
||||||
xcb_configure_window(connection, client->child, mask, values);
|
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 cols, rows;
|
||||||
int screen;
|
i3Screen *screen;
|
||||||
for (screen = 0; screen < num_screens; screen++) {
|
TAILQ_FOREACH(screen, &virtual_screens, screens) {
|
||||||
printf("Rendering screen %d\n", screen);
|
/* r_ws (rendering workspace) is just a shortcut to the Workspace being currently rendered */
|
||||||
if (workspaces[screen].fullscreen_client != NULL)
|
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 */
|
/* This is easy: A client has entered fullscreen mode, so we don’t render at all */
|
||||||
continue;
|
continue;
|
||||||
/* TODO: get the workspace which is active on the screen */
|
int width = r_ws->rect.width;
|
||||||
int width = workspaces[screen].width;
|
int height = r_ws->rect.height;
|
||||||
int height = workspaces[screen].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",
|
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 */
|
/* Go through the whole table and render what’s necessary */
|
||||||
for (cols = 0; cols < c_ws->cols; cols++)
|
for (cols = 0; cols < r_ws->cols; cols++)
|
||||||
for (rows = 0; rows < c_ws->rows; rows++) {
|
for (rows = 0; rows < r_ws->rows; rows++) {
|
||||||
Container *con = CUR_TABLE[cols][rows];
|
Container *container = r_ws->table[cols][rows];
|
||||||
printf("container has %d colspan, %d rowspan\n",
|
printf("container has %d colspan, %d rowspan\n",
|
||||||
con->colspan, con->rowspan);
|
container->colspan, container->rowspan);
|
||||||
/* Update position of the container */
|
/* Update position of the container */
|
||||||
con->row = rows;
|
container->row = rows;
|
||||||
con->col = cols;
|
container->col = cols;
|
||||||
con->x = workspaces[screen].x;
|
container->x = r_ws->rect.x;
|
||||||
con->y = workspaces[screen].y;
|
container->y = r_ws->rect.y;
|
||||||
con->width = (width / c_ws->cols) * con->colspan;
|
container->width = (width / r_ws->cols) * container->colspan;
|
||||||
con->height = (height / c_ws->rows) * con->rowspan;
|
container->height = (height / r_ws->rows) * container->rowspan;
|
||||||
|
|
||||||
/* Render it */
|
/* Render it */
|
||||||
render_container(conn, CUR_TABLE[cols][rows]);
|
render_container(connection, container);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
xcb_flush(conn);
|
xcb_flush(connection);
|
||||||
}
|
}
|
||||||
|
|
73
src/mainx.c
73
src/mainx.c
|
@ -41,6 +41,7 @@
|
||||||
#include "handlers.h"
|
#include "handlers.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "xcb.h"
|
#include "xcb.h"
|
||||||
|
#include "xinerama.h"
|
||||||
|
|
||||||
#define TERMINAL "/usr/pkg/bin/urxvt"
|
#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);
|
new = calloc(sizeof(Client), 1);
|
||||||
/* We initialize x and y with the invalid coordinates -1 so that they will
|
/* We initialize x and y with the invalid coordinates -1 so that they will
|
||||||
get updated at the next render_layout() at any case */
|
get updated at the next render_layout() at any case */
|
||||||
new->x = -1;
|
new->rect.x = -1;
|
||||||
new->y = -1;
|
new->rect.y = -1;
|
||||||
}
|
}
|
||||||
uint32_t mask = 0;
|
uint32_t mask = 0;
|
||||||
uint32_t values[3];
|
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->frame = xcb_generate_id(conn);
|
||||||
new->child = child;
|
new->child = child;
|
||||||
new->width = width;
|
new->rect.width = width;
|
||||||
new->height = height;
|
new->rect.height = height;
|
||||||
|
|
||||||
/* Don’t generate events for our new window, it should *not* be managed */
|
/* Don’t generate events for our new window, it should *not* be managed */
|
||||||
mask |= XCB_CW_OVERRIDE_REDIRECT;
|
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);
|
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… */
|
/* 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,
|
xcb_void_cookie_t cookie = xcb_create_window_checked(conn,
|
||||||
depth,
|
depth,
|
||||||
|
@ -176,6 +180,7 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child,
|
||||||
XCB_WINDOW_CLASS_COPY_FROM_PARENT,
|
XCB_WINDOW_CLASS_COPY_FROM_PARENT,
|
||||||
mask,
|
mask,
|
||||||
values);
|
values);
|
||||||
|
printf("x = %d, y = %d, width = %d, height = %d\n", x, y, width, height);
|
||||||
check_error(conn, cookie, "Could not create frame");
|
check_error(conn, cookie, "Could not create frame");
|
||||||
xcb_change_save_set(conn, XCB_SET_MODE_INSERT, child);
|
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 */);
|
XCB_BUTTON_MASK_ANY /* don’t filter for any modifiers */);
|
||||||
|
|
||||||
/* Focus the new window */
|
/* 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);
|
render_layout(conn);
|
||||||
}
|
}
|
||||||
|
@ -244,44 +249,6 @@ void manage_existing_windows(xcb_connection_t *c, xcb_property_handlers_t *proph
|
||||||
free(rep);
|
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 main(int argc, char *argv[], char *env[]) {
|
||||||
int i, screens;
|
int i, screens;
|
||||||
xcb_connection_t *c;
|
xcb_connection_t *c;
|
||||||
|
@ -355,7 +322,7 @@ int main(int argc, char *argv[], char *env[]) {
|
||||||
root_win = root;
|
root_win = root;
|
||||||
|
|
||||||
uint32_t mask = XCB_CW_EVENT_MASK;
|
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);
|
xcb_change_window_attributes(c, root, mask, values);
|
||||||
|
|
||||||
/* Setup NetWM atoms */
|
/* Setup NetWM atoms */
|
||||||
|
@ -442,6 +409,24 @@ int main(int argc, char *argv[], char *env[]) {
|
||||||
|
|
||||||
manage_existing_windows(c, &prophs, root);
|
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);
|
xcb_event_wait_for_event_loop(&evenths);
|
||||||
|
|
||||||
/* not reached */
|
/* not reached */
|
||||||
|
|
|
@ -40,6 +40,7 @@ void init_table() {
|
||||||
memset(workspaces, 0, sizeof(workspaces));
|
memset(workspaces, 0, sizeof(workspaces));
|
||||||
|
|
||||||
for (i = 0; i < 10; i++) {
|
for (i = 0; i < 10; i++) {
|
||||||
|
workspaces[i].screen = NULL;
|
||||||
expand_table_cols(&(workspaces[i]));
|
expand_table_cols(&(workspaces[i]));
|
||||||
expand_table_rows(&(workspaces[i]));
|
expand_table_rows(&(workspaces[i]));
|
||||||
}
|
}
|
||||||
|
|
36
src/util.c
36
src/util.c
|
@ -19,6 +19,14 @@
|
||||||
#include "table.h"
|
#include "table.h"
|
||||||
#include "layout.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
|
* 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),
|
* 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) {
|
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 */
|
/* Update container */
|
||||||
Client *old_client = client->container->currently_focused;
|
Client *old_client = client->container->currently_focused;
|
||||||
client->container->currently_focused = client;
|
client->container->currently_focused = client;
|
||||||
|
@ -78,7 +92,8 @@ void set_focus(xcb_connection_t *conn, Client *client) {
|
||||||
current_row = client->container->row;
|
current_row = client->container->row;
|
||||||
|
|
||||||
/* Set focus to the entered window, and flush xcb buffer immediately */
|
/* 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 */
|
/* Update last/current client’s titlebar */
|
||||||
if (old_client != NULL)
|
if (old_client != NULL)
|
||||||
decorate_window(conn, old_client);
|
decorate_window(conn, old_client);
|
||||||
|
@ -86,6 +101,17 @@ void set_focus(xcb_connection_t *conn, Client *client) {
|
||||||
xcb_flush(conn);
|
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
|
* 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
|
* 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_Y |
|
||||||
XCB_CONFIG_WINDOW_WIDTH |
|
XCB_CONFIG_WINDOW_WIDTH |
|
||||||
XCB_CONFIG_WINDOW_HEIGHT;
|
XCB_CONFIG_WINDOW_HEIGHT;
|
||||||
uint32_t values[4] = {workspace->x,
|
uint32_t values[4] = {workspace->rect.x,
|
||||||
workspace->y,
|
workspace->rect.y,
|
||||||
workspace->width,
|
workspace->rect.width,
|
||||||
workspace->height};
|
workspace->rect.height};
|
||||||
|
|
||||||
printf("child itself will be at %dx%d with size %dx%d\n",
|
printf("child itself will be at %dx%d with size %dx%d\n",
|
||||||
values[0], values[1], values[2], values[3]);
|
values[0], values[1], values[2], values[3]);
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
Loading…
Reference in New Issue