huge change: implement RandR instead of Xinerama

Thanks to Merovius for doing a proof of concept on this one and
being a driving force behind the idea.

Using RandR instead of Xinerama means that we are now able to use
the full potential of the modern way of configuring screens. That
means, i3 now has an idea of the outputs your graphic driver
provides, which allowed us to get rid of the ugly way of detecting
changes in the screen configuration which we used before. Now, your
workspaces should not be confused when changing output modes anymore.

Also, instead of having ugly heuristics to assign your workspaces
to (the screen at position X or the second screen in the list of
screens) you will be able to just specify an output name.

As this change basically touches everything, you should be prepared
for bugs. Please test and report them!
This commit is contained in:
Michael Stapelberg 2010-03-02 12:47:21 +01:00
parent 6f72970ece
commit 818e02ef35
21 changed files with 724 additions and 681 deletions

View File

@ -35,7 +35,7 @@ LDFLAGS += -lxcb-keysyms
LDFLAGS += -lxcb-atom
LDFLAGS += -lxcb-aux
LDFLAGS += -lxcb-icccm
LDFLAGS += -lxcb-xinerama
LDFLAGS += -lxcb-randr
LDFLAGS += -lxcb
LDFLAGS += -lX11
LDFLAGS += -lev

View File

@ -3,7 +3,7 @@
*
* i3 - an improved dynamic tiling window manager
*
* © 2009 Michael Stapelberg and contributors
* © 2009-2010 Michael Stapelberg and contributors
*
* See file LICENSE for license information.
*
@ -11,6 +11,7 @@
*
*/
#include <xcb/xcb.h>
#include <xcb/randr.h>
#include <xcb/xcb_atom.h>
#include <stdbool.h>
@ -25,11 +26,12 @@
*
* Lets start from the biggest to the smallest:
*
* - An i3Screen is a virtual screen (Xinerama). This can be a single one,
* though two monitors might be connected, if youre running clone
* mode. There can also be multiple of them.
* - An Output is a physical output on your graphics driver. Outputs which
* are currently in use have (output->active == true). Each output has a
* position and a mode. An output usually corresponds to one connected
* screen (except if you are running multiple screens in clone mode).
*
* - Each i3Screen contains Workspaces. The concept is known from various
* - Each Output contains Workspaces. The concept is known from various
* other window managers. Basically, a workspace is a specific set of
* windows, usually grouped thematically (irc, www, work, ). You can switch
* between these.
@ -54,7 +56,7 @@ typedef struct Client Client;
typedef struct Binding Binding;
typedef struct Workspace Workspace;
typedef struct Rect Rect;
typedef struct Screen i3Screen;
typedef struct xoutput Output;
/******************************************************************************
* Helper types
@ -228,8 +230,8 @@ struct Workspace {
* appended) */
TAILQ_HEAD(floating_clients_head, Client) floating_clients;
/** Backpointer to the screen this workspace is on */
i3Screen *screen;
/** Backpointer to the output this workspace is on */
Output *output;
/** This is a two-dimensional dynamic array of
* Container-pointers. Ive always wanted to be a three-star
@ -496,14 +498,21 @@ struct Container {
};
/**
* This is a virtual screen (Xinerama). This can be a single one, though two
* monitors might be connected, if youre running clone mode. There can also
* be multiple of them.
* An Output is a physical output on your graphics driver. Outputs which
* are currently in use have (output->active == true). Each output has a
* position and a mode. An output usually corresponds to one connected
* screen (except if you are running multiple screens in clone mode).
*
*/
struct Screen {
/** Virtual screen number */
int num;
struct xoutput {
/** Output id, so that we can requery the output directly later */
xcb_randr_output_t id;
/** Name of the output */
char *name;
/** Whether the output is currently (has a CRTC attached with a valid
* mode) */
bool active;
/** Current workspace selected on this virtual screen */
Workspace *current_workspace;
@ -519,7 +528,7 @@ struct Screen {
* _NET_WM_WINDOW_TYPE_DOCK */
SLIST_HEAD(dock_clients_head, Client) dock_clients;
TAILQ_ENTRY(Screen) screens;
TAILQ_ENTRY(xoutput) outputs;
};
#endif

View File

@ -3,7 +3,7 @@
*
* i3 - an improved dynamic tiling window manager
*
* (c) 2009 Michael Stapelberg and contributors
* © 2009-2010 Michael Stapelberg and contributors
*
* See file LICENSE for license information.
*
@ -11,6 +11,8 @@
#ifndef _HANDLERS_H
#define _HANDLERS_H
#include <xcb/randr.h>
/**
* Due to bindings like Mode_switch + <a>, we need to bind some keys in
* XCB_GRAB_MODE_SYNC. Therefore, we just replay all key presses.
@ -74,6 +76,14 @@ int handle_map_request(void *prophs, xcb_connection_t *conn,
*/
int handle_configure_event(void *prophs, xcb_connection_t *conn, xcb_configure_notify_event_t *event);
/**
* Gets triggered upon a RandR screen change event, that is when the user
* changes the screen configuration in any way (mode, position, )
*
*/
int handle_screen_change(void *prophs, xcb_connection_t *conn,
xcb_generic_event_t *e);
/**
* Configure requests are received when the application wants to resize
* windows on their own.

View File

@ -79,7 +79,7 @@ void ignore_enter_notify_forall(xcb_connection_t *conn, Workspace *workspace,
* Renders the given workspace on the given screen
*
*/
void render_workspace(xcb_connection_t *conn, i3Screen *screen, Workspace *r_ws);
void render_workspace(xcb_connection_t *conn, Output *output, Workspace *r_ws);
/**
* Renders the whole layout, that is: Go through each screen, each workspace,

62
include/randr.h Normal file
View File

@ -0,0 +1,62 @@
/*
* vim:ts=8:expandtab
*
* i3 - an improved dynamic tiling window manager
*
* © 2009-2010 Michael Stapelberg and contributors
*
* See file LICENSE for license information.
*
*/
#include "data.h"
#include <xcb/randr.h>
#ifndef _RANDR_H
#define _RANDR_H
TAILQ_HEAD(outputs_head, xoutput);
extern struct outputs_head outputs;
/**
* Returns true if both screen objects describe the same screen (checks their
* size and position).
*
*/
bool screens_are_equal(Output *screen1, Output *screen2);
/**
* We have just established a connection to the X server and need the initial
* XRandR information to setup workspaces for each screen.
*
*/
void initialize_randr(xcb_connection_t *conn, int *event_base);
/**
* (Re-)queries the outputs via RandR and stores them in the list of outputs.
*
*/
void randr_query_screens(xcb_connection_t *conn);
/**
* Returns the first output which is active.
*
*/
Output *get_first_output();
/**
* Looks in virtual_screens for the i3Screen which contains coordinates x, y
*
*/
Output *get_screen_containing(int x, int y);
/**
* Gets the screen which is the last one in the given direction, for example
* the screen on the most bottom when direction == D_DOWN, the screen most
* right when direction == D_RIGHT and so on.
*
* This function always returns a screen.
*
*/
Output *get_screen_most(direction_t direction, Output *current);
#endif

View File

@ -3,7 +3,7 @@
*
* i3 - an improved dynamic tiling window manager
*
* © 2009 Michael Stapelberg and contributors
* © 2009-2010 Michael Stapelberg and contributors
*
* See file LICENSE for license information.
*
@ -11,7 +11,7 @@
#include <xcb/xcb.h>
#include "data.h"
#include "xinerama.h"
#include "randr.h"
#ifndef _WORKSPACE_H
#define _WORKSPACE_H
@ -53,7 +53,7 @@ void workspace_show(xcb_connection_t *conn, int workspace);
* screen 1 and you just plugged in screen 1).
*
*/
void workspace_assign_to(Workspace *ws, i3Screen *screen);
void workspace_assign_to(Workspace *ws, Output *screen);
/**
* Initializes the given workspace if it is not already initialized. The given
@ -62,14 +62,14 @@ void workspace_assign_to(Workspace *ws, i3Screen *screen);
* the screen is not attached at the moment.
*
*/
void workspace_initialize(Workspace *ws, i3Screen *screen, bool recheck);
void workspace_initialize(Workspace *ws, Output *screen, bool recheck);
/**
* Gets the first unused workspace for the given screen, taking into account
* the preferred_screen setting of every workspace (workspace assignments).
*
*/
Workspace *get_first_workspace_for_screen(struct screens_head *slist, i3Screen *screen);
Workspace *get_first_workspace_for_screen(Output *screen);
/**
* Unmaps all clients (and stack windows) of the given workspace.

View File

@ -1,62 +0,0 @@
/*
* 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;
/**
* Returns true if both screen objects describe the same screen (checks their
* size and position).
*
*/
bool screens_are_equal(i3Screen *screen1, i3Screen *screen2);
/**
* 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);
/**
* This is called when the rootwindow receives a configure_notify event and
* therefore the number/position of the Xinerama screens could have changed.
*
*/
void xinerama_requery_screens(xcb_connection_t *conn);
/**
* Looks in virtual_screens for the i3Screen whose start coordinates are x, y
*
*/
i3Screen *get_screen_at(int x, int y, struct screens_head *screenlist);
/**
* Looks in virtual_screens for the i3Screen which contains coordinates x, y
*
*/
i3Screen *get_screen_containing(int x, int y);
/**
* Gets the screen which is the last one in the given direction, for example
* the screen on the most bottom when direction == D_DOWN, the screen most
* right when direction == D_RIGHT and so on.
*
* This function always returns a screen.
*
*/
i3Screen *get_screen_most(direction_t direction, i3Screen *current);
#endif

View File

@ -3,7 +3,7 @@
*
* i3 - an improved dynamic tiling window manager
*
* © 2009 Michael Stapelberg and contributors
* © 2009-2010 Michael Stapelberg and contributors
*
* See file LICENSE for license information.
*
@ -37,6 +37,7 @@
#include "floating.h"
#include "resize.h"
#include "log.h"
#include "randr.h"
static struct Stack_Window *get_stack_window(xcb_window_t window_id) {
struct Stack_Window *current;
@ -125,9 +126,9 @@ static bool button_press_stackwin(xcb_connection_t *conn, xcb_button_press_event
*
*/
static bool button_press_bar(xcb_connection_t *conn, xcb_button_press_event_t *event) {
i3Screen *screen;
TAILQ_FOREACH(screen, virtual_screens, screens) {
if (screen->bar != event->event)
Output *output;
TAILQ_FOREACH(output, &outputs, outputs) {
if (output->bar != event->event)
continue;
DLOG("Click on a bar\n");
@ -137,14 +138,14 @@ static bool button_press_bar(xcb_connection_t *conn, xcb_button_press_event_t *e
Workspace *ws = c_ws;
if (event->detail == XCB_BUTTON_INDEX_5) {
while ((ws = TAILQ_NEXT(ws, workspaces)) != TAILQ_END(workspaces_head)) {
if (ws->screen == screen) {
if (ws->output == output) {
workspace_show(conn, ws->num + 1);
return true;
}
}
} else {
while ((ws = TAILQ_PREV(ws, workspaces_head, workspaces)) != TAILQ_END(workspaces)) {
if (ws->screen == screen) {
if (ws->output == output) {
workspace_show(conn, ws->num + 1);
return true;
}
@ -153,11 +154,11 @@ static bool button_press_bar(xcb_connection_t *conn, xcb_button_press_event_t *e
return true;
}
int drawn = 0;
/* Because workspaces can be on different screens, we need to loop
through all of them and decide to count it based on its ->screen */
/* Because workspaces can be on different outputs, we need to loop
through all of them and decide to count it based on its ->output */
Workspace *ws;
TAILQ_FOREACH(ws, workspaces, workspaces) {
if (ws->screen != screen)
if (ws->output != output)
continue;
DLOG("Checking if click was on workspace %d with drawn = %d, tw = %d\n",
ws->num, drawn, ws->text_width);

View File

@ -3,7 +3,7 @@
*
* i3 - an improved dynamic tiling window manager
*
* © 2009 Michael Stapelberg and contributors
* © 2009-2010 Michael Stapelberg and contributors
*
* See file LICENSE for license information.
*
@ -22,7 +22,7 @@
#include "table.h"
#include "layout.h"
#include "i3.h"
#include "xinerama.h"
#include "randr.h"
#include "client.h"
#include "floating.h"
#include "xcb.h"
@ -108,7 +108,7 @@ static void focus_thing(xcb_connection_t *conn, direction_t direction, thing_t t
* right/left/bottom/top and just switch to the workspace on
* the target screen. */
if (thing == THING_SCREEN) {
i3Screen *cs = c_ws->screen;
Output *cs = c_ws->output;
assert(cs != NULL);
Rect bounds = cs->rect;
@ -120,9 +120,9 @@ static void focus_thing(xcb_connection_t *conn, direction_t direction, thing_t t
bounds.y -= bounds.height;
else bounds.y += bounds.height;
i3Screen *target = get_screen_containing(bounds.x, bounds.y);
Output *target = get_screen_containing(bounds.x, bounds.y);
if (target == NULL) {
DLOG("Target screen NULL\n");
DLOG("Target output NULL\n");
/* Wrap around if the target screen is out of bounds */
if (direction == D_RIGHT)
target = get_screen_most(D_LEFT, cs);
@ -162,14 +162,14 @@ static void focus_thing(xcb_connection_t *conn, direction_t direction, thing_t t
} else {
/* Lets see if there is a screen down/up there to which we can switch */
DLOG("container is at %d with height %d\n", container->y, container->height);
i3Screen *screen;
Output *output;
int destination_y = (direction == D_UP ? (container->y - 1) : (container->y + container->height + 1));
if ((screen = get_screen_containing(container->x, destination_y)) == NULL) {
if ((output = get_screen_containing(container->x, destination_y)) == NULL) {
DLOG("Wrapping screen around vertically\n");
/* No screen found? Then wrap */
screen = get_screen_most((direction == D_UP ? D_DOWN : D_UP), container->workspace->screen);
output = get_screen_most((direction == D_UP ? D_DOWN : D_UP), container->workspace->output);
}
t_ws = screen->current_workspace;
t_ws = output->current_workspace;
new_row = (direction == D_UP ? (t_ws->rows - 1) : 0);
}
@ -205,13 +205,13 @@ static void focus_thing(xcb_connection_t *conn, direction_t direction, thing_t t
} else {
/* Lets see if there is a screen left/right here to which we can switch */
DLOG("container is at %d with width %d\n", container->x, container->width);
i3Screen *screen;
Output *output;
int destination_x = (direction == D_LEFT ? (container->x - 1) : (container->x + container->width + 1));
if ((screen = get_screen_containing(destination_x, container->y)) == NULL) {
if ((output = get_screen_containing(destination_x, container->y)) == NULL) {
DLOG("Wrapping screen around horizontally\n");
screen = get_screen_most((direction == D_LEFT ? D_RIGHT : D_LEFT), container->workspace->screen);
output = get_screen_most((direction == D_LEFT ? D_RIGHT : D_LEFT), container->workspace->output);
}
t_ws = screen->current_workspace;
t_ws = output->current_workspace;
new_col = (direction == D_LEFT ? (t_ws->cols - 1) : 0);
}
@ -359,7 +359,7 @@ static void move_current_window(xcb_connection_t *conn, direction_t direction) {
/* Fix colspan/rowspan if itd overlap */
fix_colrowspan(conn, workspace);
render_workspace(conn, workspace->screen, workspace);
render_workspace(conn, workspace->output, workspace);
xcb_flush(conn);
set_focus(conn, current_client, true);
@ -532,7 +532,7 @@ static void move_floating_window_to_workspace(xcb_connection_t *conn, Client *cl
LOG("moving floating\n");
workspace_initialize(t_ws, c_ws->screen, false);
workspace_initialize(t_ws, c_ws->output, false);
/* Check if there is already a fullscreen client on the destination workspace and
* stop moving if so. */
@ -593,7 +593,7 @@ static void move_current_window_to_workspace(xcb_connection_t *conn, int workspa
if (to_focus == NULL)
to_focus = CIRCLEQ_PREV_OR_NULL(&(container->clients), current_client, clients);
workspace_initialize(t_ws, container->workspace->screen, false);
workspace_initialize(t_ws, container->workspace->output, false);
/* Check if there is already a fullscreen client on the destination workspace and
* stop moving if so. */
if (current_client->fullscreen && (t_ws->fullscreen_client != NULL)) {
@ -779,7 +779,7 @@ static void next_previous_workspace(xcb_connection_t *conn, int direction) {
if (ws == c_ws)
return;
if (ws->screen == NULL)
if (ws->output == NULL)
continue;
workspace_show(conn, ws->num + 1);
@ -795,7 +795,7 @@ static void next_previous_workspace(xcb_connection_t *conn, int direction) {
if (ws == c_ws)
return;
if (ws->screen == NULL)
if (ws->output == NULL)
continue;
workspace_show(conn, ws->num + 1);
@ -1113,7 +1113,7 @@ void parse_command(xcb_connection_t *conn, const char *command) {
/* Fix colspan/rowspan if itd overlap */
fix_colrowspan(conn, ws);
render_workspace(conn, ws->screen, ws);
render_workspace(conn, ws->output, ws);
/* Re-focus the client because cleanup_table sets the focus to the last
* focused client inside a container only. */

View File

@ -225,6 +225,8 @@ int format_event(xcb_generic_event_t *e) {
labelRequest[*((uint8_t *) e + 10)]);
break;
default:
if (e->response_type > sizeof(labelEvent) / sizeof(char*))
break;
printf("Event %s following seqnum %d%s.\n",
labelEvent[e->response_type],
seqnum,

View File

@ -412,7 +412,7 @@ void floating_move(xcb_connection_t *conn, Client *currently_focused, direction_
DLOG("floating move\n");
Rect destination = currently_focused->rect;
Rect *screen = &(currently_focused->workspace->screen->rect);
Rect *screen = &(currently_focused->workspace->output->rect);
switch (direction) {
case D_LEFT:

View File

@ -3,7 +3,7 @@
*
* i3 - an improved dynamic tiling window manager
*
* © 2009 Michael Stapelberg and contributors
* © 2009-2010 Michael Stapelberg and contributors
*
* See file LICENSE for license information.
*
@ -17,6 +17,7 @@
#include <xcb/xcb.h>
#include <xcb/xcb_atom.h>
#include <xcb/xcb_icccm.h>
#include <xcb/randr.h>
#include <X11/XKBlib.h>
@ -28,7 +29,7 @@
#include "data.h"
#include "xcb.h"
#include "util.h"
#include "xinerama.h"
#include "randr.h"
#include "config.h"
#include "queue.h"
#include "resize.h"
@ -163,21 +164,21 @@ int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press_event_
*
*/
static void check_crossing_screen_boundary(uint32_t x, uint32_t y) {
i3Screen *screen;
Output *output;
if ((screen = get_screen_containing(x, y)) == NULL) {
if ((output = get_screen_containing(x, y)) == NULL) {
ELOG("ERROR: No such screen\n");
return;
}
if (screen == c_ws->screen)
if (output == c_ws->output)
return;
c_ws->current_row = current_row;
c_ws->current_col = current_col;
c_ws = screen->current_workspace;
c_ws = output->current_workspace;
current_row = c_ws->current_row;
current_col = c_ws->current_col;
DLOG("We're now on virtual screen number %d\n", screen->num);
DLOG("We're now on output %p\n", output);
}
/*
@ -228,7 +229,7 @@ int handle_enter_notify(void *ignored, xcb_connection_t *conn, xcb_enter_notify_
return 1;
}
if (client->workspace != c_ws && client->workspace->screen == c_ws->screen) {
if (client->workspace != c_ws && client->workspace->output == c_ws->output) {
/* This can happen when a client gets assigned to a different workspace than
* the current one (see src/mainx.c:reparent_window). Shortly after it was created,
* an enter_notify will follow. */
@ -395,7 +396,7 @@ int handle_configure_request(void *prophs, xcb_connection_t *conn, xcb_configure
}
client->desired_height = event->height;
render_workspace(conn, c_ws->screen, c_ws);
render_workspace(conn, c_ws->output, c_ws);
xcb_flush(conn);
return 1;
@ -417,26 +418,28 @@ int handle_configure_request(void *prophs, xcb_connection_t *conn, xcb_configure
}
/*
* Configuration notifies are only handled because we need to set up ignore for the following
* enter notify events
* Configuration notifies are only handled because we need to set up ignore for
* the following enter notify events.
*
*/
int handle_configure_event(void *prophs, xcb_connection_t *conn, xcb_configure_notify_event_t *event) {
xcb_window_t root = xcb_setup_roots_iterator(xcb_get_setup(conn)).data->root;
/* We ignore this sequence twice because events for child and frame should be ignored */
add_ignore_event(event->sequence);
add_ignore_event(event->sequence);
if (event->event == root) {
DLOG("event->x = %d, ->y = %d, ->width = %d, ->height = %d\n", event->x, event->y, event->width, event->height);
DLOG("reconfigure of the root window, need to xinerama\n");
/* FIXME: Somehow, this is occuring too often. Therefore, we check for 0/0,
but is there a better way? */
if (event->x == 0 && event->y == 0)
xinerama_requery_screens(conn);
return 1;
}
}
/*
* Gets triggered upon a RandR screen change event, that is when the user
* changes the screen configuration in any way (mode, position, )
*
*/
int handle_screen_change(void *prophs, xcb_connection_t *conn,
xcb_generic_event_t *e) {
DLOG("RandR screen change\n");
randr_query_screens(conn);
return 1;
}
@ -500,7 +503,7 @@ int handle_unmap_notify_event(void *data, xcb_connection_t *conn, xcb_unmap_noti
if (client->dock) {
DLOG("Removing from dock clients\n");
SLIST_REMOVE(&(client->workspace->screen->dock_clients), client, Client, dock_clients);
SLIST_REMOVE(&(client->workspace->output->dock_clients), client, Client, dock_clients);
}
DLOG("child of 0x%08x.\n", client->frame);
@ -524,8 +527,8 @@ int handle_unmap_notify_event(void *data, xcb_connection_t *conn, xcb_unmap_noti
Client *to_focus = (!workspace_empty ? SLIST_FIRST(&(client->workspace->focus_stack)) : NULL);
/* If this workspace is currently active, we dont delete it */
i3Screen *screen;
TAILQ_FOREACH(screen, virtual_screens, screens)
Output *screen;
TAILQ_FOREACH(screen, &outputs, outputs)
if (screen->current_workspace == client->workspace) {
workspace_active = true;
workspace_empty = false;
@ -533,7 +536,7 @@ int handle_unmap_notify_event(void *data, xcb_connection_t *conn, xcb_unmap_noti
}
if (workspace_empty)
client->workspace->screen = NULL;
client->workspace->output = NULL;
/* Remove the urgency flag if set */
client->urgent = false;
@ -739,9 +742,9 @@ int handle_expose_event(void *data, xcb_connection_t *conn, xcb_expose_event_t *
}
/* …or one of the bars? */
i3Screen *screen;
TAILQ_FOREACH(screen, virtual_screens, screens)
if (screen->bar == event->window)
Output *output;
TAILQ_FOREACH(output, &outputs, outputs)
if (output->bar == event->window)
render_layout(conn);
return 1;
}
@ -973,8 +976,8 @@ int handle_hints(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t
/* If the workspace this client is on is not visible, we need to redraw
* the workspace bar */
if (!workspace_is_visible(client->workspace)) {
i3Screen *screen = client->workspace->screen;
render_workspace(conn, screen, screen->current_workspace);
Output *output = client->workspace->output;
render_workspace(conn, output, output->current_workspace);
xcb_flush(conn);
}

View File

@ -22,7 +22,7 @@
#include "xcb.h"
#include "table.h"
#include "util.h"
#include "xinerama.h"
#include "randr.h"
#include "layout.h"
#include "client.h"
#include "floating.h"
@ -206,7 +206,7 @@ void decorate_window(xcb_connection_t *conn, Client *client, xcb_drawable_t draw
*
*/
void reposition_client(xcb_connection_t *conn, Client *client) {
i3Screen *screen;
Output *output;
DLOG("frame 0x%08x needs to be pushed to %dx%d\n", client->frame, client->rect.x, client->rect.y);
/* Note: We can use a pointer to client->x like an array of uint32_ts
@ -217,19 +217,19 @@ void reposition_client(xcb_connection_t *conn, Client *client) {
return;
/* If the client is floating, we need to check if we moved it to a different workspace */
screen = get_screen_containing(client->rect.x + (client->rect.width / 2),
output = get_screen_containing(client->rect.x + (client->rect.width / 2),
client->rect.y + (client->rect.height / 2));
if (client->workspace->screen == screen)
if (client->workspace->output == output)
return;
if (screen == NULL) {
DLOG("Boundary checking disabled, no screen found for (%d, %d)\n", client->rect.x, client->rect.y);
if (output == NULL) {
DLOG("Boundary checking disabled, no output found for (%d, %d)\n", client->rect.x, client->rect.y);
return;
}
DLOG("Client is on workspace %p with screen %p\n", client->workspace, client->workspace->screen);
DLOG("but screen at %d, %d is %p\n", client->rect.x, client->rect.y, screen);
floating_assign_to_workspace(client, screen->current_workspace);
DLOG("Client is on workspace %p with output %p\n", client->workspace, client->workspace->output);
DLOG("but output at %d, %d is %p\n", client->rect.x, client->rect.y, output);
floating_assign_to_workspace(client, output->current_workspace);
set_focus(conn, client, true);
}
@ -566,7 +566,7 @@ void render_container(xcb_connection_t *conn, Container *container) {
static void render_bars(xcb_connection_t *conn, Workspace *r_ws, int width, int *height) {
Client *client;
SLIST_FOREACH(client, &(r_ws->screen->dock_clients), dock_clients) {
SLIST_FOREACH(client, &(r_ws->output->dock_clients), dock_clients) {
DLOG("client is at %d, should be at %d\n", client->rect.y, *height);
if (client->force_reconfigure |
update_if_necessary(&(client->rect.x), r_ws->rect.x) |
@ -586,48 +586,48 @@ static void render_bars(xcb_connection_t *conn, Workspace *r_ws, int width, int
static void render_internal_bar(xcb_connection_t *conn, Workspace *r_ws, int width, int height) {
i3Font *font = load_font(conn, config.font);
i3Screen *screen = r_ws->screen;
Output *output = r_ws->output;
enum { SET_NORMAL = 0, SET_FOCUSED = 1 };
/* Fill the whole bar in black */
xcb_change_gc_single(conn, screen->bargc, XCB_GC_FOREGROUND, get_colorpixel(conn, "#000000"));
xcb_change_gc_single(conn, output->bargc, XCB_GC_FOREGROUND, get_colorpixel(conn, "#000000"));
xcb_rectangle_t rect = {0, 0, width, height};
xcb_poly_fill_rectangle(conn, screen->bar, screen->bargc, 1, &rect);
xcb_poly_fill_rectangle(conn, output->bar, output->bargc, 1, &rect);
/* Set font */
xcb_change_gc_single(conn, screen->bargc, XCB_GC_FONT, font->id);
xcb_change_gc_single(conn, output->bargc, XCB_GC_FONT, font->id);
int drawn = 0;
Workspace *ws;
TAILQ_FOREACH(ws, workspaces, workspaces) {
if (ws->screen != screen)
if (ws->output != output)
continue;
struct Colortriple *color;
if (screen->current_workspace == ws)
if (output->current_workspace == ws)
color = &(config.bar.focused);
else if (ws->urgent)
color = &(config.bar.urgent);
else color = &(config.bar.unfocused);
/* Draw the outer rect */
xcb_draw_rect(conn, screen->bar, screen->bargc, color->border,
xcb_draw_rect(conn, output->bar, output->bargc, color->border,
drawn, /* x */
1, /* y */
ws->text_width + 5 + 5, /* width = text width + 5 px left + 5px right */
height - 2 /* height = max. height - 1 px upper and 1 px bottom border */);
/* Draw the background of this rect */
xcb_draw_rect(conn, screen->bar, screen->bargc, color->background,
xcb_draw_rect(conn, output->bar, output->bargc, color->background,
drawn + 1,
2,
ws->text_width + 4 + 4,
height - 4);
xcb_change_gc_single(conn, screen->bargc, XCB_GC_FOREGROUND, color->text);
xcb_change_gc_single(conn, screen->bargc, XCB_GC_BACKGROUND, color->background);
xcb_image_text_16(conn, ws->name_len, screen->bar, screen->bargc, drawn + 5 /* X */,
xcb_change_gc_single(conn, output->bargc, XCB_GC_FOREGROUND, color->text);
xcb_change_gc_single(conn, output->bargc, XCB_GC_BACKGROUND, color->background);
xcb_image_text_16(conn, ws->name_len, output->bar, output->bargc, drawn + 5 /* X */,
font->height + 1 /* Y = baseline of font */,
(xcb_char2b_t*)ws->name);
drawn += ws->text_width + 12;
@ -668,14 +668,14 @@ void ignore_enter_notify_forall(xcb_connection_t *conn, Workspace *workspace, bo
* Renders the given workspace on the given screen
*
*/
void render_workspace(xcb_connection_t *conn, i3Screen *screen, Workspace *r_ws) {
void render_workspace(xcb_connection_t *conn, Output *output, Workspace *r_ws) {
i3Font *font = load_font(conn, config.font);
int width = r_ws->rect.width;
int height = r_ws->rect.height;
/* Reserve space for dock clients */
Client *client;
SLIST_FOREACH(client, &(screen->dock_clients), dock_clients)
SLIST_FOREACH(client, &(output->dock_clients), dock_clients)
height -= client->desired_height;
/* Space for the internal bar */
@ -748,14 +748,11 @@ void render_workspace(xcb_connection_t *conn, i3Screen *screen, Workspace *r_ws)
*
*/
void render_layout(xcb_connection_t *conn) {
i3Screen *screen;
Output *output;
if (virtual_screens == NULL)
return;
TAILQ_FOREACH(screen, virtual_screens, screens)
if (screen->current_workspace != NULL)
render_workspace(conn, screen, screen->current_workspace);
TAILQ_FOREACH(output, &outputs, outputs)
if (output->current_workspace != NULL)
render_workspace(conn, output, output->current_workspace);
xcb_flush(conn);
}

View File

@ -3,7 +3,7 @@
*
* i3 - an improved dynamic tiling window manager
*
* © 2009 Michael Stapelberg and contributors
* © 2009-2010 Michael Stapelberg and contributors
*
* See file LICENSE for license information.
*
@ -30,7 +30,6 @@
#include <xcb/xcb_property.h>
#include <xcb/xcb_keysyms.h>
#include <xcb/xcb_icccm.h>
#include <xcb/xinerama.h>
#include <ev.h>
@ -45,7 +44,7 @@
#include "table.h"
#include "util.h"
#include "xcb.h"
#include "xinerama.h"
#include "randr.h"
#include "manage.h"
#include "ipc.h"
#include "log.h"
@ -470,9 +469,14 @@ int main(int argc, char *argv[], char *env[]) {
}
}
/* check for Xinerama */
DLOG("Checking for Xinerama...\n");
initialize_xinerama(conn);
DLOG("Checking for XRandR...\n");
int randr_base;
initialize_randr(conn, &randr_base);
xcb_event_set_handler(&evenths,
randr_base + XCB_RANDR_SCREEN_CHANGE_NOTIFY,
handle_screen_change,
NULL);
xcb_flush(conn);
@ -483,14 +487,14 @@ int main(int argc, char *argv[], char *env[]) {
return 1;
}
i3Screen *screen = get_screen_containing(reply->root_x, reply->root_y);
Output *screen = get_screen_containing(reply->root_x, reply->root_y);
if (screen == NULL) {
ELOG("ERROR: No screen at %d x %d, starting on the first screen\n",
reply->root_x, reply->root_y);
screen = TAILQ_FIRST(virtual_screens);
screen = get_first_output();
}
DLOG("Starting on %d\n", screen->current_workspace);
DLOG("Starting on %p\n", screen->current_workspace);
c_ws = screen->current_workspace;
manage_existing_windows(conn, &prophs, root);

View File

@ -255,7 +255,7 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child,
new->titlebar_position = TITLEBAR_OFF;
new->force_reconfigure = true;
new->container = NULL;
SLIST_INSERT_HEAD(&(c_ws->screen->dock_clients), new, dock_clients);
SLIST_INSERT_HEAD(&(c_ws->output->dock_clients), new, dock_clients);
/* If its a dock we cant make it float, so we break */
new->floating = FLOATING_AUTO_OFF;
break;
@ -334,14 +334,14 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child,
LOG("Assignment \"%s\" matches, so putting it on workspace %d\n",
assign->windowclass_title, assign->workspace);
if (c_ws->screen->current_workspace->num == (assign->workspace-1)) {
if (c_ws->output->current_workspace->num == (assign->workspace-1)) {
DLOG("We are already there, no need to do anything\n");
break;
}
DLOG("Changing container/workspace and unmapping the client\n");
Workspace *t_ws = workspace_get(assign->workspace-1);
workspace_initialize(t_ws, c_ws->screen, false);
workspace_initialize(t_ws, c_ws->output, false);
new->container = t_ws->table[t_ws->current_col][t_ws->current_row];
new->workspace = t_ws;

455
src/randr.c Normal file
View File

@ -0,0 +1,455 @@
/*
* vim:ts=8:expandtab
*
* i3 - an improved dynamic tiling window manager
*
* © 2009-2010 Michael Stapelberg and contributors
*
* See file LICENSE for license information.
*
* For more information on RandR, please see the X.org RandR specification at
* http://cgit.freedesktop.org/xorg/proto/randrproto/tree/randrproto.txt
* (take your time to read it completely, it answers all questions).
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>
#include <time.h>
#include <unistd.h>
#include <xcb/xcb.h>
#include <xcb/randr.h>
#include "queue.h"
#include "i3.h"
#include "data.h"
#include "table.h"
#include "util.h"
#include "layout.h"
#include "xcb.h"
#include "config.h"
#include "workspace.h"
#include "log.h"
#include "ewmh.h"
/* While a clean namespace is usually a pretty good thing, we really need
* to use shorter names than the whole xcb_randr_* default names. */
typedef xcb_randr_get_crtc_info_reply_t crtc_info;
typedef xcb_randr_mode_info_t mode_info;
typedef xcb_randr_get_screen_resources_current_reply_t resources_reply;
/* Stores all outputs available in your current session. */
struct outputs_head outputs = TAILQ_HEAD_INITIALIZER(outputs);
/*
* Returns true if both screen objects describe the same screen (checks their
* size and position).
*
*/
bool screens_are_equal(Output *screen1, Output *screen2) {
/* If one of both objects (or both) are NULL, we cannot compare them */
if (screen1 == NULL || screen2 == NULL)
return false;
/* If the pointers are equal, take the short-circuit */
if (screen1 == screen2)
return true;
/* Compare their size and position - other properties are not relevant
* to determine if a screen is equal to another one */
return (memcmp(&(screen1->rect), &(screen2->rect), sizeof(Rect)) == 0);
}
/*
* Get a specific output by its internal X11 id. Used by randr_query_screens
* to check if the output is new (only in the first scan) or if we are
* re-scanning.
*
*/
static Output *get_output_by_id(xcb_randr_output_t id) {
Output *screen;
TAILQ_FOREACH(screen, &outputs, outputs)
if (screen->id == id)
return screen;
return NULL;
}
/*
* Returns the first output which is active.
*
*/
Output *get_first_output() {
Output *screen;
TAILQ_FOREACH(screen, &outputs, outputs) {
if (screen->active)
return screen;
}
return NULL;
}
/*
* Looks in virtual_screens for the Output which contains coordinates x, y
*
*/
Output *get_screen_containing(int x, int y) {
Output *screen;
TAILQ_FOREACH(screen, &outputs, outputs) {
if (!screen->active)
continue;
DLOG("comparing x=%d y=%d with x=%d and y=%d width %d height %d\n",
x, y, screen->rect.x, screen->rect.y, screen->rect.width, screen->rect.height);
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;
}
/*
* Gets the screen which is the last one in the given direction, for example the screen
* on the most bottom when direction == D_DOWN, the screen most right when direction == D_RIGHT
* and so on.
*
* This function always returns a screen.
*
*/
Output *get_screen_most(direction_t direction, Output *current) {
Output *screen, *candidate = NULL;
int position = 0;
TAILQ_FOREACH(screen, &outputs, outputs) {
/* Repeated calls of WIN determine the winner of the comparison */
#define WIN(variable, condition) \
if (variable condition) { \
candidate = screen; \
position = variable; \
} \
break;
if (((direction == D_UP) || (direction == D_DOWN)) &&
(current->rect.x != screen->rect.x))
continue;
if (((direction == D_LEFT) || (direction == D_RIGHT)) &&
(current->rect.y != screen->rect.y))
continue;
switch (direction) {
case D_UP:
WIN(screen->rect.y, <= position);
case D_DOWN:
WIN(screen->rect.y, >= position);
case D_LEFT:
WIN(screen->rect.x, <= position);
case D_RIGHT:
WIN(screen->rect.x, >= position);
}
}
assert(candidate != NULL);
return candidate;
}
static void initialize_output(xcb_connection_t *conn, Output *output,
Workspace *workspace) {
i3Font *font = load_font(conn, config.font);
workspace->output = output;
output->current_workspace = workspace;
/* Create a xoutput for each output */
Rect bar_rect = {output->rect.x,
output->rect.y + output->rect.height - (font->height + 6),
output->rect.x + output->rect.width,
font->height + 6};
uint32_t mask = XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK;
uint32_t values[] = {1, XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_BUTTON_PRESS};
output->bar = create_window(conn, bar_rect, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_CURSOR_LEFT_PTR, true, mask, values);
output->bargc = xcb_generate_id(conn);
xcb_create_gc(conn, output->bargc, output->bar, 0, 0);
SLIST_INIT(&(output->dock_clients));
DLOG("initialized output at (%d, %d) with %d x %d\n",
output->rect.x, output->rect.y, output->rect.width, output->rect.height);
}
/*
* Fills virtual_screens with exactly one screen with width/height of the
* whole X screen.
*
*/
static void disable_randr(xcb_connection_t *conn) {
xcb_screen_t *root_screen = xcb_setup_roots_iterator(xcb_get_setup(conn)).data;
DLOG("RandR extension not found, disabling.\n");
Output *s = scalloc(sizeof(Output));
s->active = true;
s->rect.x = 0;
s->rect.y = 0;
s->rect.width = root_screen->width_in_pixels;
s->rect.height = root_screen->height_in_pixels;
TAILQ_INSERT_TAIL(&outputs, s, outputs);
}
/*
* Searches for a mode in the current RandR configuration by the mode id.
* Returns NULL if no such mode could be found (should never happen).
*
*/
static mode_info *get_mode_by_id(resources_reply *reply, xcb_randr_mode_t mode) {
xcb_randr_mode_info_iterator_t it;
for (it = xcb_randr_get_screen_resources_current_modes_iterator(reply);
it.rem > 0;
xcb_randr_mode_info_next(&it)) {
if (it.data->id == mode)
return it.data;
}
return NULL;
}
/*
* This function needs to be called when changing the mode of an output when
* it already has some workspaces (or a bar window) assigned.
*
* It reconfigures the bar window for the new mode, copies the new rect into
* each workspace on this output and forces all windows on the affected
* workspaces to be reconfigured.
*
* It is necessary to call render_layout() afterwards.
*
*/
static void output_change_mode(xcb_connection_t *conn, Output *output) {
i3Font *font = load_font(conn, config.font);
Workspace *ws;
Client *client;
DLOG("Output mode changed, reconfiguring bar, updating workspaces\n");
Rect bar_rect = {output->rect.x,
output->rect.y + output->rect.height - (font->height + 6),
output->rect.x + output->rect.width,
font->height + 6};
xcb_set_window_rect(conn, output->bar, bar_rect);
/* go through all workspaces and set force_reconfigure */
TAILQ_FOREACH(ws, workspaces, workspaces) {
if (ws->output != output)
continue;
/* Update dimensions from output */
memcpy(&(ws->rect), &(ws->output->rect), sizeof(Rect));
SLIST_FOREACH(client, &(ws->focus_stack), focus_clients)
client->force_reconfigure = true;
}
}
/*
* Gets called by randr_query_screens() for each output. The function adds new
* outputs to the list of outputs, checks if the mode of existing outputs has
* been changed or if an existing output has been disabled.
*
*/
static void handle_output(xcb_connection_t *conn, xcb_randr_output_t id,
xcb_randr_get_output_info_reply_t *output,
xcb_timestamp_t cts, resources_reply *res) {
Workspace *ws;
/* each CRT controller has a position in which we are interested in */
crtc_info *crtc;
/* the CRTC runs in a specific mode, while the position is stored in
* the output itself */
mode_info *mode;
Output *new = get_output_by_id(id);
bool existing = (new != NULL);
if (!existing)
new = scalloc(sizeof(Output));
new->id = id;
asprintf(&new->name, "%.*s",
xcb_randr_get_output_info_name_length(output),
xcb_randr_get_output_info_name(output));
DLOG("found output with name %s\n", new->name);
/* Even if no CRTC is used at the moment, we store the output so that
* we do not need to change the list ever again (we only update the
* position/size) */
if (output->crtc == XCB_NONE) {
if (!existing)
TAILQ_INSERT_TAIL(&outputs, new, outputs);
else if (new->active) {
new->active = false;
new->current_workspace = NULL;
DLOG("Output %s disabled (no CRTC)\n", new->name);
TAILQ_FOREACH(ws, workspaces, workspaces) {
if (ws->output != new)
continue;
workspace_assign_to(ws, get_first_output());
}
}
free(output);
return;
}
xcb_randr_get_crtc_info_cookie_t icookie;
icookie = xcb_randr_get_crtc_info(conn, output->crtc, cts);
if ((crtc = xcb_randr_get_crtc_info_reply(conn, icookie, NULL)) == NULL ||
(mode = get_mode_by_id(res, crtc->mode)) == NULL) {
DLOG("Skipping output %s: could not get CRTC/mode (%p/%p)\n",
new->name, crtc, mode);
free(new);
free(output);
return;
}
new->active = true;
bool updated = update_if_necessary(&(new->rect.x), crtc->x) |
update_if_necessary(&(new->rect.y), crtc->y) |
update_if_necessary(&(new->rect.width), mode->width) |
update_if_necessary(&(new->rect.height), mode->height);
DLOG("mode: %dx%d+%d+%d\n", new->rect.width, new->rect.height,
new->rect.x, new->rect.y);
/* If we dont need to change an existing output or if the output
* does not exist in the first place, the case is simple: we either
* need to insert the new output or we are done. */
if (!updated || !existing) {
if (!existing)
TAILQ_INSERT_TAIL(&outputs, new, outputs);
free(output);
return;
}
output_change_mode(conn, new);
}
/*
* (Re-)queries the outputs via RandR and stores them in the list of outputs.
*
*/
void randr_query_screens(xcb_connection_t *conn) {
xcb_randr_get_screen_resources_current_cookie_t rcookie;
resources_reply *res;
/* timestamp of the configuration so that we get consistent replies to all
* requests (if the configuration changes between our different calls) */
xcb_timestamp_t cts;
/* an output is VGA-1, LVDS-1, etc. (usually physical video outputs) */
xcb_randr_output_t *randr_outputs;
/* Get screen resources (crtcs, outputs, modes) */
rcookie = xcb_randr_get_screen_resources_current(conn, root);
if ((res = xcb_randr_get_screen_resources_current_reply(conn, rcookie, NULL)) == NULL)
die("Could not get RandR screen resources\n");
cts = res->config_timestamp;
int len = xcb_randr_get_screen_resources_current_outputs_length(res);
randr_outputs = xcb_randr_get_screen_resources_current_outputs(res);
/* Request information for each output */
xcb_randr_get_output_info_cookie_t ocookie[len];
for (int i = 0; i < len; i++)
ocookie[i] = xcb_randr_get_output_info(conn, randr_outputs[i], cts);
/* Loop through all outputs available for this X11 screen */
xcb_randr_get_output_info_reply_t *output;
for (int i = 0; i < len; i++) {
if ((output = xcb_randr_get_output_info_reply(conn, ocookie[i], NULL)) == NULL)
continue;
handle_output(conn, randr_outputs[i], output, cts, res);
}
free(res);
Output *screen, *oscreen;
/* Check for clones and reduce the mode to the lowest common mode */
TAILQ_FOREACH(screen, &outputs, outputs) {
if (!screen->active)
continue;
DLOG("screen %p, position (%d, %d), checking for clones\n",
screen, screen->rect.x, screen->rect.y);
TAILQ_FOREACH(oscreen, &outputs, outputs) {
if (oscreen == screen || !oscreen->active)
continue;
if (oscreen->rect.x != screen->rect.x ||
oscreen->rect.y != screen->rect.y)
continue;
DLOG("screen %p has the same position, his mode = %d x %d\n",
oscreen, oscreen->rect.width, oscreen->rect.height);
uint32_t width = min(oscreen->rect.width, screen->rect.width);
uint32_t height = min(oscreen->rect.height, screen->rect.height);
if (update_if_necessary(&(screen->rect.width), width) |
update_if_necessary(&(screen->rect.height), height))
output_change_mode(conn, screen);
if (update_if_necessary(&(oscreen->rect.width), width) |
update_if_necessary(&(oscreen->rect.height), height))
output_change_mode(conn, oscreen);
DLOG("new screen mode %d x %d, oscreen mode %d x %d\n",
screen->rect.width, screen->rect.height,
oscreen->rect.width, oscreen->rect.height);
}
}
ewmh_update_workarea();
/* Just go through each workspace and associate as many screens as we can. */
TAILQ_FOREACH(screen, &outputs, outputs) {
if (!screen->active || screen->current_workspace != NULL)
continue;
Workspace *ws = get_first_workspace_for_screen(screen);
initialize_output(conn, screen, ws);
}
/* render_layout flushes */
render_layout(conn);
}
/*
* We have just established a connection to the X server and need the initial
* XRandR information to setup workspaces for each screen.
*
*/
void initialize_randr(xcb_connection_t *conn, int *event_base) {
const xcb_query_extension_reply_t *extreply;
extreply = xcb_get_extension_data(conn, &xcb_randr_id);
if (!extreply->present)
disable_randr(conn);
else randr_query_screens(conn);
if (event_base != NULL)
*event_base = extreply->first_event;
xcb_randr_select_input(conn, root,
XCB_RANDR_NOTIFY_MASK_SCREEN_CHANGE |
XCB_RANDR_NOTIFY_MASK_OUTPUT_CHANGE |
XCB_RANDR_NOTIFY_MASK_CRTC_CHANGE |
XCB_RANDR_NOTIFY_MASK_OUTPUT_PROPERTY);
xcb_flush(conn);
}

View File

@ -24,7 +24,7 @@
#include "xcb.h"
#include "debug.h"
#include "layout.h"
#include "xinerama.h"
#include "randr.h"
#include "config.h"
#include "floating.h"
#include "workspace.h"
@ -38,7 +38,7 @@
int resize_graphical_handler(xcb_connection_t *conn, Workspace *ws, int first, int second,
resize_orientation_t orientation, xcb_button_press_event_t *event) {
int new_position;
i3Screen *screen = get_screen_containing(event->root_x, event->root_y);
struct xoutput *screen = get_screen_containing(event->root_x, event->root_y);
if (screen == NULL) {
ELOG("BUG: No screen found at this position (%d, %d)\n", event->root_x, event->root_y);
return 1;
@ -49,7 +49,7 @@ int resize_graphical_handler(xcb_connection_t *conn, Workspace *ws, int first, i
* screens during runtime. Instead, we just use the most right and most
* bottom Xinerama screen and use their position + width/height to get
* the area of pixels currently in use */
i3Screen *most_right = get_screen_most(D_RIGHT, screen),
struct xoutput *most_right = get_screen_most(D_RIGHT, screen),
*most_bottom = get_screen_most(D_DOWN, screen);
DLOG("event->event_x = %d, event->root_x = %d\n", event->event_x, event->root_x);

View File

@ -31,7 +31,7 @@
#include "xcb.h"
#include "log.h"
#include "config.h"
#include "xinerama.h"
#include "randr.h"
static xcb_gcontext_t pixmap_gc;
static xcb_pixmap_t pixmap;
@ -170,9 +170,9 @@ void handle_signal(int sig, siginfo_t *info, void *data) {
int width = font_width + 20;
/* Open a popup window on each virtual screen */
i3Screen *screen;
Output *screen;
xcb_window_t win;
TAILQ_FOREACH(screen, virtual_screens, screens) {
TAILQ_FOREACH(screen, &outputs, outputs) {
win = open_input_window(conn, screen->rect, width, height);
/* Create pixmap */

View File

@ -454,7 +454,7 @@ Client *get_matching_client(xcb_connection_t *conn, const char *window_classtitl
DLOG("Getting clients for class \"%s\" / title \"%s\"\n", to_class, to_title);
Workspace *ws;
TAILQ_FOREACH(ws, workspaces, workspaces) {
if (ws->screen == NULL)
if (ws->output == NULL)
continue;
Client *client;

View File

@ -3,7 +3,7 @@
*
* i3 - an improved dynamic tiling window manager
*
* © 2009 Michael Stapelberg and contributors
* © 2009-2010 Michael Stapelberg and contributors
*
* See file LICENSE for license information.
*
@ -22,7 +22,7 @@
#include "config.h"
#include "xcb.h"
#include "table.h"
#include "xinerama.h"
#include "randr.h"
#include "layout.h"
#include "workspace.h"
#include "client.h"
@ -100,7 +100,7 @@ void workspace_set_name(Workspace *ws, const char *name) {
*
*/
bool workspace_is_visible(Workspace *ws) {
return (ws->screen->current_workspace == ws);
return (ws->output->current_workspace == ws);
}
/*
@ -120,22 +120,22 @@ void workspace_show(xcb_connection_t *conn, int workspace) {
c_ws->current_col = current_col;
/* Check if the workspace has not been used yet */
workspace_initialize(t_ws, c_ws->screen, false);
workspace_initialize(t_ws, c_ws->output, false);
if (c_ws->screen != t_ws->screen) {
/* We need to switch to the other screen first */
DLOG("moving over to other screen.\n");
if (c_ws->output != t_ws->output) {
/* We need to switch to the other output first */
DLOG("moving over to other output.\n");
/* Store the old client */
Client *old_client = CUR_CELL->currently_focused;
c_ws = t_ws->screen->current_workspace;
c_ws = t_ws->output->current_workspace;
current_col = c_ws->current_col;
current_row = c_ws->current_row;
if (CUR_CELL->currently_focused != NULL)
need_warp = true;
else {
Rect *dims = &(c_ws->screen->rect);
Rect *dims = &(c_ws->output->rect);
xcb_warp_pointer(conn, XCB_NONE, root, 0, 0, 0, 0,
dims->x + (dims->width / 2), dims->y + (dims->height / 2));
}
@ -147,7 +147,7 @@ void workspace_show(xcb_connection_t *conn, int workspace) {
}
/* Check if we need to change something or if were already there */
if (c_ws->screen->current_workspace->num == (workspace-1)) {
if (c_ws->output->current_workspace->num == (workspace-1)) {
Client *last_focused = SLIST_FIRST(&(c_ws->focus_stack));
if (last_focused != SLIST_END(&(c_ws->focus_stack)))
set_focus(conn, last_focused, true);
@ -160,7 +160,7 @@ void workspace_show(xcb_connection_t *conn, int workspace) {
}
Workspace *old_workspace = c_ws;
c_ws = t_ws->screen->current_workspace = workspace_get(workspace-1);
c_ws = t_ws->output->current_workspace = workspace_get(workspace-1);
/* Unmap all clients of the old workspace */
workspace_unmap_clients(conn, old_workspace);
@ -173,7 +173,7 @@ void workspace_show(xcb_connection_t *conn, int workspace) {
/* POTENTIAL TO IMPROVE HERE: due to the call to _map_clients first and
* render_layout afterwards, there is a short flickering on the source
* workspace (assign ws 3 to screen 0, ws 4 to screen 1, create single
* workspace (assign ws 3 to output 0, ws 4 to output 1, create single
* client on ws 4, move it to ws 3, switch to ws 3, youll see the
* flickering). */
@ -188,7 +188,7 @@ void workspace_show(xcb_connection_t *conn, int workspace) {
/* We can warp the pointer only after the window has been
* reconfigured in render_layout, otherwise the pointer will
* be warped to the old position, which will not work when we
* moved it to another screen. */
* moved it to another output. */
if (last_focused != SLIST_END(&(c_ws->focus_stack)) && need_warp) {
client_warp_pointer_into(conn, last_focused);
xcb_flush(conn);
@ -205,8 +205,8 @@ void workspace_show(xcb_connection_t *conn, int workspace) {
* ("1280x800").
*
*/
static i3Screen *get_screen_from_preference(struct screens_head *slist, char *preference) {
i3Screen *screen;
static Output *get_screen_from_preference(char *preference) {
Output *screen;
char *rest;
int preferred_screen = strtol(preference, &rest, 10);
@ -228,7 +228,7 @@ static i3Screen *get_screen_from_preference(struct screens_head *slist, char *pr
DLOG("Looking for screen at %d x %d\n", x, y);
TAILQ_FOREACH(screen, slist, screens)
TAILQ_FOREACH(screen, &outputs, outputs)
if ((x == INT_MAX || screen->rect.x == x) &&
(y == INT_MAX || screen->rect.y == y)) {
DLOG("found %p\n", screen);
@ -239,7 +239,7 @@ static i3Screen *get_screen_from_preference(struct screens_head *slist, char *pr
return NULL;
} else {
int c = 0;
TAILQ_FOREACH(screen, slist, screens)
TAILQ_FOREACH(screen, &outputs, outputs)
if (c++ == preferred_screen)
return screen;
}
@ -248,28 +248,27 @@ static i3Screen *get_screen_from_preference(struct screens_head *slist, char *pr
}
/*
* Assigns the given workspace to the given screen by correctly updating its
* Assigns the given workspace to the given output by correctly updating its
* state and reconfiguring all the clients on this workspace.
*
* This is called when initializing a screen and when re-assigning it to a
* different screen which just got available (if you configured it to be on
* screen 1 and you just plugged in screen 1).
* This is called when initializing a output and when re-assigning it to a
* different output which just got available (if you configured it to be on
* output 1 and you just plugged in output 1).
*
*/
void workspace_assign_to(Workspace *ws, i3Screen *screen) {
void workspace_assign_to(Workspace *ws, Output *output) {
Client *client;
bool empty = true;
ws->screen = screen;
ws->output = output;
/* Copy the dimensions from the virtual screen */
memcpy(&(ws->rect), &(ws->screen->rect), sizeof(Rect));
/* Copy the dimensions from the virtual output */
memcpy(&(ws->rect), &(ws->output->rect), sizeof(Rect));
ewmh_update_workarea();
/* Force reconfiguration for each client on that workspace */
FOR_TABLE(ws)
CIRCLEQ_FOREACH(client, &(ws->table[cols][rows]->clients), clients) {
SLIST_FOREACH(client, &(ws->focus_stack), focus_clients) {
client->force_reconfigure = true;
empty = false;
}
@ -278,7 +277,7 @@ void workspace_assign_to(Workspace *ws, i3Screen *screen) {
return;
/* Render the workspace to reconfigure the clients. However, they will be visible now, so… */
render_workspace(global_conn, screen, ws);
render_workspace(global_conn, output, ws);
/* …unless we want to see them at the moment, we should hide that workspace */
if (workspace_is_visible(ws))
@ -288,7 +287,7 @@ void workspace_assign_to(Workspace *ws, i3Screen *screen) {
if (c_ws == ws) {
DLOG("Need to adjust c_ws...\n");
c_ws = screen->current_workspace;
c_ws = output->current_workspace;
}
}
@ -299,29 +298,29 @@ void workspace_assign_to(Workspace *ws, i3Screen *screen) {
* the screen is not attached at the moment.
*
*/
void workspace_initialize(Workspace *ws, i3Screen *screen, bool recheck) {
i3Screen *old_screen;
void workspace_initialize(Workspace *ws, Output *output, bool recheck) {
Output *old_output;
if (ws->screen != NULL && !recheck) {
if (ws->output != NULL && !recheck) {
DLOG("Workspace already initialized\n");
return;
}
old_screen = ws->screen;
old_output = ws->output;
/* If this workspace has no preferred screen or if the screen it wants
/* If this workspace has no preferred output or if the output it wants
* to be on is not available at the moment, we initialize it with
* the screen which was given */
* the output which was given */
if (ws->preferred_screen == NULL ||
(ws->screen = get_screen_from_preference(virtual_screens, ws->preferred_screen)) == NULL)
ws->screen = screen;
(ws->output = get_screen_from_preference(ws->preferred_screen)) == NULL)
ws->output = output;
DLOG("old_screen = %p, ws->screen = %p\n", old_screen, ws->screen);
DLOG("old_output = %p, ws->output = %p\n", old_output, ws->output);
/* If the assignment did not change, we do not need to update anything */
if (old_screen != NULL && ws->screen == old_screen)
if (old_output != NULL && ws->output == old_output)
return;
workspace_assign_to(ws, ws->screen);
workspace_assign_to(ws, ws->output);
}
/*
@ -329,13 +328,13 @@ void workspace_initialize(Workspace *ws, i3Screen *screen, bool recheck) {
* the preferred_screen setting of every workspace (workspace assignments).
*
*/
Workspace *get_first_workspace_for_screen(struct screens_head *slist, i3Screen *screen) {
Workspace *get_first_workspace_for_screen(Output *output) {
Workspace *result = NULL;
Workspace *ws;
TAILQ_FOREACH(ws, workspaces, workspaces) {
if (ws->preferred_screen == NULL ||
!screens_are_equal(get_screen_from_preference(slist, ws->preferred_screen), screen))
!screens_are_equal(get_screen_from_preference(ws->preferred_screen), output))
continue;
result = ws;
@ -346,7 +345,7 @@ Workspace *get_first_workspace_for_screen(struct screens_head *slist, i3Screen *
/* No assignment found, returning first unused workspace */
Workspace *ws;
TAILQ_FOREACH(ws, workspaces, workspaces) {
if (ws->screen != NULL)
if (ws->output != NULL)
continue;
result = ws;
@ -364,7 +363,7 @@ Workspace *get_first_workspace_for_screen(struct screens_head *slist, i3Screen *
result = workspace_get(last_ws + 1);
}
workspace_initialize(result, screen, false);
workspace_initialize(result, output, false);
return result;
}
@ -438,14 +437,14 @@ void workspace_unmap_clients(xcb_connection_t *conn, Workspace *u_ws) {
/* Re-assign the workspace of all dock clients which use this workspace */
Client *dock;
DLOG("workspace %p is empty\n", u_ws);
SLIST_FOREACH(dock, &(u_ws->screen->dock_clients), dock_clients) {
SLIST_FOREACH(dock, &(u_ws->output->dock_clients), dock_clients) {
if (dock->workspace != u_ws)
continue;
DLOG("Re-assigning dock client to c_ws (%p)\n", c_ws);
dock->workspace = c_ws;
}
u_ws->screen = NULL;
u_ws->output = NULL;
}
/* Unmap the stack windows on the given workspace, if any */
@ -494,7 +493,7 @@ int workspace_height(Workspace *ws) {
/* Reserve space for dock clients */
Client *client;
SLIST_FOREACH(client, &(ws->screen->dock_clients), dock_clients)
SLIST_FOREACH(client, &(ws->output->dock_clients), dock_clients)
height -= client->desired_height;
/* Space for the internal bar */

View File

@ -1,437 +0,0 @@
/*
* vim:ts=8:expandtab
*
* i3 - an improved dynamic tiling window manager
*
* © 2009 Michael Stapelberg and contributors
*
* See file LICENSE for license information.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>
#include <time.h>
#include <unistd.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"
#include "layout.h"
#include "xcb.h"
#include "config.h"
#include "workspace.h"
#include "log.h"
/* This TAILQ of i3Screens stores the virtual screens, used for handling overlapping screens
* (xrandr --same-as) */
struct screens_head *virtual_screens;
static bool xinerama_enabled = true;
/*
* Returns true if both screen objects describe the same screen (checks their
* size and position).
*
*/
bool screens_are_equal(i3Screen *screen1, i3Screen *screen2) {
/* If one of both objects (or both) are NULL, we cannot compare them */
if (screen1 == NULL || screen2 == NULL)
return false;
/* If the pointers are equal, take the short-circuit */
if (screen1 == screen2)
return true;
/* Compare their size and position - other properties are not relevant
* to determine if a screen is equal to another one */
return (memcmp(&(screen1->rect), &(screen2->rect), sizeof(Rect)) == 0);
}
/*
* Looks in virtual_screens for the i3Screen whose start coordinates are x, y
*
*/
i3Screen *get_screen_at(int x, int y, struct screens_head *screenlist) {
i3Screen *screen;
TAILQ_FOREACH(screen, screenlist, screens)
if (screen->rect.x == x && screen->rect.y == y)
return screen;
return NULL;
}
/*
* Looks in virtual_screens for the i3Screen which contains coordinates x, y
*
*/
i3Screen *get_screen_containing(int x, int y) {
i3Screen *screen;
TAILQ_FOREACH(screen, virtual_screens, screens) {
DLOG("comparing x=%d y=%d with x=%d and y=%d width %d height %d\n",
x, y, screen->rect.x, screen->rect.y, screen->rect.width, screen->rect.height);
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;
}
/*
* Gets the screen which is the last one in the given direction, for example the screen
* on the most bottom when direction == D_DOWN, the screen most right when direction == D_RIGHT
* and so on.
*
* This function always returns a screen.
*
*/
i3Screen *get_screen_most(direction_t direction, i3Screen *current) {
i3Screen *screen, *candidate = NULL;
int position = 0;
TAILQ_FOREACH(screen, virtual_screens, screens) {
/* Repeated calls of WIN determine the winner of the comparison */
#define WIN(variable, condition) \
if (variable condition) { \
candidate = screen; \
position = variable; \
} \
break;
if (((direction == D_UP) || (direction == D_DOWN)) &&
(current->rect.x != screen->rect.x))
continue;
if (((direction == D_LEFT) || (direction == D_RIGHT)) &&
(current->rect.y != screen->rect.y))
continue;
switch (direction) {
case D_UP:
WIN(screen->rect.y, <= position);
case D_DOWN:
WIN(screen->rect.y, >= position);
case D_LEFT:
WIN(screen->rect.x, <= position);
case D_RIGHT:
WIN(screen->rect.x, >= position);
}
}
assert(candidate != NULL);
return candidate;
}
static void initialize_screen(xcb_connection_t *conn, i3Screen *screen, Workspace *workspace) {
i3Font *font = load_font(conn, config.font);
workspace->screen = screen;
screen->current_workspace = workspace;
/* Create a bar for each screen */
Rect bar_rect = {screen->rect.x,
screen->rect.y + screen->rect.height - (font->height + 6),
screen->rect.x + screen->rect.width,
font->height + 6};
uint32_t mask = XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK;
uint32_t values[] = {1, XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_BUTTON_PRESS};
screen->bar = create_window(conn, bar_rect, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_CURSOR_LEFT_PTR, true, mask, values);
screen->bargc = xcb_generate_id(conn);
xcb_create_gc(conn, screen->bargc, screen->bar, 0, 0);
SLIST_INIT(&(screen->dock_clients));
DLOG("that is virtual screen at %d x %d with %d x %d\n",
screen->rect.x, screen->rect.y, screen->rect.width, screen->rect.height);
}
/*
* Fills virtual_screens with exactly one screen with width/height of the whole X server.
*
*/
static void disable_xinerama(xcb_connection_t *conn) {
xcb_screen_t *root_screen = xcb_setup_roots_iterator(xcb_get_setup(conn)).data;
i3Screen *s = calloc(sizeof(i3Screen), 1);
s->rect.x = 0;
s->rect.y = 0;
s->rect.width = root_screen->width_in_pixels;
s->rect.height = root_screen->height_in_pixels;
num_screens = 1;
s->num = 0;
TAILQ_INSERT_TAIL(virtual_screens, s, screens);
xinerama_enabled = false;
}
/*
* Gets the Xinerama screens and converts them to virtual i3Screens (only one screen for two
* Xinerama screen which are configured in clone mode) in the given screenlist
*
*/
static void query_screens(xcb_connection_t *conn, struct screens_head *screenlist) {
xcb_xinerama_query_screens_reply_t *reply;
xcb_xinerama_screen_info_t *screen_info;
time_t before_trying = time(NULL);
/* Try repeatedly to find screens (there might be short timeframes in
* which the X server does not return any screens, such as when rotating
* screens), but not longer than 5 seconds (strictly speaking, only four
* seconds of trying are guaranteed due to the 1-second-resolution) */
while ((time(NULL) - before_trying) < 10) {
reply = xcb_xinerama_query_screens_reply(conn, xcb_xinerama_query_screens_unchecked(conn), NULL);
if (!reply) {
ELOG("Couldn't get Xinerama screens\n");
return;
}
screen_info = xcb_xinerama_query_screens_screen_info(reply);
int screens = xcb_xinerama_query_screens_screen_info_length(reply);
num_screens = 0;
for (int screen = 0; screen < screens; screen++) {
i3Screen *s = get_screen_at(screen_info[screen].x_org, screen_info[screen].y_org, screenlist);
if (s != NULL) {
DLOG("Re-used old Xinerama screen %p\n", s);
/* This screen already exists. We use the littlest screen so that the user
can always see the complete workspace */
s->rect.width = min(s->rect.width, screen_info[screen].width);
s->rect.height = min(s->rect.height, screen_info[screen].height);
} else {
s = calloc(sizeof(i3Screen), 1);
DLOG("Created new Xinerama screen %p\n", s);
s->rect.x = screen_info[screen].x_org;
s->rect.y = screen_info[screen].y_org;
s->rect.width = screen_info[screen].width;
s->rect.height = screen_info[screen].height;
/* We always treat the screen at 0x0 as the primary screen */
if (s->rect.x == 0 && s->rect.y == 0)
TAILQ_INSERT_HEAD(screenlist, s, screens);
else TAILQ_INSERT_TAIL(screenlist, s, screens);
num_screens++;
}
DLOG("found Xinerama screen: %d x %d at %d x %d\n",
screen_info[screen].width, screen_info[screen].height,
screen_info[screen].x_org, screen_info[screen].y_org);
}
free(reply);
if (num_screens == 0) {
ELOG("No screens found. This is weird. Trying again...\n");
/* Give the scheduler a chance to do something else
* and dont hog the CPU */
usleep(250);
continue;
}
break;
}
if (num_screens == 0) {
ELOG("No screens found for 10 seconds. Please fix your setup. i3 will exit now.\n");
exit(0);
}
}
/*
* We have just established a connection to the X server and need the initial Xinerama
* information to setup workspaces for each screen.
*
*/
void initialize_xinerama(xcb_connection_t *conn) {
virtual_screens = scalloc(sizeof(struct screens_head));
TAILQ_INIT(virtual_screens);
if (!xcb_get_extension_data(conn, &xcb_xinerama_id)->present) {
DLOG("Xinerama extension not found, disabling.\n");
disable_xinerama(conn);
} else {
xcb_xinerama_is_active_reply_t *reply;
reply = xcb_xinerama_is_active_reply(conn, xcb_xinerama_is_active(conn), NULL);
if (reply == NULL || !reply->state) {
DLOG("Xinerama is not active (in your X-Server), disabling.\n");
disable_xinerama(conn);
} else
query_screens(conn, virtual_screens);
FREE(reply);
}
i3Screen *screen;
num_screens = 0;
/* Just go through each workspace and associate as many screens as we can. */
TAILQ_FOREACH(screen, virtual_screens, screens) {
screen->num = num_screens;
num_screens++;
Workspace *ws = get_first_workspace_for_screen(virtual_screens, screen);
initialize_screen(conn, screen, ws);
}
}
/*
* This is called when the rootwindow receives a configure_notify event and therefore the
* number/position of the Xinerama screens could have changed.
*
*/
void xinerama_requery_screens(xcb_connection_t *conn) {
i3Font *font = load_font(conn, config.font);
/* POSSIBLE PROBLEM: Is the order of the Xinerama screens always constant? That is, can
it change when I move the --right-of video projector to --left-of? */
if (!xinerama_enabled) {
DLOG("Xinerama is disabled\n");
return;
}
/* We use a separate copy to diff with the previous set of screens */
struct screens_head *new_screens = scalloc(sizeof(struct screens_head));
TAILQ_INIT(new_screens);
query_screens(conn, new_screens);
i3Screen *first = TAILQ_FIRST(new_screens),
*screen,
*old_screen;
int screen_count = 0;
/* Mark each workspace which currently is assigned to a screen, so we
* can garbage-collect afterwards */
Workspace *ws;
TAILQ_FOREACH(ws, workspaces, workspaces)
ws->reassigned = (ws->screen == NULL);
TAILQ_FOREACH(screen, new_screens, screens) {
screen->num = screen_count;
screen->current_workspace = NULL;
TAILQ_FOREACH(old_screen, virtual_screens, screens) {
if (old_screen->num != screen_count)
continue;
DLOG("Found a matching screen\n");
/* Use the same workspace */
screen->current_workspace = old_screen->current_workspace;
/* Re-use the old bar window */
screen->bar = old_screen->bar;
screen->bargc = old_screen->bargc;
DLOG("old_screen->bar = %p\n", old_screen->bar);
Rect bar_rect = {screen->rect.x,
screen->rect.y + screen->rect.height - (font->height + 6),
screen->rect.width,
font->height + 6};
DLOG("configuring bar to be at %d x %d with %d x %d\n",
bar_rect.x, bar_rect.y, bar_rect.height, bar_rect.width);
xcb_configure_window(conn, screen->bar, XCB_CONFIG_WINDOW_X |
XCB_CONFIG_WINDOW_Y |
XCB_CONFIG_WINDOW_WIDTH |
XCB_CONFIG_WINDOW_HEIGHT, &(bar_rect.x));
/* Copy the list head for the dock clients */
screen->dock_clients = old_screen->dock_clients;
SLIST_INIT(&(old_screen->dock_clients));
/* Update the dimensions */
Workspace *ws;
TAILQ_FOREACH(ws, workspaces, workspaces) {
if (ws->screen != old_screen)
continue;
DLOG("re-assigning ws %d\n", ws->num);
memcpy(&(ws->rect), &(screen->rect), sizeof(Rect));
ws->screen = screen;
ws->reassigned = true;
}
break;
}
if (screen->current_workspace == NULL) {
/* Find the first unused workspace, preferring the ones
* which are assigned to this screen and initialize
* the screen with it. */
DLOG("getting first ws for screen %p\n", screen);
Workspace *ws = get_first_workspace_for_screen(new_screens, screen);
initialize_screen(conn, screen, ws);
ws->reassigned = true;
/* As this workspace just got visible (we got a new screen
* without workspace), we need to map its clients */
workspace_map_clients(conn, ws);
}
screen_count++;
}
/* check for dock_clients which are out of bounds */
TAILQ_FOREACH(old_screen, virtual_screens, screens) {
if (SLIST_EMPTY(&(old_screen->dock_clients)))
continue;
DLOG("dock_clients out of bounds at screen %p, reassigning\n", old_screen);
if (SLIST_EMPTY(&(first->dock_clients))) {
first->dock_clients = old_screen->dock_clients;
continue;
}
/* We need to merge the lists */
Client *dock_client;
while (!SLIST_EMPTY(&(old_screen->dock_clients))) {
dock_client = SLIST_FIRST(&(old_screen->dock_clients));
SLIST_INSERT_HEAD(&(first->dock_clients), dock_client, dock_clients);
SLIST_REMOVE_HEAD(&(old_screen->dock_clients), dock_clients);
}
}
/* Check for workspaces which are out of bounds */
TAILQ_FOREACH(ws, workspaces, workspaces) {
if (ws->reassigned)
continue;
DLOG("Closing bar window (%p)\n", ws->screen->bar);
xcb_destroy_window(conn, ws->screen->bar);
DLOG("Workspace %d's screen out of bounds, assigning to first screen\n", ws->num + 1);
workspace_assign_to(ws, first);
}
xcb_flush(conn);
/* Free the old list */
while (!TAILQ_EMPTY(virtual_screens)) {
screen = TAILQ_FIRST(virtual_screens);
TAILQ_REMOVE(virtual_screens, screen, screens);
free(screen);
}
free(virtual_screens);
virtual_screens = new_screens;
/* Check for workspaces which need to be assigned to specific screens
* which may now be available */
TAILQ_FOREACH(ws, workspaces, workspaces) {
if (ws->preferred_screen == NULL || ws->screen == NULL)
continue;
workspace_initialize(ws, ws->screen, true);
}
DLOG("Current workspace is now: %d\n", first->current_workspace);
render_layout(conn);
}