Go to the documentation of this file.
16 #include <xcb/randr.h>
19 xcb_randr_get_output_primary_reply_t *
primary;
37 if (output->
id ==
id) {
52 bool get_primary = (strcasecmp(
"primary", name) == 0);
54 if (require_active && !output->
active) {
57 if (output->
primary && get_primary) {
76 Output *output, *result = NULL;
93 die(
"No usable outputs available.\n");
121 DLOG(
"comparing x=%d y=%d with x=%d and y=%d width %d height %d\n",
138 unsigned int mid_x = rect.
x + rect.
width / 2;
139 unsigned int mid_y = rect.
y + rect.
height / 2;
155 DLOG(
"comparing x=%d y=%d %dx%d with x=%d and y=%d %dx%d\n",
175 int lx = rect.
x, uy = rect.
y;
182 int lx_o = (int)output->
rect.
x, uy_o = (
int)output->
rect.
y;
184 DLOG(
"comparing x=%d y=%d with x=%d and y=%d width %d height %d\n",
186 int left =
max(lx, lx_o);
187 int right =
min(rx, rx_o);
188 int bottom =
min(by, by_o);
189 int top =
max(uy, uy_o);
190 if (left < right && bottom > top) {
191 long area = (right - left) * (bottom - top);
192 if (area > max_area) {
217 else if (direction ==
D_LEFT)
219 else if (direction ==
D_DOWN)
251 other = &(output->
rect);
253 if ((direction ==
D_RIGHT && other->x > cur->
x) ||
254 (direction ==
D_LEFT && other->x < cur->
x)) {
257 if ((other->y + other->height) <= cur->
y ||
258 (cur->
y + cur->
height) <= other->y)
260 }
else if ((direction ==
D_DOWN && other->y > cur->
y) ||
261 (direction ==
D_UP && other->y < cur->
y)) {
264 if ((other->x + other->width) <= cur->
x ||
265 (cur->
x + cur->
width) <= other->x)
279 if ((direction ==
D_RIGHT && other->x < best->
rect.
x) ||
280 (direction ==
D_LEFT && other->x > best->
rect.
x) ||
281 (direction ==
D_DOWN && other->y < best->
rect.
y) ||
282 (direction ==
D_UP && other->y > best->
rect.
y)) {
289 if ((direction ==
D_RIGHT && other->x > best->
rect.
x) ||
290 (direction ==
D_LEFT && other->x < best->
rect.
x) ||
291 (direction ==
D_DOWN && other->y > best->
rect.
y) ||
292 (direction ==
D_UP && other->y < best->
rect.
y)) {
330 Con *con = NULL, *current;
343 DLOG(
"Using existing con %p / %s\n", con, con->
name);
351 con->
type = CT_OUTPUT;
364 DLOG(
"Not adding workspace, this was a reused con\n");
368 DLOG(
"Changing layout, adding top/bottom dockarea\n");
370 topdock->
type = CT_DOCKAREA;
375 match->
dock = M_DOCK_TOP;
390 DLOG(
"adding main content container\n");
392 content->
type = CT_CON;
404 bottomdock->
type = CT_DOCKAREA;
409 match->
dock = M_DOCK_BOTTOM;
454 if (output->
con != workspace_out) {
458 DLOG(
"Moving workspace \"%s\" from output \"%s\" to \"%s\" due to assignment\n",
492 LOG(
"Initializing first assigned workspace \"%s\" for output \"%s\"\n",
499 DLOG(
"Now adding a workspace\n");
503 if (previous_focus) {
520 DLOG(
"Output mode changed, updating rect\n");
521 assert(
output->con != NULL);
524 Con *content, *workspace, *child;
532 TAILQ_FOREACH (child, &(workspace->floating_head), floating_windows) {
548 DLOG(
"Setting workspace [%d,%s]'s layout to %d.\n", workspace->
num, workspace->
name, workspace->
layout);
549 if ((child =
TAILQ_FIRST(&(workspace->nodes_head)))) {
552 DLOG(
"Setting child [%d,%s]'s layout to %d.\n", child->
num, child->
name, child->
layout);
563 #if XCB_RANDR_MINOR_VERSION < 5
572 DLOG(
"Querying outputs using RandR 1.5\n");
573 xcb_generic_error_t *err;
574 xcb_randr_get_monitors_reply_t *monitors =
575 xcb_randr_get_monitors_reply(
578 ELOG(
"Could not get RandR monitors: X11 error code %d\n", err->error_code);
589 output->to_be_disabled =
true;
593 DLOG(
"%d RandR monitors found (timestamp %d)\n",
594 xcb_randr_get_monitors_monitors_length(monitors),
595 monitors->timestamp);
597 xcb_randr_monitor_info_iterator_t iter;
598 for (iter = xcb_randr_get_monitors_monitors_iterator(monitors);
600 xcb_randr_monitor_info_next(&iter)) {
601 const xcb_randr_monitor_info_t *monitor_info = iter.data;
602 xcb_get_atom_name_reply_t *atom_reply =
603 xcb_get_atom_name_reply(
604 conn, xcb_get_atom_name(
conn, monitor_info->name), &err);
606 ELOG(
"Could not get RandR monitor name: X11 error code %d\n", err->error_code);
612 xcb_get_atom_name_name_length(atom_reply),
613 xcb_get_atom_name_name(atom_reply));
623 xcb_randr_output_t *randr_outputs = xcb_randr_monitor_info_outputs(monitor_info);
624 int randr_output_len = xcb_randr_monitor_info_outputs_length(monitor_info);
625 for (
int i = 0; i < randr_output_len; i++) {
626 xcb_randr_output_t randr_output = randr_outputs[i];
628 xcb_randr_get_output_info_reply_t *info =
629 xcb_randr_get_output_info_reply(
conn,
630 xcb_randr_get_output_info(
conn, randr_output, monitors->timestamp),
633 if (info != NULL && info->crtc != XCB_NONE) {
636 xcb_randr_get_output_info_name_length(info),
637 xcb_randr_get_output_info_name(info));
639 if (strcmp(
name, oname) != 0) {
655 if (monitor_info->primary) {
664 new->to_be_disabled =
false;
666 new->primary = monitor_info->primary;
673 new->changed = update_x || update_y || update_w || update_h;
675 DLOG(
"name %s, x %d, y %d, width %d px, height %d px, width %d mm, height %d mm, primary %d, automatic %d\n",
677 monitor_info->x, monitor_info->y, monitor_info->width, monitor_info->height,
678 monitor_info->width_in_millimeters, monitor_info->height_in_millimeters,
679 monitor_info->primary, monitor_info->automatic);
696 xcb_randr_get_output_info_reply_t *output,
698 xcb_randr_get_screen_resources_current_reply_t *res) {
700 xcb_randr_get_crtc_info_reply_t *crtc;
703 bool existing = (
new != NULL);
718 xcb_randr_get_output_info_name_length(output),
719 xcb_randr_get_output_info_name(output));
727 if (output->crtc == XCB_NONE) {
733 }
else if (new->active)
734 new->to_be_disabled =
true;
738 xcb_randr_get_crtc_info_cookie_t icookie;
739 icookie = xcb_randr_get_crtc_info(
conn, output->crtc, cts);
740 if ((crtc = xcb_randr_get_crtc_info_reply(
conn, icookie, NULL)) == NULL) {
741 DLOG(
"Skipping output %s: could not get CRTC (%p)\n",
751 const bool updated = update_x || update_y || update_w || update_h;
753 new->active = (
new->rect.width != 0 &&
new->rect.height != 0);
755 DLOG(
"width/height 0/0, disabling output\n");
759 DLOG(
"mode: %dx%d+%d+%d\n", new->rect.width, new->rect.height,
760 new->rect.x, new->rect.y);
765 if (!updated || !existing) {
783 DLOG(
"Querying outputs using RandR ≤ 1.4\n");
786 xcb_randr_get_screen_resources_current_cookie_t rcookie;
787 rcookie = xcb_randr_get_screen_resources_current(
conn,
root);
788 xcb_randr_get_output_primary_cookie_t pcookie;
789 pcookie = xcb_randr_get_output_primary(
conn,
root);
791 if ((
primary = xcb_randr_get_output_primary_reply(
conn, pcookie, NULL)) == NULL)
792 ELOG(
"Could not get RandR primary output\n");
796 xcb_randr_get_screen_resources_current_reply_t *res =
797 xcb_randr_get_screen_resources_current_reply(
conn, rcookie, NULL);
799 ELOG(
"Could not query screen resources.\n");
805 const xcb_timestamp_t cts = res->config_timestamp;
807 const int len = xcb_randr_get_screen_resources_current_outputs_length(res);
810 xcb_randr_output_t *randr_outputs = xcb_randr_get_screen_resources_current_outputs(res);
813 xcb_randr_get_output_info_cookie_t ocookie[len];
814 for (
int i = 0; i < len; i++)
815 ocookie[i] = xcb_randr_get_output_info(
conn, randr_outputs[i], cts);
818 for (
int i = 0; i < len; i++) {
819 xcb_randr_get_output_info_reply_t *output;
821 if ((output = xcb_randr_get_output_info_reply(
conn, ocookie[i], NULL)) == NULL)
852 if (current != next &&
TAILQ_EMPTY(&(current->focus_head))) {
854 DLOG(
"Getting rid of current = %p / %s (empty, unfocused)\n", current, current->
name);
858 DLOG(
"Detaching current = %p / %s\n", current, current->
name);
860 DLOG(
"Re-attaching current = %p / %s\n", current, current->
name);
862 DLOG(
"Fixing the coordinates of floating containers\n");
864 TAILQ_FOREACH (floating_con, &(current->floating_head), floating_windows) {
871 DLOG(
"now focusing next = %p\n", next);
879 if (child->
type != CT_DOCKAREA) {
882 DLOG(
"Handling dock con %p\n", child);
889 DLOG(
"Moving dock client %p to nc %p\n", dock, nc);
891 DLOG(
"Re-attaching\n");
897 DLOG(
"Destroying disappearing con %p\n", con);
916 DLOG(
"Active RandR output found. Disabling root output.\n");
921 DLOG(
"No active RandR output found. Enabling root output.\n");
930 DLOG(
"output %p / %s, position (%d, %d), checking for clones\n",
943 DLOG(
"output %p has the same position, its mode = %d x %d\n",
950 if (update_w || update_h) {
960 DLOG(
"new output mode %d x %d, other mode %d x %d\n",
971 if (output->
active && output->
con == NULL) {
987 DLOG(
"No output %s found, moving its old content to first output\n", con->
name);
1045 if (output->
con != NULL) {
1068 const xcb_query_extension_reply_t *extreply;
1073 extreply = xcb_get_extension_data(
conn, &xcb_randr_id);
1074 if (!extreply->present) {
1075 DLOG(
"RandR is not present, activating root output.\n");
1080 xcb_generic_error_t *err;
1081 xcb_randr_query_version_reply_t *randr_version =
1082 xcb_randr_query_version_reply(
1083 conn, xcb_randr_query_version(
conn, XCB_RANDR_MAJOR_VERSION, XCB_RANDR_MINOR_VERSION), &err);
1085 ELOG(
"Could not query RandR version: X11 error code %d\n", err->error_code);
1092 (randr_version->minor_version >= 5) &&
1095 free(randr_version);
1099 if (event_base != NULL)
1100 *event_base = extreply->first_event;
1103 XCB_RANDR_NOTIFY_MASK_SCREEN_CHANGE |
1104 XCB_RANDR_NOTIFY_MASK_OUTPUT_CHANGE |
1105 XCB_RANDR_NOTIFY_MASK_CRTC_CHANGE |
1106 XCB_RANDR_NOTIFY_MASK_OUTPUT_PROPERTY);
enum Match::@15 insert_where
#define TAILQ_FIRST(head)
Output * create_root_output(xcb_connection_t *conn)
Creates an output covering the root window.
void randr_disable_output(Output *output)
Disables the output and moves its content.
Con * con_new(Con *parent, i3Window *window)
A wrapper for con_new_skeleton, to retain the old con_new behaviour.
bool update_if_necessary(uint32_t *destination, const uint32_t new_value)
Updates *destination with new_value and returns true if it was changed or false if it was the same.
static void randr_query_outputs_14(void)
void match_init(Match *match)
Initializes the Match data structure.
bool output_triggers_assignment(Output *output, struct Workspace_Assignment *assignment)
Returns true if the first output assigned to a workspace with the given workspace assignment is the s...
Output * get_output_next(direction_t direction, Output *current, output_close_far_t close_far)
Gets the output which is the next one in the given direction.
Output * get_output_from_rect(Rect rect)
Returns the active output which contains the midpoint of the given rect.
Stores a rectangle, for example the size of a window, the child window etc.
Output * get_output_for_con(Con *con)
Returns the output for the given con.
static Output * root_output
Output * get_output_containing(unsigned int x, unsigned int y)
Returns the active (!) output which contains the coordinates x, y or NULL if there is no output which...
char * output_primary_name(Output *output)
Retrieves the primary name of an output.
Con * con
Pointer to the Con which represents this output.
char * sstrdup(const char *str)
Safe-wrapper around strdup which exits if malloc returns NULL (meaning that there is no more memory a...
void randr_query_outputs(void)
(Re-)queries the outputs via RandR and stores them in the list of outputs.
#define SLIST_FOREACH(var, head, field)
void output_init_con(Output *output)
Initializes a CT_OUTPUT Con (searches existing ones from inplace restart before) to use for the given...
#define TAILQ_NEXT(elm, field)
struct ws_assignments_head ws_assignments
void con_detach(Con *con)
Detaches the given container from its current parent.
void ewmh_update_desktop_properties(void)
Updates all the EWMH desktop properties.
Output * output_containing_rect(Rect rect)
In output_containing_rect, we check if any active output contains part of the container.
bool changed
Internal flags, necessary for querying RandR screens (happens in two stages)
#define TAILQ_EMPTY(head)
#define SLIST_FIRST(head)
Output * get_output_next_wrap(direction_t direction, Output *current)
Like get_output_next with close_far == CLOSEST_OUTPUT, but wraps.
#define SLIST_REMOVE_HEAD(head, field)
Output * get_first_output(void)
Returns the first output which is active.
Rect rect
x, y, width, height
static void fallback_to_root_output(void)
void workspace_show(Con *workspace)
Switches to the given workspace.
Output * get_output_with_dimensions(Rect rect)
Returns the active output which spans exactly the area specified by rect or NULL if there is no outpu...
int num
the workspace number, if this Con is of type CT_WORKSPACE and the workspace is not a named workspace ...
#define TAILQ_INSERT_TAIL(head, elm, field)
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, xcb_randr_get_screen_resources_current_reply_t *res)
void tree_render(void)
Renders the tree, that is rendering all outputs using render_con() and pushing the changes to X11 usi...
void * scalloc(size_t num, size_t size)
Safe-wrapper around calloc which exits if malloc returns NULL (meaning that there is no more memory a...
A "match" is a data structure which acts like a mask or expression to match certain windows or not.
struct all_cons_head all_cons
#define SLIST_INSERT_HEAD(head, elm, field)
int con_num_children(Con *con)
Returns the number of children of this container.
void randr_init(int *event_base, const bool disable_randr15)
We have just established a connection to the X server and need the initial XRandR information to setu...
#define GREP_FIRST(dest, head, condition)
static bool randr_query_outputs_15(void)
bool active
Whether the output is currently active (has a CRTC attached with a valid mode)
#define TAILQ_HEAD_INITIALIZER(head)
bool con_is_internal(Con *con)
Returns true if the container is internal, such as __i3_scratch.
Con * get_assigned_output(const char *name, long parsed_num)
Returns the first output that is assigned to a workspace specified by the given name or number.
bool tree_close_internal(Con *con, kill_window_t kill_window, bool dont_kill_parent)
Closes the given container including all children.
static bool has_randr_1_5
An Output is a physical output on your graphics driver.
xcb_screen_t * root_screen
xcb_randr_get_output_primary_reply_t * primary
static void move_content(Con *con)
#define SLIST_EMPTY(head)
xcb_randr_output_t id
Output id, so that we can requery the output directly later.
struct outputs_head outputs
void con_attach(Con *con, Con *parent, bool ignore_focus)
Attaches the given container to the given parent.
Con * con_for_window(Con *con, i3Window *window, Match **store_match)
Returns the first container below 'con' which wants to swallow this window TODO: priority.
static Output * get_output_by_id(xcb_randr_output_t id)
void workspace_move_to_output(Con *ws, Output *output)
Move the given workspace to the specified output.
static void output_change_mode(xcb_connection_t *conn, Output *output)
void workspace_show_by_name(const char *num)
Looks up the workspace by name and switches to it.
void floating_fix_coordinates(Con *con, Rect *old_rect, Rect *new_rect)
Fixes the coordinates of the floating window whenever the window gets reassigned to a different outpu...
void x_set_name(Con *con, const char *name)
Sets the WM_NAME property (so, no UTF8, but used only for debugging anyways) of the given name.
int default_orientation
Default orientation for new containers.
#define TAILQ_INSERT_HEAD(head, elm, field)
Con * create_workspace_on_output(Output *output, Con *content)
Returns a pointer to a new workspace in the given output.
static bool any_randr_output_active(void)
xcb_connection_t * conn
XCB connection and root screen.
void init_ws_for_output(Output *output)
Initializes at least one workspace for this output, trying the following steps until there is at leas...
void con_focus(Con *con)
Sets input focus to the given container.
#define TAILQ_FOREACH(var, head, field)
Output * get_output_by_name(const char *name, const bool require_active)
Returns the output with the given name or NULL.
Stores which workspace (by name or number) goes to which output.
A 'Con' represents everything from the X11 root window down to a single X11 window.
Con * con_get_workspace(Con *con)
Gets the workspace container this node is on.
fullscreen_mode_t fullscreen_mode
int sasprintf(char **strp, const char *fmt,...)
Safe-wrapper around asprintf which exits if it returns -1 (meaning that there is no more memory avail...
Con * output_get_content(Con *output)
Returns the output container below the given output container.
void con_fix_percent(Con *con)
Updates the percent attribute of the children of the given container.
#define TAILQ_REMOVE(head, elm, field)