16 #include <xcb/randr.h> 19 xcb_randr_get_output_primary_reply_t *
primary;
37 if (output->
id ==
id) {
51 const bool get_primary = (strcasecmp(
"primary", name) == 0);
52 const bool get_non_primary = (strcasecmp(
"nonprimary", name) == 0);
56 if (require_active && !output->
active) {
59 if (output->
primary && get_primary) {
62 if (!output->
primary && get_non_primary) {
81 Output *output, *result = NULL;
98 die(
"No usable outputs available.\n");
126 DLOG(
"comparing x=%d y=%d with x=%d and y=%d width %d height %d\n",
143 unsigned int mid_x = rect.
x + rect.
width / 2;
144 unsigned int mid_y = rect.
y + rect.
height / 2;
160 DLOG(
"comparing x=%d y=%d %dx%d with x=%d and y=%d %dx%d\n",
180 int lx = rect.
x, uy = rect.
y;
187 int lx_o = (int)output->
rect.
x, uy_o = (
int)output->
rect.
y;
189 DLOG(
"comparing x=%d y=%d with x=%d and y=%d width %d height %d\n",
191 int left =
max(lx, lx_o);
192 int right =
min(rx, rx_o);
193 int bottom =
min(by, by_o);
194 int top =
max(uy, uy_o);
195 if (left < right && bottom > top) {
196 long area = (right - left) * (bottom - top);
197 if (area > max_area) {
222 else if (direction ==
D_LEFT)
224 else if (direction ==
D_DOWN)
256 other = &(output->
rect);
258 if ((direction ==
D_RIGHT && other->x > cur->
x) ||
259 (direction ==
D_LEFT && other->x < cur->
x)) {
262 if ((other->y + other->height) <= cur->
y ||
263 (cur->
y + cur->
height) <= other->y)
265 }
else if ((direction ==
D_DOWN && other->y > cur->
y) ||
266 (direction ==
D_UP && other->y < cur->
y)) {
269 if ((other->x + other->width) <= cur->
x ||
270 (cur->
x + cur->
width) <= other->x)
284 if ((direction ==
D_RIGHT && other->x < best->
rect.
x) ||
285 (direction ==
D_LEFT && other->x > best->
rect.
x) ||
286 (direction ==
D_DOWN && other->y < best->
rect.
y) ||
287 (direction ==
D_UP && other->y > best->
rect.
y)) {
294 if ((direction ==
D_RIGHT && other->x > best->
rect.
x) ||
295 (direction ==
D_LEFT && other->x < best->
rect.
x) ||
296 (direction ==
D_DOWN && other->y > best->
rect.
y) ||
297 (direction ==
D_UP && other->y < best->
rect.
y)) {
335 Con *con = NULL, *current;
348 DLOG(
"Using existing con %p / %s\n", con, con->
name);
356 con->
type = CT_OUTPUT;
369 DLOG(
"Not adding workspace, this was a reused con\n");
373 DLOG(
"Changing layout, adding top/bottom dockarea\n");
375 topdock->
type = CT_DOCKAREA;
380 match->
dock = M_DOCK_TOP;
395 DLOG(
"adding main content container\n");
397 content->
type = CT_CON;
409 bottomdock->
type = CT_DOCKAREA;
414 match->
dock = M_DOCK_BOTTOM;
459 if (output->
con != workspace_out) {
463 DLOG(
"Moving workspace \"%s\" from output \"%s\" to \"%s\" due to assignment\n",
497 LOG(
"Initializing first assigned workspace \"%s\" for output \"%s\"\n",
504 DLOG(
"Now adding a workspace\n");
508 if (previous_focus) {
525 DLOG(
"Output mode changed, updating rect\n");
526 assert(
output->con != NULL);
529 Con *content, *workspace, *child;
537 TAILQ_FOREACH (child, &(workspace->floating_head), floating_windows) {
553 DLOG(
"Setting workspace [%d,%s]'s layout to %d.\n", workspace->
num, workspace->
name, workspace->
layout);
554 if ((child =
TAILQ_FIRST(&(workspace->nodes_head)))) {
557 DLOG(
"Setting child [%d,%s]'s layout to %d.\n", child->
num, child->
name, child->
layout);
568 #if XCB_RANDR_MINOR_VERSION < 5 577 DLOG(
"Querying outputs using RandR 1.5\n");
578 xcb_generic_error_t *err;
579 xcb_randr_get_monitors_reply_t *monitors =
580 xcb_randr_get_monitors_reply(
583 ELOG(
"Could not get RandR monitors: X11 error code %d\n", err->error_code);
594 output->to_be_disabled =
true;
598 DLOG(
"%d RandR monitors found (timestamp %d)\n",
599 xcb_randr_get_monitors_monitors_length(monitors),
600 monitors->timestamp);
602 xcb_randr_monitor_info_iterator_t iter;
603 for (iter = xcb_randr_get_monitors_monitors_iterator(monitors);
605 xcb_randr_monitor_info_next(&iter)) {
606 const xcb_randr_monitor_info_t *monitor_info = iter.data;
607 xcb_get_atom_name_reply_t *atom_reply =
608 xcb_get_atom_name_reply(
609 conn, xcb_get_atom_name(
conn, monitor_info->name), &err);
611 ELOG(
"Could not get RandR monitor name: X11 error code %d\n", err->error_code);
617 xcb_get_atom_name_name_length(atom_reply),
618 xcb_get_atom_name_name(atom_reply));
628 xcb_randr_output_t *randr_outputs = xcb_randr_monitor_info_outputs(monitor_info);
629 int randr_output_len = xcb_randr_monitor_info_outputs_length(monitor_info);
630 for (
int i = 0; i < randr_output_len; i++) {
631 xcb_randr_output_t randr_output = randr_outputs[i];
633 xcb_randr_get_output_info_reply_t *info =
634 xcb_randr_get_output_info_reply(
conn,
635 xcb_randr_get_output_info(
conn, randr_output, monitors->timestamp),
638 if (info != NULL && info->crtc != XCB_NONE) {
641 xcb_randr_get_output_info_name_length(info),
642 xcb_randr_get_output_info_name(info));
644 if (strcmp(
name, oname) != 0) {
660 if (monitor_info->primary) {
669 new->to_be_disabled =
false;
671 new->primary = monitor_info->primary;
678 new->changed = update_x || update_y || update_w || update_h;
680 DLOG(
"name %s, x %d, y %d, width %d px, height %d px, width %d mm, height %d mm, primary %d, automatic %d\n",
682 monitor_info->x, monitor_info->y, monitor_info->width, monitor_info->height,
683 monitor_info->width_in_millimeters, monitor_info->height_in_millimeters,
684 monitor_info->primary, monitor_info->automatic);
701 xcb_randr_get_output_info_reply_t *output,
703 xcb_randr_get_screen_resources_current_reply_t *res) {
705 xcb_randr_get_crtc_info_reply_t *crtc;
708 bool existing = (
new != NULL);
723 xcb_randr_get_output_info_name_length(output),
724 xcb_randr_get_output_info_name(output));
732 if (output->crtc == XCB_NONE) {
738 }
else if (new->active)
739 new->to_be_disabled =
true;
743 xcb_randr_get_crtc_info_cookie_t icookie;
744 icookie = xcb_randr_get_crtc_info(
conn, output->crtc, cts);
745 if ((crtc = xcb_randr_get_crtc_info_reply(
conn, icookie, NULL)) == NULL) {
746 DLOG(
"Skipping output %s: could not get CRTC (%p)\n",
756 const bool updated = update_x || update_y || update_w || update_h;
758 new->active = (
new->rect.width != 0 &&
new->rect.height != 0);
760 DLOG(
"width/height 0/0, disabling output\n");
764 DLOG(
"mode: %dx%d+%d+%d\n", new->rect.width, new->rect.height,
765 new->rect.x, new->rect.y);
770 if (!updated || !existing) {
788 DLOG(
"Querying outputs using RandR ≤ 1.4\n");
791 xcb_randr_get_screen_resources_current_cookie_t rcookie;
792 rcookie = xcb_randr_get_screen_resources_current(
conn,
root);
793 xcb_randr_get_output_primary_cookie_t pcookie;
794 pcookie = xcb_randr_get_output_primary(
conn,
root);
796 if ((
primary = xcb_randr_get_output_primary_reply(
conn, pcookie, NULL)) == NULL)
797 ELOG(
"Could not get RandR primary output\n");
801 xcb_randr_get_screen_resources_current_reply_t *res =
802 xcb_randr_get_screen_resources_current_reply(
conn, rcookie, NULL);
804 ELOG(
"Could not query screen resources.\n");
810 const xcb_timestamp_t cts = res->config_timestamp;
812 const int len = xcb_randr_get_screen_resources_current_outputs_length(res);
815 xcb_randr_output_t *randr_outputs = xcb_randr_get_screen_resources_current_outputs(res);
818 xcb_randr_get_output_info_cookie_t ocookie[len];
819 for (
int i = 0; i < len; i++)
820 ocookie[i] = xcb_randr_get_output_info(
conn, randr_outputs[i], cts);
823 for (
int i = 0; i < len; i++) {
824 xcb_randr_get_output_info_reply_t *output;
826 if ((output = xcb_randr_get_output_info_reply(
conn, ocookie[i], NULL)) == NULL)
857 if (current != next &&
TAILQ_EMPTY(&(current->focus_head))) {
859 DLOG(
"Getting rid of current = %p / %s (empty, unfocused)\n", current, current->
name);
863 DLOG(
"Detaching current = %p / %s\n", current, current->
name);
865 DLOG(
"Re-attaching current = %p / %s\n", current, current->
name);
867 DLOG(
"Fixing the coordinates of floating containers\n");
869 TAILQ_FOREACH (floating_con, &(current->floating_head), floating_windows) {
876 DLOG(
"now focusing next = %p\n", next);
884 if (child->
type != CT_DOCKAREA) {
887 DLOG(
"Handling dock con %p\n", child);
894 DLOG(
"Moving dock client %p to nc %p\n", dock, nc);
896 DLOG(
"Re-attaching\n");
902 DLOG(
"Destroying disappearing con %p\n", con);
921 DLOG(
"Active RandR output found. Disabling root output.\n");
926 DLOG(
"No active RandR output found. Enabling root output.\n");
935 DLOG(
"output %p / %s, position (%d, %d), checking for clones\n",
948 DLOG(
"output %p has the same position, its mode = %d x %d\n",
955 if (update_w || update_h) {
965 DLOG(
"new output mode %d x %d, other mode %d x %d\n",
976 if (output->
active && output->
con == NULL) {
992 DLOG(
"No output %s found, moving its old content to first output\n", con->
name);
1050 if (output->
con != NULL) {
1073 const xcb_query_extension_reply_t *extreply;
1078 extreply = xcb_get_extension_data(
conn, &xcb_randr_id);
1079 if (!extreply->present) {
1080 DLOG(
"RandR is not present, activating root output.\n");
1085 xcb_generic_error_t *err;
1086 xcb_randr_query_version_reply_t *randr_version =
1087 xcb_randr_query_version_reply(
1088 conn, xcb_randr_query_version(
conn, XCB_RANDR_MAJOR_VERSION, XCB_RANDR_MINOR_VERSION), &err);
1090 ELOG(
"Could not query RandR version: X11 error code %d\n", err->error_code);
1097 (randr_version->minor_version >= 5) &&
1100 free(randr_version);
1104 if (event_base != NULL)
1105 *event_base = extreply->first_event;
1108 XCB_RANDR_NOTIFY_MASK_SCREEN_CHANGE |
1109 XCB_RANDR_NOTIFY_MASK_OUTPUT_CHANGE |
1110 XCB_RANDR_NOTIFY_MASK_CRTC_CHANGE |
1111 XCB_RANDR_NOTIFY_MASK_OUTPUT_PROPERTY);
An Output is a physical output on your graphics driver.
void con_fix_percent(Con *con)
Updates the percent attribute of the children of the given container.
void con_detach(Con *con)
Detaches the given container from its current parent.
Output * output_containing_rect(Rect rect)
In output_containing_rect, we check if any active output contains part of the container.
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...
void workspace_show_by_name(const char *num)
Looks up the workspace by name and switches to it.
static bool randr_query_outputs_15(void)
static bool has_randr_1_5
char * output_primary_name(Output *output)
Retrieves the primary name of an output.
xcb_randr_get_output_primary_reply_t * primary
A "match" is a data structure which acts like a mask or expression to match certain windows or not...
#define TAILQ_FIRST(head)
Con * output_get_content(Con *output)
Returns the output container below the given output container.
Con * create_workspace_on_output(Output *output, Con *content)
Returns a pointer to a new workspace in the given output.
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...
bool changed
Internal flags, necessary for querying RandR screens (happens in two stages)
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)
Stores a rectangle, for example the size of a window, the child window etc.
static void move_content(Con *con)
struct all_cons_head all_cons
void con_focus(Con *con)
Sets input focus to the given container.
struct ws_assignments_head ws_assignments
#define SLIST_REMOVE_HEAD(head, field)
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...
static void output_change_mode(xcb_connection_t *conn, Output *output)
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...
bool tree_close_internal(Con *con, kill_window_t kill_window, bool dont_kill_parent)
Closes the given container including all children.
#define GREP_FIRST(dest, head, condition)
void randr_disable_output(Output *output)
Disables the output and moves its content.
#define SLIST_FIRST(head)
void init_ws_for_output(Output *output)
Initializes at least one workspace for this output, trying the following steps until there is at leas...
Stores which workspace (by name or number) goes to which output and its gaps config.
int num
the workspace number, if this Con is of type CT_WORKSPACE and the workspace is not a named workspace ...
int default_orientation
Default orientation for new containers.
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...
#define TAILQ_EMPTY(head)
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...
#define TAILQ_FOREACH(var, head, field)
Con * con_get_workspace(Con *con)
Gets the workspace container this node is on.
Output * get_output_by_name(const char *name, const bool require_active)
Returns the output with the given name or NULL.
int con_num_children(Con *con)
Returns the number of children of this container.
static Output * get_output_by_id(xcb_randr_output_t id)
Output * create_root_output(xcb_connection_t *conn)
Creates an output covering the root window.
void output_init_con(Output *output)
Initializes a CT_OUTPUT Con (searches existing ones from inplace restart before) to use for the given...
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...
#define TAILQ_INSERT_HEAD(head, elm, field)
fullscreen_mode_t fullscreen_mode
#define SLIST_INSERT_HEAD(head, elm, field)
Output * get_output_from_rect(Rect rect)
Returns the active output which contains the midpoint of the given rect.
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...
bool con_is_internal(Con *con)
Returns true if the container is internal, such as __i3_scratch.
#define SLIST_FOREACH(var, head, field)
void workspace_show(Con *workspace)
Switches to the given workspace.
#define TAILQ_NEXT(elm, field)
static bool any_randr_output_active(void)
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 void fallback_to_root_output(void)
xcb_connection_t * conn
XCB connection and root screen.
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...
void tree_render(void)
Renders the tree, that is rendering all outputs using render_con() and pushing the changes to X11 usi...
Con * con_new(Con *parent, i3Window *window)
A wrapper for con_new_skeleton, to retain the old con_new behaviour.
xcb_screen_t * root_screen
#define SLIST_EMPTY(head)
void randr_query_outputs(void)
(Re-)queries the outputs via RandR and stores them in the list of outputs.
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 TAILQ_INSERT_TAIL(head, elm, field)
void ewmh_update_desktop_properties(void)
Updates all the EWMH desktop properties.
char * sstrdup(const char *str)
Safe-wrapper around strdup which exits if malloc returns NULL (meaning that there is no more memory a...
xcb_randr_output_t id
Output id, so that we can requery the output directly later.
A 'Con' represents everything from the X11 root window down to a single X11 window.
static void randr_query_outputs_14(void)
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.
void con_attach(Con *con, Con *parent, bool ignore_focus)
Attaches the given container to the given parent.
#define TAILQ_HEAD_INITIALIZER(head)
static Output * root_output
Output * get_output_next_wrap(direction_t direction, Output *current)
Like get_output_next with close_far == CLOSEST_OUTPUT, but wraps.
Con * con
Pointer to the Con which represents this output.
#define TAILQ_REMOVE(head, elm, field)
Output * get_first_output(void)
Returns the first output which is active.
void workspace_move_to_output(Con *ws, Output *output)
Move the given workspace to the specified output.
Output * get_output_for_con(Con *con)
Returns the output for the given con.
struct outputs_head outputs
enum Match::@15 insert_where
Rect rect
x, y, width, height
void match_init(Match *match)
Initializes the Match data structure.
bool active
Whether the output is currently active (has a CRTC attached with a valid mode)