15 #include <xcb/randr.h> 18 xcb_randr_get_output_primary_reply_t *
primary;
49 bool get_primary = (strcasecmp(
"primary", name) == 0);
51 if (output->
primary && get_primary) {
54 if (require_active && !output->
active) {
79 die(
"No usable outputs available.\n");
107 DLOG(
"comparing x=%d y=%d with x=%d and y=%d width %d height %d\n",
124 unsigned int mid_x = rect.
x + rect.
width / 2;
125 unsigned int mid_y = rect.
y + rect.
height / 2;
141 DLOG(
"comparing x=%d y=%d %dx%d with x=%d and y=%d %dx%d\n",
161 int lx = rect.
x, uy = rect.
y;
168 int lx_o = (int)output->
rect.
x, uy_o = (
int)output->
rect.
y;
170 DLOG(
"comparing x=%d y=%d with x=%d and y=%d width %d height %d\n",
172 int left =
max(lx, lx_o);
173 int right =
min(rx, rx_o);
174 int bottom =
min(by, by_o);
175 int top =
max(uy, uy_o);
176 if (left < right && bottom > top) {
177 long area = (right - left) * (bottom - top);
178 if (area > max_area) {
203 else if (direction ==
D_LEFT)
205 else if (direction ==
D_DOWN)
237 other = &(output->
rect);
239 if ((direction ==
D_RIGHT && other->x > cur->
x) ||
240 (direction ==
D_LEFT && other->x < cur->
x)) {
243 if ((other->y + other->height) <= cur->
y ||
244 (cur->
y + cur->
height) <= other->y)
246 }
else if ((direction ==
D_DOWN && other->y > cur->
y) ||
247 (direction ==
D_UP && other->y < cur->
y)) {
250 if ((other->x + other->width) <= cur->
x ||
251 (cur->
x + cur->
width) <= other->x)
265 if ((direction ==
D_RIGHT && other->x < best->
rect.
x) ||
266 (direction ==
D_LEFT && other->x > best->
rect.
x) ||
267 (direction ==
D_DOWN && other->y < best->
rect.
y) ||
268 (direction ==
D_UP && other->y > best->
rect.
y)) {
275 if ((direction ==
D_RIGHT && other->x > best->
rect.
x) ||
276 (direction ==
D_LEFT && other->x < best->
rect.
x) ||
277 (direction ==
D_DOWN && other->y > best->
rect.
y) ||
278 (direction ==
D_UP && other->y < best->
rect.
y)) {
316 Con *con = NULL, *current;
329 DLOG(
"Using existing con %p / %s\n", con, con->
name);
337 con->
type = CT_OUTPUT;
350 DLOG(
"Not adding workspace, this was a reused con\n");
354 DLOG(
"Changing layout, adding top/bottom dockarea\n");
356 topdock->
type = CT_DOCKAREA;
361 match->
dock = M_DOCK_TOP;
376 DLOG(
"adding main content container\n");
378 content->
type = CT_CON;
390 bottomdock->
type = CT_DOCKAREA;
395 match->
dock = M_DOCK_BOTTOM;
434 if (workspace == NULL)
440 if (workspace_out ==
output->con) {
441 LOG(
"Workspace \"%s\" assigned to output \"%s\", but it is already " 442 "there. Do you have two assignment directives for the same " 443 "workspace in your configuration file?\n",
448 DLOG(
"Moving workspace \"%s\" from output \"%s\" to \"%s\" due to assignment\n",
479 LOG(
"Initializing first assigned workspace \"%s\" for output \"%s\"\n",
486 DLOG(
"Now adding a workspace\n");
490 if (previous_focus) {
507 DLOG(
"Output mode changed, updating rect\n");
508 assert(
output->con != NULL);
511 Con *content, *workspace, *child;
535 DLOG(
"Setting workspace [%d,%s]'s layout to %d.\n", workspace->
num, workspace->
name, workspace->
layout);
539 DLOG(
"Setting child [%d,%s]'s layout to %d.\n", child->
num, child->
name, child->
layout);
550 #if XCB_RANDR_MINOR_VERSION < 5 559 DLOG(
"Querying outputs using RandR 1.5\n");
560 xcb_generic_error_t *err;
561 xcb_randr_get_monitors_reply_t *monitors =
562 xcb_randr_get_monitors_reply(
565 ELOG(
"Could not get RandR monitors: X11 error code %d\n", err->error_code);
576 output->to_be_disabled =
true;
580 DLOG(
"%d RandR monitors found (timestamp %d)\n",
581 xcb_randr_get_monitors_monitors_length(monitors),
582 monitors->timestamp);
584 xcb_randr_monitor_info_iterator_t iter;
585 for (iter = xcb_randr_get_monitors_monitors_iterator(monitors);
587 xcb_randr_monitor_info_next(&iter)) {
588 const xcb_randr_monitor_info_t *monitor_info = iter.data;
589 xcb_get_atom_name_reply_t *atom_reply =
590 xcb_get_atom_name_reply(
591 conn, xcb_get_atom_name(
conn, monitor_info->name), &err);
593 ELOG(
"Could not get RandR monitor name: X11 error code %d\n", err->error_code);
599 xcb_get_atom_name_name_length(atom_reply),
600 xcb_get_atom_name_name(atom_reply));
610 xcb_randr_output_t *randr_outputs = xcb_randr_monitor_info_outputs(monitor_info);
611 int randr_output_len = xcb_randr_monitor_info_outputs_length(monitor_info);
612 for (
int i = 0; i < randr_output_len; i++) {
613 xcb_randr_output_t randr_output = randr_outputs[i];
615 xcb_randr_get_output_info_reply_t *info =
616 xcb_randr_get_output_info_reply(
conn,
617 xcb_randr_get_output_info(
conn, randr_output, monitors->timestamp),
620 if (info != NULL && info->crtc != XCB_NONE) {
623 xcb_randr_get_output_info_name_length(info),
624 xcb_randr_get_output_info_name(info));
626 if (strcmp(
name, oname) != 0) {
642 if (monitor_info->primary) {
651 new->to_be_disabled =
false;
653 new->primary = monitor_info->primary;
661 DLOG(
"name %s, x %d, y %d, width %d px, height %d px, width %d mm, height %d mm, primary %d, automatic %d\n",
663 monitor_info->x, monitor_info->y, monitor_info->width, monitor_info->height,
664 monitor_info->width_in_millimeters, monitor_info->height_in_millimeters,
665 monitor_info->primary, monitor_info->automatic);
682 xcb_randr_get_output_info_reply_t *output,
684 xcb_randr_get_screen_resources_current_reply_t *res) {
686 xcb_randr_get_crtc_info_reply_t *crtc;
689 bool existing = (
new != NULL);
704 xcb_randr_get_output_info_name_length(output),
705 xcb_randr_get_output_info_name(output));
713 if (output->crtc == XCB_NONE) {
719 }
else if (new->active)
720 new->to_be_disabled =
true;
724 xcb_randr_get_crtc_info_cookie_t icookie;
725 icookie = xcb_randr_get_crtc_info(
conn, output->crtc, cts);
726 if ((crtc = xcb_randr_get_crtc_info_reply(
conn, icookie, NULL)) == NULL) {
727 DLOG(
"Skipping output %s: could not get CRTC (%p)\n",
738 new->active = (
new->rect.width != 0 &&
new->rect.height != 0);
740 DLOG(
"width/height 0/0, disabling output\n");
744 DLOG(
"mode: %dx%d+%d+%d\n", new->rect.width, new->rect.height,
745 new->rect.x, new->rect.y);
750 if (!updated || !existing) {
768 DLOG(
"Querying outputs using RandR ≤ 1.4\n");
771 xcb_randr_get_screen_resources_current_cookie_t rcookie;
772 rcookie = xcb_randr_get_screen_resources_current(
conn,
root);
773 xcb_randr_get_output_primary_cookie_t pcookie;
774 pcookie = xcb_randr_get_output_primary(
conn,
root);
776 if ((
primary = xcb_randr_get_output_primary_reply(
conn, pcookie, NULL)) == NULL)
777 ELOG(
"Could not get RandR primary output\n");
781 xcb_randr_get_screen_resources_current_reply_t *res =
782 xcb_randr_get_screen_resources_current_reply(
conn, rcookie, NULL);
784 ELOG(
"Could not query screen resources.\n");
790 const xcb_timestamp_t cts = res->config_timestamp;
792 const int len = xcb_randr_get_screen_resources_current_outputs_length(res);
795 xcb_randr_output_t *randr_outputs = xcb_randr_get_screen_resources_current_outputs(res);
798 xcb_randr_get_output_info_cookie_t ocookie[len];
799 for (
int i = 0; i < len; i++)
800 ocookie[i] = xcb_randr_get_output_info(
conn, randr_outputs[i], cts);
803 for (
int i = 0; i < len; i++) {
804 xcb_randr_get_output_info_reply_t *output;
806 if ((output = xcb_randr_get_output_info_reply(
conn, ocookie[i], NULL)) == NULL)
831 DLOG(
"Active RandR output found. Disabling root output.\n");
836 DLOG(
"No active RandR output found. Enabling root output.\n");
845 DLOG(
"output %p / %s, position (%d, %d), checking for clones\n",
858 DLOG(
"output %p has the same position, his mode = %d x %d\n",
873 DLOG(
"new output mode %d x %d, other mode %d x %d\n",
884 if (output->
active && output->
con == NULL) {
949 if (output->
con != NULL) {
962 DLOG(
"Getting rid of current = %p / %s (empty, unfocused)\n", current, current->
name);
966 DLOG(
"Detaching current = %p / %s\n", current, current->
name);
968 DLOG(
"Re-attaching current = %p / %s\n", current, current->
name);
970 DLOG(
"Fixing the coordinates of floating containers\n");
979 DLOG(
"now focusing next = %p\n", next);
987 if (child->
type != CT_DOCKAREA)
989 DLOG(
"Handling dock con %p\n", child);
996 DLOG(
"Moving dock client %p to nc %p\n", dock, nc);
998 DLOG(
"Re-attaching\n");
1004 DLOG(
"destroying disappearing con %p\n", output->
con);
1009 DLOG(
"Done. Should be fine now\n");
1028 const xcb_query_extension_reply_t *extreply;
1033 extreply = xcb_get_extension_data(
conn, &xcb_randr_id);
1034 if (!extreply->present) {
1035 DLOG(
"RandR is not present, activating root output.\n");
1040 xcb_generic_error_t *err;
1041 xcb_randr_query_version_reply_t *randr_version =
1042 xcb_randr_query_version_reply(
1043 conn, xcb_randr_query_version(
conn, XCB_RANDR_MAJOR_VERSION, XCB_RANDR_MINOR_VERSION), &err);
1045 ELOG(
"Could not query RandR version: X11 error code %d\n", err->error_code);
1052 (randr_version->minor_version >= 5) &&
1055 free(randr_version);
1059 if (event_base != NULL)
1060 *event_base = extreply->first_event;
1063 XCB_RANDR_NOTIFY_MASK_SCREEN_CHANGE |
1064 XCB_RANDR_NOTIFY_MASK_OUTPUT_CHANGE |
1065 XCB_RANDR_NOTIFY_MASK_CRTC_CHANGE |
1066 XCB_RANDR_NOTIFY_MASK_OUTPUT_PROPERTY);
void con_fix_percent(Con *con)
Updates the percent attribute of the children of the given container.
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.
void con_detach(Con *con)
Detaches the given container from its current parent.
Stores a rectangle, for example the size of a window, the child window etc.
struct outputs_head outputs
#define SLIST_FOREACH(var, head, field)
void workspace_show(Con *workspace)
Switches to the given workspace.
Output * output_containing_rect(Rect rect)
In output_containing_rect, we check if any active output contains part of the container.
Rect rect
x, y, width, height
static bool any_randr_output_active(void)
#define TAILQ_EMPTY(head)
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...
xcb_connection_t * conn
XCB connection and root screen.
static void fallback_to_root_output(void)
void con_focus(Con *con)
Sets input focus to the given container.
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...
#define SLIST_EMPTY(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...
names_head
List of names for the output.
void randr_query_outputs(void)
Initializes the specified output, assigning the specified workspace to it.
xcb_screen_t * root_screen
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...
An Output is a physical output on your graphics driver.
fullscreen_mode_t fullscreen_mode
#define TAILQ_INSERT_HEAD(head, elm, field)
Output * get_output_by_name(const char *name, const bool require_active)
Returns the output with the given name or NULL.
static void output_change_mode(xcb_connection_t *conn, Output *output)
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.
#define TAILQ_REMOVE(head, elm, field)
xcb_randr_output_t id
Output id, so that we can requery the output directly later.
void output_init_con(Output *output)
Initializes a CT_OUTPUT Con (searches existing ones from inplace restart before) to use for the given...
static Output * root_output
Stores which workspace (by name or number) goes to which output and its gaps config.
#define TAILQ_FIRST(head)
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...
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...
Output * get_first_output(void)
Returns the first output which is active.
void workspace_show_by_name(const char *num)
Looks up the workspace by name and switches to it.
Con * con_get_workspace(Con *con)
Gets the workspace container this node is on.
Con * con_get_output(Con *con)
Gets the output container (first container with CT_OUTPUT in hierarchy) this node is on.
int con_num_children(Con *con)
Returns the number of children of this container.
#define SLIST_FIRST(head)
#define TAILQ_NEXT(elm, field)
void tree_render(void)
Renders the tree, that is rendering all outputs using render_con() and pushing the changes to X11 usi...
static bool randr_query_outputs_15(void)
static bool has_randr_1_5
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.
char * sstrdup(const char *str)
Safe-wrapper around strdup which exits if malloc returns NULL (meaning that there is no more memory a...
bool changed
Internal flags, necessary for querying RandR screens (happens in two stages)
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...
Con * output_get_content(Con *output)
Returns the output container below the given output container.
A "match" is a data structure which acts like a mask or expression to match certain windows or not.
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)
enum Match::@17 insert_where
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.
Output * get_output_from_rect(Rect rect)
Returns the active output which contains the midpoint of the given rect.
void workspace_move_to_output(Con *ws, Output *output)
Move the given workspace to the specified output.
Con * get_existing_workspace_by_name(const char *name)
Returns the workspace with the given name or NULL if such a workspace does not exist.
bool tree_close_internal(Con *con, kill_window_t kill_window, bool dont_kill_parent)
Closes the given container including all children.
Con * con_new(Con *parent, i3Window *window)
A wrapper for con_new_skeleton, to retain the old con_new behaviour.
#define SLIST_REMOVE_HEAD(head, field)
int default_orientation
Default orientation for new containers.
#define GREP_FIRST(dest, head, condition)
Output * get_output_next_wrap(direction_t direction, Output *current)
Like get_output_next with close_far == CLOSEST_OUTPUT, but wraps.
bool active
Whether the output is currently active (has a CRTC attached with a valid mode)
struct ws_assignments_head ws_assignments
#define SLIST_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.
int num
the workspace number, if this Con is of type CT_WORKSPACE and the workspace is not a named workspace ...
void con_attach(Con *con, Con *parent, bool ignore_focus)
Attaches the given container to the given parent.
A 'Con' represents everything from the X11 root window down to a single X11 window.
void randr_disable_output(Output *output)
Disables the output and moves its content.
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...
void match_init(Match *match)
Initializes the Match data structure.
char * output_primary_name(Output *output)
Retrieves the primary name of an output.
static void randr_query_outputs_14(void)
Con * con
Pointer to the Con which represents this output.
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.
#define TAILQ_HEAD_INITIALIZER(head)
#define TAILQ_INSERT_TAIL(head, elm, field)
#define TAILQ_FOREACH(var, head, field)
xcb_randr_get_output_primary_reply_t * primary