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:
parent
6f72970ece
commit
818e02ef35
|
@ -35,7 +35,7 @@ LDFLAGS += -lxcb-keysyms
|
||||||
LDFLAGS += -lxcb-atom
|
LDFLAGS += -lxcb-atom
|
||||||
LDFLAGS += -lxcb-aux
|
LDFLAGS += -lxcb-aux
|
||||||
LDFLAGS += -lxcb-icccm
|
LDFLAGS += -lxcb-icccm
|
||||||
LDFLAGS += -lxcb-xinerama
|
LDFLAGS += -lxcb-randr
|
||||||
LDFLAGS += -lxcb
|
LDFLAGS += -lxcb
|
||||||
LDFLAGS += -lX11
|
LDFLAGS += -lX11
|
||||||
LDFLAGS += -lev
|
LDFLAGS += -lev
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
*
|
*
|
||||||
* i3 - an improved dynamic tiling window manager
|
* i3 - an improved dynamic tiling window manager
|
||||||
*
|
*
|
||||||
* © 2009 Michael Stapelberg and contributors
|
* © 2009-2010 Michael Stapelberg and contributors
|
||||||
*
|
*
|
||||||
* See file LICENSE for license information.
|
* See file LICENSE for license information.
|
||||||
*
|
*
|
||||||
|
@ -11,6 +11,7 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#include <xcb/xcb.h>
|
#include <xcb/xcb.h>
|
||||||
|
#include <xcb/randr.h>
|
||||||
#include <xcb/xcb_atom.h>
|
#include <xcb/xcb_atom.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
@ -25,11 +26,12 @@
|
||||||
*
|
*
|
||||||
* Let’s start from the biggest to the smallest:
|
* Let’s start from the biggest to the smallest:
|
||||||
*
|
*
|
||||||
* - An i3Screen is a virtual screen (Xinerama). This can be a single one,
|
* - An Output is a physical output on your graphics driver. Outputs which
|
||||||
* though two monitors might be connected, if you’re running clone
|
* are currently in use have (output->active == true). Each output has a
|
||||||
* mode. There can also be multiple of them.
|
* 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
|
* other window managers. Basically, a workspace is a specific set of
|
||||||
* windows, usually grouped thematically (irc, www, work, …). You can switch
|
* windows, usually grouped thematically (irc, www, work, …). You can switch
|
||||||
* between these.
|
* between these.
|
||||||
|
@ -54,7 +56,7 @@ 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 Rect Rect;
|
||||||
typedef struct Screen i3Screen;
|
typedef struct xoutput Output;
|
||||||
|
|
||||||
/******************************************************************************
|
/******************************************************************************
|
||||||
* Helper types
|
* Helper types
|
||||||
|
@ -228,8 +230,8 @@ struct Workspace {
|
||||||
* appended) */
|
* appended) */
|
||||||
TAILQ_HEAD(floating_clients_head, Client) floating_clients;
|
TAILQ_HEAD(floating_clients_head, Client) floating_clients;
|
||||||
|
|
||||||
/** Backpointer to the screen this workspace is on */
|
/** Backpointer to the output this workspace is on */
|
||||||
i3Screen *screen;
|
Output *output;
|
||||||
|
|
||||||
/** This is a two-dimensional dynamic array of
|
/** This is a two-dimensional dynamic array of
|
||||||
* Container-pointers. I’ve always wanted to be a three-star
|
* Container-pointers. I’ve 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
|
* An Output is a physical output on your graphics driver. Outputs which
|
||||||
* monitors might be connected, if you’re running clone mode. There can also
|
* are currently in use have (output->active == true). Each output has a
|
||||||
* be multiple of them.
|
* position and a mode. An output usually corresponds to one connected
|
||||||
|
* screen (except if you are running multiple screens in clone mode).
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
struct Screen {
|
struct xoutput {
|
||||||
/** Virtual screen number */
|
/** Output id, so that we can requery the output directly later */
|
||||||
int num;
|
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 */
|
/** Current workspace selected on this virtual screen */
|
||||||
Workspace *current_workspace;
|
Workspace *current_workspace;
|
||||||
|
@ -519,7 +528,7 @@ struct Screen {
|
||||||
* _NET_WM_WINDOW_TYPE_DOCK */
|
* _NET_WM_WINDOW_TYPE_DOCK */
|
||||||
SLIST_HEAD(dock_clients_head, Client) dock_clients;
|
SLIST_HEAD(dock_clients_head, Client) dock_clients;
|
||||||
|
|
||||||
TAILQ_ENTRY(Screen) screens;
|
TAILQ_ENTRY(xoutput) outputs;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
*
|
*
|
||||||
* i3 - an improved dynamic tiling window manager
|
* 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.
|
* See file LICENSE for license information.
|
||||||
*
|
*
|
||||||
|
@ -11,6 +11,8 @@
|
||||||
#ifndef _HANDLERS_H
|
#ifndef _HANDLERS_H
|
||||||
#define _HANDLERS_H
|
#define _HANDLERS_H
|
||||||
|
|
||||||
|
#include <xcb/randr.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Due to bindings like Mode_switch + <a>, we need to bind some keys in
|
* 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.
|
* 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);
|
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
|
* Configure requests are received when the application wants to resize
|
||||||
* windows on their own.
|
* windows on their own.
|
||||||
|
|
|
@ -79,7 +79,7 @@ void ignore_enter_notify_forall(xcb_connection_t *conn, Workspace *workspace,
|
||||||
* Renders the given workspace on the given screen
|
* 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,
|
* Renders the whole layout, that is: Go through each screen, each workspace,
|
||||||
|
|
|
@ -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
|
|
@ -3,7 +3,7 @@
|
||||||
*
|
*
|
||||||
* i3 - an improved dynamic tiling window manager
|
* i3 - an improved dynamic tiling window manager
|
||||||
*
|
*
|
||||||
* © 2009 Michael Stapelberg and contributors
|
* © 2009-2010 Michael Stapelberg and contributors
|
||||||
*
|
*
|
||||||
* See file LICENSE for license information.
|
* See file LICENSE for license information.
|
||||||
*
|
*
|
||||||
|
@ -11,7 +11,7 @@
|
||||||
#include <xcb/xcb.h>
|
#include <xcb/xcb.h>
|
||||||
|
|
||||||
#include "data.h"
|
#include "data.h"
|
||||||
#include "xinerama.h"
|
#include "randr.h"
|
||||||
|
|
||||||
#ifndef _WORKSPACE_H
|
#ifndef _WORKSPACE_H
|
||||||
#define _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).
|
* 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
|
* 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.
|
* 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
|
* Gets the first unused workspace for the given screen, taking into account
|
||||||
* the preferred_screen setting of every workspace (workspace assignments).
|
* 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.
|
* Unmaps all clients (and stack windows) of the given workspace.
|
||||||
|
|
|
@ -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
|
|
19
src/click.c
19
src/click.c
|
@ -3,7 +3,7 @@
|
||||||
*
|
*
|
||||||
* i3 - an improved dynamic tiling window manager
|
* i3 - an improved dynamic tiling window manager
|
||||||
*
|
*
|
||||||
* © 2009 Michael Stapelberg and contributors
|
* © 2009-2010 Michael Stapelberg and contributors
|
||||||
*
|
*
|
||||||
* See file LICENSE for license information.
|
* See file LICENSE for license information.
|
||||||
*
|
*
|
||||||
|
@ -37,6 +37,7 @@
|
||||||
#include "floating.h"
|
#include "floating.h"
|
||||||
#include "resize.h"
|
#include "resize.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
#include "randr.h"
|
||||||
|
|
||||||
static struct Stack_Window *get_stack_window(xcb_window_t window_id) {
|
static struct Stack_Window *get_stack_window(xcb_window_t window_id) {
|
||||||
struct Stack_Window *current;
|
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) {
|
static bool button_press_bar(xcb_connection_t *conn, xcb_button_press_event_t *event) {
|
||||||
i3Screen *screen;
|
Output *output;
|
||||||
TAILQ_FOREACH(screen, virtual_screens, screens) {
|
TAILQ_FOREACH(output, &outputs, outputs) {
|
||||||
if (screen->bar != event->event)
|
if (output->bar != event->event)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
DLOG("Click on a bar\n");
|
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;
|
Workspace *ws = c_ws;
|
||||||
if (event->detail == XCB_BUTTON_INDEX_5) {
|
if (event->detail == XCB_BUTTON_INDEX_5) {
|
||||||
while ((ws = TAILQ_NEXT(ws, workspaces)) != TAILQ_END(workspaces_head)) {
|
while ((ws = TAILQ_NEXT(ws, workspaces)) != TAILQ_END(workspaces_head)) {
|
||||||
if (ws->screen == screen) {
|
if (ws->output == output) {
|
||||||
workspace_show(conn, ws->num + 1);
|
workspace_show(conn, ws->num + 1);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
while ((ws = TAILQ_PREV(ws, workspaces_head, workspaces)) != TAILQ_END(workspaces)) {
|
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);
|
workspace_show(conn, ws->num + 1);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -153,11 +154,11 @@ static bool button_press_bar(xcb_connection_t *conn, xcb_button_press_event_t *e
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
int drawn = 0;
|
int drawn = 0;
|
||||||
/* Because workspaces can be on different screens, we need to loop
|
/* Because workspaces can be on different outputs, we need to loop
|
||||||
through all of them and decide to count it based on its ->screen */
|
through all of them and decide to count it based on its ->output */
|
||||||
Workspace *ws;
|
Workspace *ws;
|
||||||
TAILQ_FOREACH(ws, workspaces, workspaces) {
|
TAILQ_FOREACH(ws, workspaces, workspaces) {
|
||||||
if (ws->screen != screen)
|
if (ws->output != output)
|
||||||
continue;
|
continue;
|
||||||
DLOG("Checking if click was on workspace %d with drawn = %d, tw = %d\n",
|
DLOG("Checking if click was on workspace %d with drawn = %d, tw = %d\n",
|
||||||
ws->num, drawn, ws->text_width);
|
ws->num, drawn, ws->text_width);
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
*
|
*
|
||||||
* i3 - an improved dynamic tiling window manager
|
* i3 - an improved dynamic tiling window manager
|
||||||
*
|
*
|
||||||
* © 2009 Michael Stapelberg and contributors
|
* © 2009-2010 Michael Stapelberg and contributors
|
||||||
*
|
*
|
||||||
* See file LICENSE for license information.
|
* See file LICENSE for license information.
|
||||||
*
|
*
|
||||||
|
@ -22,7 +22,7 @@
|
||||||
#include "table.h"
|
#include "table.h"
|
||||||
#include "layout.h"
|
#include "layout.h"
|
||||||
#include "i3.h"
|
#include "i3.h"
|
||||||
#include "xinerama.h"
|
#include "randr.h"
|
||||||
#include "client.h"
|
#include "client.h"
|
||||||
#include "floating.h"
|
#include "floating.h"
|
||||||
#include "xcb.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
|
* right/left/bottom/top and just switch to the workspace on
|
||||||
* the target screen. */
|
* the target screen. */
|
||||||
if (thing == THING_SCREEN) {
|
if (thing == THING_SCREEN) {
|
||||||
i3Screen *cs = c_ws->screen;
|
Output *cs = c_ws->output;
|
||||||
assert(cs != NULL);
|
assert(cs != NULL);
|
||||||
Rect bounds = cs->rect;
|
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;
|
bounds.y -= bounds.height;
|
||||||
else 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) {
|
if (target == NULL) {
|
||||||
DLOG("Target screen NULL\n");
|
DLOG("Target output NULL\n");
|
||||||
/* Wrap around if the target screen is out of bounds */
|
/* Wrap around if the target screen is out of bounds */
|
||||||
if (direction == D_RIGHT)
|
if (direction == D_RIGHT)
|
||||||
target = get_screen_most(D_LEFT, cs);
|
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 {
|
} else {
|
||||||
/* Let’s see if there is a screen down/up there to which we can switch */
|
/* Let’s 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);
|
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));
|
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");
|
DLOG("Wrapping screen around vertically\n");
|
||||||
/* No screen found? Then wrap */
|
/* 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);
|
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 {
|
} else {
|
||||||
/* Let’s see if there is a screen left/right here to which we can switch */
|
/* Let’s 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);
|
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));
|
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");
|
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);
|
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 it’d overlap */
|
/* Fix colspan/rowspan if it’d overlap */
|
||||||
fix_colrowspan(conn, workspace);
|
fix_colrowspan(conn, workspace);
|
||||||
|
|
||||||
render_workspace(conn, workspace->screen, workspace);
|
render_workspace(conn, workspace->output, workspace);
|
||||||
xcb_flush(conn);
|
xcb_flush(conn);
|
||||||
|
|
||||||
set_focus(conn, current_client, true);
|
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");
|
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
|
/* Check if there is already a fullscreen client on the destination workspace and
|
||||||
* stop moving if so. */
|
* 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)
|
if (to_focus == NULL)
|
||||||
to_focus = CIRCLEQ_PREV_OR_NULL(&(container->clients), current_client, clients);
|
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
|
/* Check if there is already a fullscreen client on the destination workspace and
|
||||||
* stop moving if so. */
|
* stop moving if so. */
|
||||||
if (current_client->fullscreen && (t_ws->fullscreen_client != NULL)) {
|
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)
|
if (ws == c_ws)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (ws->screen == NULL)
|
if (ws->output == NULL)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
workspace_show(conn, ws->num + 1);
|
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)
|
if (ws == c_ws)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (ws->screen == NULL)
|
if (ws->output == NULL)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
workspace_show(conn, ws->num + 1);
|
workspace_show(conn, ws->num + 1);
|
||||||
|
@ -1113,7 +1113,7 @@ void parse_command(xcb_connection_t *conn, const char *command) {
|
||||||
/* Fix colspan/rowspan if it’d overlap */
|
/* Fix colspan/rowspan if it’d overlap */
|
||||||
fix_colrowspan(conn, ws);
|
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
|
/* Re-focus the client because cleanup_table sets the focus to the last
|
||||||
* focused client inside a container only. */
|
* focused client inside a container only. */
|
||||||
|
|
|
@ -225,6 +225,8 @@ int format_event(xcb_generic_event_t *e) {
|
||||||
labelRequest[*((uint8_t *) e + 10)]);
|
labelRequest[*((uint8_t *) e + 10)]);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
if (e->response_type > sizeof(labelEvent) / sizeof(char*))
|
||||||
|
break;
|
||||||
printf("Event %s following seqnum %d%s.\n",
|
printf("Event %s following seqnum %d%s.\n",
|
||||||
labelEvent[e->response_type],
|
labelEvent[e->response_type],
|
||||||
seqnum,
|
seqnum,
|
||||||
|
|
|
@ -412,7 +412,7 @@ void floating_move(xcb_connection_t *conn, Client *currently_focused, direction_
|
||||||
DLOG("floating move\n");
|
DLOG("floating move\n");
|
||||||
|
|
||||||
Rect destination = currently_focused->rect;
|
Rect destination = currently_focused->rect;
|
||||||
Rect *screen = &(currently_focused->workspace->screen->rect);
|
Rect *screen = &(currently_focused->workspace->output->rect);
|
||||||
|
|
||||||
switch (direction) {
|
switch (direction) {
|
||||||
case D_LEFT:
|
case D_LEFT:
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
*
|
*
|
||||||
* i3 - an improved dynamic tiling window manager
|
* i3 - an improved dynamic tiling window manager
|
||||||
*
|
*
|
||||||
* © 2009 Michael Stapelberg and contributors
|
* © 2009-2010 Michael Stapelberg and contributors
|
||||||
*
|
*
|
||||||
* See file LICENSE for license information.
|
* See file LICENSE for license information.
|
||||||
*
|
*
|
||||||
|
@ -17,6 +17,7 @@
|
||||||
#include <xcb/xcb.h>
|
#include <xcb/xcb.h>
|
||||||
#include <xcb/xcb_atom.h>
|
#include <xcb/xcb_atom.h>
|
||||||
#include <xcb/xcb_icccm.h>
|
#include <xcb/xcb_icccm.h>
|
||||||
|
#include <xcb/randr.h>
|
||||||
|
|
||||||
#include <X11/XKBlib.h>
|
#include <X11/XKBlib.h>
|
||||||
|
|
||||||
|
@ -28,7 +29,7 @@
|
||||||
#include "data.h"
|
#include "data.h"
|
||||||
#include "xcb.h"
|
#include "xcb.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "xinerama.h"
|
#include "randr.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "queue.h"
|
#include "queue.h"
|
||||||
#include "resize.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) {
|
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");
|
ELOG("ERROR: No such screen\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (screen == c_ws->screen)
|
if (output == c_ws->output)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
c_ws->current_row = current_row;
|
c_ws->current_row = current_row;
|
||||||
c_ws->current_col = current_col;
|
c_ws->current_col = current_col;
|
||||||
c_ws = screen->current_workspace;
|
c_ws = output->current_workspace;
|
||||||
current_row = c_ws->current_row;
|
current_row = c_ws->current_row;
|
||||||
current_col = c_ws->current_col;
|
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;
|
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
|
/* 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,
|
* the current one (see src/mainx.c:reparent_window). Shortly after it was created,
|
||||||
* an enter_notify will follow. */
|
* 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;
|
client->desired_height = event->height;
|
||||||
render_workspace(conn, c_ws->screen, c_ws);
|
render_workspace(conn, c_ws->output, c_ws);
|
||||||
xcb_flush(conn);
|
xcb_flush(conn);
|
||||||
|
|
||||||
return 1;
|
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
|
* Configuration notifies are only handled because we need to set up ignore for
|
||||||
* enter notify events
|
* the following enter notify events.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
int handle_configure_event(void *prophs, xcb_connection_t *conn, xcb_configure_notify_event_t *event) {
|
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 */
|
/* We ignore this sequence twice because events for child and frame should be ignored */
|
||||||
add_ignore_event(event->sequence);
|
add_ignore_event(event->sequence);
|
||||||
add_ignore_event(event->sequence);
|
add_ignore_event(event->sequence);
|
||||||
|
|
||||||
if (event->event == root) {
|
return 1;
|
||||||
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? */
|
* Gets triggered upon a RandR screen change event, that is when the user
|
||||||
if (event->x == 0 && event->y == 0)
|
* changes the screen configuration in any way (mode, position, …)
|
||||||
xinerama_requery_screens(conn);
|
*
|
||||||
return 1;
|
*/
|
||||||
}
|
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;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -500,7 +503,7 @@ int handle_unmap_notify_event(void *data, xcb_connection_t *conn, xcb_unmap_noti
|
||||||
|
|
||||||
if (client->dock) {
|
if (client->dock) {
|
||||||
DLOG("Removing from dock clients\n");
|
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);
|
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);
|
Client *to_focus = (!workspace_empty ? SLIST_FIRST(&(client->workspace->focus_stack)) : NULL);
|
||||||
|
|
||||||
/* If this workspace is currently active, we don’t delete it */
|
/* If this workspace is currently active, we don’t delete it */
|
||||||
i3Screen *screen;
|
Output *screen;
|
||||||
TAILQ_FOREACH(screen, virtual_screens, screens)
|
TAILQ_FOREACH(screen, &outputs, outputs)
|
||||||
if (screen->current_workspace == client->workspace) {
|
if (screen->current_workspace == client->workspace) {
|
||||||
workspace_active = true;
|
workspace_active = true;
|
||||||
workspace_empty = false;
|
workspace_empty = false;
|
||||||
|
@ -533,7 +536,7 @@ int handle_unmap_notify_event(void *data, xcb_connection_t *conn, xcb_unmap_noti
|
||||||
}
|
}
|
||||||
|
|
||||||
if (workspace_empty)
|
if (workspace_empty)
|
||||||
client->workspace->screen = NULL;
|
client->workspace->output = NULL;
|
||||||
|
|
||||||
/* Remove the urgency flag if set */
|
/* Remove the urgency flag if set */
|
||||||
client->urgent = false;
|
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? */
|
/* …or one of the bars? */
|
||||||
i3Screen *screen;
|
Output *output;
|
||||||
TAILQ_FOREACH(screen, virtual_screens, screens)
|
TAILQ_FOREACH(output, &outputs, outputs)
|
||||||
if (screen->bar == event->window)
|
if (output->bar == event->window)
|
||||||
render_layout(conn);
|
render_layout(conn);
|
||||||
return 1;
|
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
|
/* If the workspace this client is on is not visible, we need to redraw
|
||||||
* the workspace bar */
|
* the workspace bar */
|
||||||
if (!workspace_is_visible(client->workspace)) {
|
if (!workspace_is_visible(client->workspace)) {
|
||||||
i3Screen *screen = client->workspace->screen;
|
Output *output = client->workspace->output;
|
||||||
render_workspace(conn, screen, screen->current_workspace);
|
render_workspace(conn, output, output->current_workspace);
|
||||||
xcb_flush(conn);
|
xcb_flush(conn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
57
src/layout.c
57
src/layout.c
|
@ -22,7 +22,7 @@
|
||||||
#include "xcb.h"
|
#include "xcb.h"
|
||||||
#include "table.h"
|
#include "table.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "xinerama.h"
|
#include "randr.h"
|
||||||
#include "layout.h"
|
#include "layout.h"
|
||||||
#include "client.h"
|
#include "client.h"
|
||||||
#include "floating.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) {
|
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);
|
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
|
/* 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;
|
return;
|
||||||
|
|
||||||
/* If the client is floating, we need to check if we moved it to a different workspace */
|
/* 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));
|
client->rect.y + (client->rect.height / 2));
|
||||||
if (client->workspace->screen == screen)
|
if (client->workspace->output == output)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (screen == NULL) {
|
if (output == NULL) {
|
||||||
DLOG("Boundary checking disabled, no screen found for (%d, %d)\n", client->rect.x, client->rect.y);
|
DLOG("Boundary checking disabled, no output found for (%d, %d)\n", client->rect.x, client->rect.y);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
DLOG("Client is on workspace %p with screen %p\n", client->workspace, client->workspace->screen);
|
DLOG("Client is on workspace %p with output %p\n", client->workspace, client->workspace->output);
|
||||||
DLOG("but screen at %d, %d is %p\n", client->rect.x, client->rect.y, screen);
|
DLOG("but output at %d, %d is %p\n", client->rect.x, client->rect.y, output);
|
||||||
floating_assign_to_workspace(client, screen->current_workspace);
|
floating_assign_to_workspace(client, output->current_workspace);
|
||||||
|
|
||||||
set_focus(conn, client, true);
|
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) {
|
static void render_bars(xcb_connection_t *conn, Workspace *r_ws, int width, int *height) {
|
||||||
Client *client;
|
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);
|
DLOG("client is at %d, should be at %d\n", client->rect.y, *height);
|
||||||
if (client->force_reconfigure |
|
if (client->force_reconfigure |
|
||||||
update_if_necessary(&(client->rect.x), r_ws->rect.x) |
|
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) {
|
static void render_internal_bar(xcb_connection_t *conn, Workspace *r_ws, int width, int height) {
|
||||||
i3Font *font = load_font(conn, config.font);
|
i3Font *font = load_font(conn, config.font);
|
||||||
i3Screen *screen = r_ws->screen;
|
Output *output = r_ws->output;
|
||||||
enum { SET_NORMAL = 0, SET_FOCUSED = 1 };
|
enum { SET_NORMAL = 0, SET_FOCUSED = 1 };
|
||||||
|
|
||||||
/* Fill the whole bar in black */
|
/* 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_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 */
|
/* 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;
|
int drawn = 0;
|
||||||
Workspace *ws;
|
Workspace *ws;
|
||||||
TAILQ_FOREACH(ws, workspaces, workspaces) {
|
TAILQ_FOREACH(ws, workspaces, workspaces) {
|
||||||
if (ws->screen != screen)
|
if (ws->output != output)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
struct Colortriple *color;
|
struct Colortriple *color;
|
||||||
|
|
||||||
if (screen->current_workspace == ws)
|
if (output->current_workspace == ws)
|
||||||
color = &(config.bar.focused);
|
color = &(config.bar.focused);
|
||||||
else if (ws->urgent)
|
else if (ws->urgent)
|
||||||
color = &(config.bar.urgent);
|
color = &(config.bar.urgent);
|
||||||
else color = &(config.bar.unfocused);
|
else color = &(config.bar.unfocused);
|
||||||
|
|
||||||
/* Draw the outer rect */
|
/* 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 */
|
drawn, /* x */
|
||||||
1, /* y */
|
1, /* y */
|
||||||
ws->text_width + 5 + 5, /* width = text width + 5 px left + 5px right */
|
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 */);
|
height - 2 /* height = max. height - 1 px upper and 1 px bottom border */);
|
||||||
|
|
||||||
/* Draw the background of this rect */
|
/* 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,
|
drawn + 1,
|
||||||
2,
|
2,
|
||||||
ws->text_width + 4 + 4,
|
ws->text_width + 4 + 4,
|
||||||
height - 4);
|
height - 4);
|
||||||
|
|
||||||
xcb_change_gc_single(conn, screen->bargc, XCB_GC_FOREGROUND, color->text);
|
xcb_change_gc_single(conn, output->bargc, XCB_GC_FOREGROUND, color->text);
|
||||||
xcb_change_gc_single(conn, screen->bargc, XCB_GC_BACKGROUND, color->background);
|
xcb_change_gc_single(conn, output->bargc, XCB_GC_BACKGROUND, color->background);
|
||||||
xcb_image_text_16(conn, ws->name_len, screen->bar, screen->bargc, drawn + 5 /* X */,
|
xcb_image_text_16(conn, ws->name_len, output->bar, output->bargc, drawn + 5 /* X */,
|
||||||
font->height + 1 /* Y = baseline of font */,
|
font->height + 1 /* Y = baseline of font */,
|
||||||
(xcb_char2b_t*)ws->name);
|
(xcb_char2b_t*)ws->name);
|
||||||
drawn += ws->text_width + 12;
|
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
|
* 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);
|
i3Font *font = load_font(conn, config.font);
|
||||||
int width = r_ws->rect.width;
|
int width = r_ws->rect.width;
|
||||||
int height = r_ws->rect.height;
|
int height = r_ws->rect.height;
|
||||||
|
|
||||||
/* Reserve space for dock clients */
|
/* Reserve space for dock clients */
|
||||||
Client *client;
|
Client *client;
|
||||||
SLIST_FOREACH(client, &(screen->dock_clients), dock_clients)
|
SLIST_FOREACH(client, &(output->dock_clients), dock_clients)
|
||||||
height -= client->desired_height;
|
height -= client->desired_height;
|
||||||
|
|
||||||
/* Space for the internal bar */
|
/* 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) {
|
void render_layout(xcb_connection_t *conn) {
|
||||||
i3Screen *screen;
|
Output *output;
|
||||||
|
|
||||||
if (virtual_screens == NULL)
|
TAILQ_FOREACH(output, &outputs, outputs)
|
||||||
return;
|
if (output->current_workspace != NULL)
|
||||||
|
render_workspace(conn, output, output->current_workspace);
|
||||||
TAILQ_FOREACH(screen, virtual_screens, screens)
|
|
||||||
if (screen->current_workspace != NULL)
|
|
||||||
render_workspace(conn, screen, screen->current_workspace);
|
|
||||||
|
|
||||||
xcb_flush(conn);
|
xcb_flush(conn);
|
||||||
}
|
}
|
||||||
|
|
22
src/mainx.c
22
src/mainx.c
|
@ -3,7 +3,7 @@
|
||||||
*
|
*
|
||||||
* i3 - an improved dynamic tiling window manager
|
* i3 - an improved dynamic tiling window manager
|
||||||
*
|
*
|
||||||
* © 2009 Michael Stapelberg and contributors
|
* © 2009-2010 Michael Stapelberg and contributors
|
||||||
*
|
*
|
||||||
* See file LICENSE for license information.
|
* See file LICENSE for license information.
|
||||||
*
|
*
|
||||||
|
@ -30,7 +30,6 @@
|
||||||
#include <xcb/xcb_property.h>
|
#include <xcb/xcb_property.h>
|
||||||
#include <xcb/xcb_keysyms.h>
|
#include <xcb/xcb_keysyms.h>
|
||||||
#include <xcb/xcb_icccm.h>
|
#include <xcb/xcb_icccm.h>
|
||||||
#include <xcb/xinerama.h>
|
|
||||||
|
|
||||||
#include <ev.h>
|
#include <ev.h>
|
||||||
|
|
||||||
|
@ -45,7 +44,7 @@
|
||||||
#include "table.h"
|
#include "table.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "xcb.h"
|
#include "xcb.h"
|
||||||
#include "xinerama.h"
|
#include "randr.h"
|
||||||
#include "manage.h"
|
#include "manage.h"
|
||||||
#include "ipc.h"
|
#include "ipc.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
@ -470,9 +469,14 @@ int main(int argc, char *argv[], char *env[]) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* check for Xinerama */
|
DLOG("Checking for XRandR...\n");
|
||||||
DLOG("Checking for Xinerama...\n");
|
int randr_base;
|
||||||
initialize_xinerama(conn);
|
initialize_randr(conn, &randr_base);
|
||||||
|
|
||||||
|
xcb_event_set_handler(&evenths,
|
||||||
|
randr_base + XCB_RANDR_SCREEN_CHANGE_NOTIFY,
|
||||||
|
handle_screen_change,
|
||||||
|
NULL);
|
||||||
|
|
||||||
xcb_flush(conn);
|
xcb_flush(conn);
|
||||||
|
|
||||||
|
@ -483,14 +487,14 @@ int main(int argc, char *argv[], char *env[]) {
|
||||||
return 1;
|
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) {
|
if (screen == NULL) {
|
||||||
ELOG("ERROR: No screen at %d x %d, starting on the first screen\n",
|
ELOG("ERROR: No screen at %d x %d, starting on the first screen\n",
|
||||||
reply->root_x, reply->root_y);
|
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;
|
c_ws = screen->current_workspace;
|
||||||
|
|
||||||
manage_existing_windows(conn, &prophs, root);
|
manage_existing_windows(conn, &prophs, root);
|
||||||
|
|
|
@ -255,7 +255,7 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child,
|
||||||
new->titlebar_position = TITLEBAR_OFF;
|
new->titlebar_position = TITLEBAR_OFF;
|
||||||
new->force_reconfigure = true;
|
new->force_reconfigure = true;
|
||||||
new->container = NULL;
|
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 it’s a dock we can’t make it float, so we break */
|
/* If it’s a dock we can’t make it float, so we break */
|
||||||
new->floating = FLOATING_AUTO_OFF;
|
new->floating = FLOATING_AUTO_OFF;
|
||||||
break;
|
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",
|
LOG("Assignment \"%s\" matches, so putting it on workspace %d\n",
|
||||||
assign->windowclass_title, assign->workspace);
|
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");
|
DLOG("We are already there, no need to do anything\n");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
DLOG("Changing container/workspace and unmapping the client\n");
|
DLOG("Changing container/workspace and unmapping the client\n");
|
||||||
Workspace *t_ws = workspace_get(assign->workspace-1);
|
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->container = t_ws->table[t_ws->current_col][t_ws->current_row];
|
||||||
new->workspace = t_ws;
|
new->workspace = t_ws;
|
||||||
|
|
|
@ -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 don’t 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);
|
||||||
|
}
|
|
@ -24,7 +24,7 @@
|
||||||
#include "xcb.h"
|
#include "xcb.h"
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
#include "layout.h"
|
#include "layout.h"
|
||||||
#include "xinerama.h"
|
#include "randr.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "floating.h"
|
#include "floating.h"
|
||||||
#include "workspace.h"
|
#include "workspace.h"
|
||||||
|
@ -38,7 +38,7 @@
|
||||||
int resize_graphical_handler(xcb_connection_t *conn, Workspace *ws, int first, int second,
|
int resize_graphical_handler(xcb_connection_t *conn, Workspace *ws, int first, int second,
|
||||||
resize_orientation_t orientation, xcb_button_press_event_t *event) {
|
resize_orientation_t orientation, xcb_button_press_event_t *event) {
|
||||||
int new_position;
|
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) {
|
if (screen == NULL) {
|
||||||
ELOG("BUG: No screen found at this position (%d, %d)\n", event->root_x, event->root_y);
|
ELOG("BUG: No screen found at this position (%d, %d)\n", event->root_x, event->root_y);
|
||||||
return 1;
|
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
|
* screens during runtime. Instead, we just use the most right and most
|
||||||
* bottom Xinerama screen and use their position + width/height to get
|
* bottom Xinerama screen and use their position + width/height to get
|
||||||
* the area of pixels currently in use */
|
* 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);
|
*most_bottom = get_screen_most(D_DOWN, screen);
|
||||||
|
|
||||||
DLOG("event->event_x = %d, event->root_x = %d\n", event->event_x, event->root_x);
|
DLOG("event->event_x = %d, event->root_x = %d\n", event->event_x, event->root_x);
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
#include "xcb.h"
|
#include "xcb.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "xinerama.h"
|
#include "randr.h"
|
||||||
|
|
||||||
static xcb_gcontext_t pixmap_gc;
|
static xcb_gcontext_t pixmap_gc;
|
||||||
static xcb_pixmap_t pixmap;
|
static xcb_pixmap_t pixmap;
|
||||||
|
@ -170,9 +170,9 @@ void handle_signal(int sig, siginfo_t *info, void *data) {
|
||||||
int width = font_width + 20;
|
int width = font_width + 20;
|
||||||
|
|
||||||
/* Open a popup window on each virtual screen */
|
/* Open a popup window on each virtual screen */
|
||||||
i3Screen *screen;
|
Output *screen;
|
||||||
xcb_window_t win;
|
xcb_window_t win;
|
||||||
TAILQ_FOREACH(screen, virtual_screens, screens) {
|
TAILQ_FOREACH(screen, &outputs, outputs) {
|
||||||
win = open_input_window(conn, screen->rect, width, height);
|
win = open_input_window(conn, screen->rect, width, height);
|
||||||
|
|
||||||
/* Create pixmap */
|
/* Create pixmap */
|
||||||
|
|
|
@ -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);
|
DLOG("Getting clients for class \"%s\" / title \"%s\"\n", to_class, to_title);
|
||||||
Workspace *ws;
|
Workspace *ws;
|
||||||
TAILQ_FOREACH(ws, workspaces, workspaces) {
|
TAILQ_FOREACH(ws, workspaces, workspaces) {
|
||||||
if (ws->screen == NULL)
|
if (ws->output == NULL)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
Client *client;
|
Client *client;
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
*
|
*
|
||||||
* i3 - an improved dynamic tiling window manager
|
* i3 - an improved dynamic tiling window manager
|
||||||
*
|
*
|
||||||
* © 2009 Michael Stapelberg and contributors
|
* © 2009-2010 Michael Stapelberg and contributors
|
||||||
*
|
*
|
||||||
* See file LICENSE for license information.
|
* See file LICENSE for license information.
|
||||||
*
|
*
|
||||||
|
@ -22,7 +22,7 @@
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "xcb.h"
|
#include "xcb.h"
|
||||||
#include "table.h"
|
#include "table.h"
|
||||||
#include "xinerama.h"
|
#include "randr.h"
|
||||||
#include "layout.h"
|
#include "layout.h"
|
||||||
#include "workspace.h"
|
#include "workspace.h"
|
||||||
#include "client.h"
|
#include "client.h"
|
||||||
|
@ -100,7 +100,7 @@ void workspace_set_name(Workspace *ws, const char *name) {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
bool workspace_is_visible(Workspace *ws) {
|
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;
|
c_ws->current_col = current_col;
|
||||||
|
|
||||||
/* Check if the workspace has not been used yet */
|
/* 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) {
|
if (c_ws->output != t_ws->output) {
|
||||||
/* We need to switch to the other screen first */
|
/* We need to switch to the other output first */
|
||||||
DLOG("moving over to other screen.\n");
|
DLOG("moving over to other output.\n");
|
||||||
|
|
||||||
/* Store the old client */
|
/* Store the old client */
|
||||||
Client *old_client = CUR_CELL->currently_focused;
|
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_col = c_ws->current_col;
|
||||||
current_row = c_ws->current_row;
|
current_row = c_ws->current_row;
|
||||||
if (CUR_CELL->currently_focused != NULL)
|
if (CUR_CELL->currently_focused != NULL)
|
||||||
need_warp = true;
|
need_warp = true;
|
||||||
else {
|
else {
|
||||||
Rect *dims = &(c_ws->screen->rect);
|
Rect *dims = &(c_ws->output->rect);
|
||||||
xcb_warp_pointer(conn, XCB_NONE, root, 0, 0, 0, 0,
|
xcb_warp_pointer(conn, XCB_NONE, root, 0, 0, 0, 0,
|
||||||
dims->x + (dims->width / 2), dims->y + (dims->height / 2));
|
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 we’re already there */
|
/* Check if we need to change something or if we’re 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));
|
Client *last_focused = SLIST_FIRST(&(c_ws->focus_stack));
|
||||||
if (last_focused != SLIST_END(&(c_ws->focus_stack)))
|
if (last_focused != SLIST_END(&(c_ws->focus_stack)))
|
||||||
set_focus(conn, last_focused, true);
|
set_focus(conn, last_focused, true);
|
||||||
|
@ -160,7 +160,7 @@ void workspace_show(xcb_connection_t *conn, int workspace) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Workspace *old_workspace = c_ws;
|
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 */
|
/* Unmap all clients of the old workspace */
|
||||||
workspace_unmap_clients(conn, 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
|
/* POTENTIAL TO IMPROVE HERE: due to the call to _map_clients first and
|
||||||
* render_layout afterwards, there is a short flickering on the source
|
* 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, you’ll see the
|
* client on ws 4, move it to ws 3, switch to ws 3, you’ll see the
|
||||||
* flickering). */
|
* 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
|
/* We can warp the pointer only after the window has been
|
||||||
* reconfigured in render_layout, otherwise the pointer will
|
* reconfigured in render_layout, otherwise the pointer will
|
||||||
* be warped to the old position, which will not work when we
|
* 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) {
|
if (last_focused != SLIST_END(&(c_ws->focus_stack)) && need_warp) {
|
||||||
client_warp_pointer_into(conn, last_focused);
|
client_warp_pointer_into(conn, last_focused);
|
||||||
xcb_flush(conn);
|
xcb_flush(conn);
|
||||||
|
@ -205,8 +205,8 @@ void workspace_show(xcb_connection_t *conn, int workspace) {
|
||||||
* ("1280x800").
|
* ("1280x800").
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
static i3Screen *get_screen_from_preference(struct screens_head *slist, char *preference) {
|
static Output *get_screen_from_preference(char *preference) {
|
||||||
i3Screen *screen;
|
Output *screen;
|
||||||
char *rest;
|
char *rest;
|
||||||
int preferred_screen = strtol(preference, &rest, 10);
|
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);
|
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) &&
|
if ((x == INT_MAX || screen->rect.x == x) &&
|
||||||
(y == INT_MAX || screen->rect.y == y)) {
|
(y == INT_MAX || screen->rect.y == y)) {
|
||||||
DLOG("found %p\n", screen);
|
DLOG("found %p\n", screen);
|
||||||
|
@ -239,7 +239,7 @@ static i3Screen *get_screen_from_preference(struct screens_head *slist, char *pr
|
||||||
return NULL;
|
return NULL;
|
||||||
} else {
|
} else {
|
||||||
int c = 0;
|
int c = 0;
|
||||||
TAILQ_FOREACH(screen, slist, screens)
|
TAILQ_FOREACH(screen, &outputs, outputs)
|
||||||
if (c++ == preferred_screen)
|
if (c++ == preferred_screen)
|
||||||
return screen;
|
return screen;
|
||||||
}
|
}
|
||||||
|
@ -248,37 +248,36 @@ 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.
|
* state and reconfiguring all the clients on this workspace.
|
||||||
*
|
*
|
||||||
* This is called when initializing a screen and when re-assigning it to a
|
* This is called when initializing a output and when re-assigning it to a
|
||||||
* different screen which just got available (if you configured it to be on
|
* different output which just got available (if you configured it to be on
|
||||||
* screen 1 and you just plugged in screen 1).
|
* 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;
|
Client *client;
|
||||||
bool empty = true;
|
bool empty = true;
|
||||||
|
|
||||||
ws->screen = screen;
|
ws->output = output;
|
||||||
|
|
||||||
/* Copy the dimensions from the virtual screen */
|
/* Copy the dimensions from the virtual output */
|
||||||
memcpy(&(ws->rect), &(ws->screen->rect), sizeof(Rect));
|
memcpy(&(ws->rect), &(ws->output->rect), sizeof(Rect));
|
||||||
|
|
||||||
ewmh_update_workarea();
|
ewmh_update_workarea();
|
||||||
|
|
||||||
/* Force reconfiguration for each client on that workspace */
|
/* Force reconfiguration for each client on that workspace */
|
||||||
FOR_TABLE(ws)
|
SLIST_FOREACH(client, &(ws->focus_stack), focus_clients) {
|
||||||
CIRCLEQ_FOREACH(client, &(ws->table[cols][rows]->clients), clients) {
|
client->force_reconfigure = true;
|
||||||
client->force_reconfigure = true;
|
empty = false;
|
||||||
empty = false;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (empty)
|
if (empty)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Render the workspace to reconfigure the clients. However, they will be visible now, so… */
|
/* 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 */
|
/* …unless we want to see them at the moment, we should hide that workspace */
|
||||||
if (workspace_is_visible(ws))
|
if (workspace_is_visible(ws))
|
||||||
|
@ -288,7 +287,7 @@ void workspace_assign_to(Workspace *ws, i3Screen *screen) {
|
||||||
|
|
||||||
if (c_ws == ws) {
|
if (c_ws == ws) {
|
||||||
DLOG("Need to adjust c_ws...\n");
|
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.
|
* the screen is not attached at the moment.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void workspace_initialize(Workspace *ws, i3Screen *screen, bool recheck) {
|
void workspace_initialize(Workspace *ws, Output *output, bool recheck) {
|
||||||
i3Screen *old_screen;
|
Output *old_output;
|
||||||
|
|
||||||
if (ws->screen != NULL && !recheck) {
|
if (ws->output != NULL && !recheck) {
|
||||||
DLOG("Workspace already initialized\n");
|
DLOG("Workspace already initialized\n");
|
||||||
return;
|
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
|
* 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 ||
|
if (ws->preferred_screen == NULL ||
|
||||||
(ws->screen = get_screen_from_preference(virtual_screens, ws->preferred_screen)) == NULL)
|
(ws->output = get_screen_from_preference(ws->preferred_screen)) == NULL)
|
||||||
ws->screen = screen;
|
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 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;
|
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).
|
* 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 *result = NULL;
|
||||||
|
|
||||||
Workspace *ws;
|
Workspace *ws;
|
||||||
TAILQ_FOREACH(ws, workspaces, workspaces) {
|
TAILQ_FOREACH(ws, workspaces, workspaces) {
|
||||||
if (ws->preferred_screen == NULL ||
|
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;
|
continue;
|
||||||
|
|
||||||
result = ws;
|
result = ws;
|
||||||
|
@ -346,7 +345,7 @@ Workspace *get_first_workspace_for_screen(struct screens_head *slist, i3Screen *
|
||||||
/* No assignment found, returning first unused workspace */
|
/* No assignment found, returning first unused workspace */
|
||||||
Workspace *ws;
|
Workspace *ws;
|
||||||
TAILQ_FOREACH(ws, workspaces, workspaces) {
|
TAILQ_FOREACH(ws, workspaces, workspaces) {
|
||||||
if (ws->screen != NULL)
|
if (ws->output != NULL)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
result = ws;
|
result = ws;
|
||||||
|
@ -364,7 +363,7 @@ Workspace *get_first_workspace_for_screen(struct screens_head *slist, i3Screen *
|
||||||
result = workspace_get(last_ws + 1);
|
result = workspace_get(last_ws + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
workspace_initialize(result, screen, false);
|
workspace_initialize(result, output, false);
|
||||||
return result;
|
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 */
|
/* Re-assign the workspace of all dock clients which use this workspace */
|
||||||
Client *dock;
|
Client *dock;
|
||||||
DLOG("workspace %p is empty\n", u_ws);
|
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)
|
if (dock->workspace != u_ws)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
DLOG("Re-assigning dock client to c_ws (%p)\n", c_ws);
|
DLOG("Re-assigning dock client to c_ws (%p)\n", c_ws);
|
||||||
dock->workspace = c_ws;
|
dock->workspace = c_ws;
|
||||||
}
|
}
|
||||||
u_ws->screen = NULL;
|
u_ws->output = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Unmap the stack windows on the given workspace, if any */
|
/* Unmap the stack windows on the given workspace, if any */
|
||||||
|
@ -494,7 +493,7 @@ int workspace_height(Workspace *ws) {
|
||||||
|
|
||||||
/* Reserve space for dock clients */
|
/* Reserve space for dock clients */
|
||||||
Client *client;
|
Client *client;
|
||||||
SLIST_FOREACH(client, &(ws->screen->dock_clients), dock_clients)
|
SLIST_FOREACH(client, &(ws->output->dock_clients), dock_clients)
|
||||||
height -= client->desired_height;
|
height -= client->desired_height;
|
||||||
|
|
||||||
/* Space for the internal bar */
|
/* Space for the internal bar */
|
||||||
|
|
437
src/xinerama.c
437
src/xinerama.c
|
@ -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 don’t 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);
|
|
||||||
}
|
|
Loading…
Reference in New Issue