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) {
73 Output *output, *result = NULL;
90 die(
"No usable outputs available.\n");
118 DLOG(
"comparing x=%d y=%d with x=%d and y=%d width %d height %d\n",
135 unsigned int mid_x = rect.
x + rect.
width / 2;
136 unsigned int mid_y = rect.
y + rect.
height / 2;
152 DLOG(
"comparing x=%d y=%d %dx%d with x=%d and y=%d %dx%d\n",
172 int lx = rect.
x, uy = rect.
y;
179 int lx_o = (int)output->
rect.
x, uy_o = (
int)output->
rect.
y;
181 DLOG(
"comparing x=%d y=%d with x=%d and y=%d width %d height %d\n",
183 int left =
max(lx, lx_o);
184 int right =
min(rx, rx_o);
185 int bottom =
min(by, by_o);
186 int top =
max(uy, uy_o);
187 if (left < right && bottom > top) {
188 long area = (right - left) * (bottom - top);
189 if (area > max_area) {
214 else if (direction ==
D_LEFT)
216 else if (direction ==
D_DOWN)
248 other = &(output->
rect);
250 if ((direction ==
D_RIGHT && other->x > cur->
x) ||
251 (direction ==
D_LEFT && other->x < cur->
x)) {
254 if ((other->y + other->height) <= cur->
y ||
255 (cur->
y + cur->
height) <= other->y)
257 }
else if ((direction ==
D_DOWN && other->y > cur->
y) ||
258 (direction ==
D_UP && other->y < cur->
y)) {
261 if ((other->x + other->width) <= cur->
x ||
262 (cur->
x + cur->
width) <= other->x)
276 if ((direction ==
D_RIGHT && other->x < best->
rect.
x) ||
277 (direction ==
D_LEFT && other->x > best->
rect.
x) ||
278 (direction ==
D_DOWN && other->y < best->
rect.
y) ||
279 (direction ==
D_UP && other->y > best->
rect.
y)) {
286 if ((direction ==
D_RIGHT && other->x > best->
rect.
x) ||
287 (direction ==
D_LEFT && other->x < best->
rect.
x) ||
288 (direction ==
D_DOWN && other->y > best->
rect.
y) ||
289 (direction ==
D_UP && other->y < best->
rect.
y)) {
327 Con *con = NULL, *current;
340 DLOG(
"Using existing con %p / %s\n", con, con->
name);
348 con->
type = CT_OUTPUT;
361 DLOG(
"Not adding workspace, this was a reused con\n");
365 DLOG(
"Changing layout, adding top/bottom dockarea\n");
367 topdock->
type = CT_DOCKAREA;
372 match->
dock = M_DOCK_TOP;
387 DLOG(
"adding main content container\n");
389 content->
type = CT_CON;
401 bottomdock->
type = CT_DOCKAREA;
406 match->
dock = M_DOCK_BOTTOM;
445 if (workspace == NULL)
451 if (workspace_out ==
output->con) {
452 LOG(
"Workspace \"%s\" assigned to output \"%s\", but it is already " 453 "there. Do you have two assignment directives for the same " 454 "workspace in your configuration file?\n",
459 DLOG(
"Moving workspace \"%s\" from output \"%s\" to \"%s\" due to assignment\n",
490 LOG(
"Initializing first assigned workspace \"%s\" for output \"%s\"\n",
497 DLOG(
"Now adding a workspace\n");
501 if (previous_focus) {
518 DLOG(
"Output mode changed, updating rect\n");
519 assert(
output->con != NULL);
522 Con *content, *workspace, *child;
546 DLOG(
"Setting workspace [%d,%s]'s layout to %d.\n", workspace->
num, workspace->
name, workspace->
layout);
550 DLOG(
"Setting child [%d,%s]'s layout to %d.\n", child->
num, child->
name, child->
layout);
561 #if XCB_RANDR_MINOR_VERSION < 5 570 DLOG(
"Querying outputs using RandR 1.5\n");
571 xcb_generic_error_t *err;
572 xcb_randr_get_monitors_reply_t *monitors =
573 xcb_randr_get_monitors_reply(
576 ELOG(
"Could not get RandR monitors: X11 error code %d\n", err->error_code);
587 output->to_be_disabled =
true;
591 DLOG(
"%d RandR monitors found (timestamp %d)\n",
592 xcb_randr_get_monitors_monitors_length(monitors),
593 monitors->timestamp);
595 xcb_randr_monitor_info_iterator_t iter;
596 for (iter = xcb_randr_get_monitors_monitors_iterator(monitors);
598 xcb_randr_monitor_info_next(&iter)) {
599 const xcb_randr_monitor_info_t *monitor_info = iter.data;
600 xcb_get_atom_name_reply_t *atom_reply =
601 xcb_get_atom_name_reply(
602 conn, xcb_get_atom_name(
conn, monitor_info->name), &err);
604 ELOG(
"Could not get RandR monitor name: X11 error code %d\n", err->error_code);
610 xcb_get_atom_name_name_length(atom_reply),
611 xcb_get_atom_name_name(atom_reply));
621 xcb_randr_output_t *randr_outputs = xcb_randr_monitor_info_outputs(monitor_info);
622 int randr_output_len = xcb_randr_monitor_info_outputs_length(monitor_info);
623 for (
int i = 0; i < randr_output_len; i++) {
624 xcb_randr_output_t randr_output = randr_outputs[i];
626 xcb_randr_get_output_info_reply_t *info =
627 xcb_randr_get_output_info_reply(
conn,
628 xcb_randr_get_output_info(
conn, randr_output, monitors->timestamp),
631 if (info != NULL && info->crtc != XCB_NONE) {
634 xcb_randr_get_output_info_name_length(info),
635 xcb_randr_get_output_info_name(info));
637 if (strcmp(
name, oname) != 0) {
653 if (monitor_info->primary) {
662 new->to_be_disabled =
false;
664 new->primary = monitor_info->primary;
672 DLOG(
"name %s, x %d, y %d, width %d px, height %d px, width %d mm, height %d mm, primary %d, automatic %d\n",
674 monitor_info->x, monitor_info->y, monitor_info->width, monitor_info->height,
675 monitor_info->width_in_millimeters, monitor_info->height_in_millimeters,
676 monitor_info->primary, monitor_info->automatic);
693 xcb_randr_get_output_info_reply_t *output,
695 xcb_randr_get_screen_resources_current_reply_t *res) {
697 xcb_randr_get_crtc_info_reply_t *crtc;
700 bool existing = (
new != NULL);
715 xcb_randr_get_output_info_name_length(output),
716 xcb_randr_get_output_info_name(output));
724 if (output->crtc == XCB_NONE) {
730 }
else if (new->active)
731 new->to_be_disabled =
true;
735 xcb_randr_get_crtc_info_cookie_t icookie;
736 icookie = xcb_randr_get_crtc_info(
conn, output->crtc, cts);
737 if ((crtc = xcb_randr_get_crtc_info_reply(
conn, icookie, NULL)) == NULL) {
738 DLOG(
"Skipping output %s: could not get CRTC (%p)\n",
749 new->active = (
new->rect.width != 0 &&
new->rect.height != 0);
751 DLOG(
"width/height 0/0, disabling output\n");
755 DLOG(
"mode: %dx%d+%d+%d\n", new->rect.width, new->rect.height,
756 new->rect.x, new->rect.y);
761 if (!updated || !existing) {
779 DLOG(
"Querying outputs using RandR ≤ 1.4\n");
782 xcb_randr_get_screen_resources_current_cookie_t rcookie;
783 rcookie = xcb_randr_get_screen_resources_current(
conn,
root);
784 xcb_randr_get_output_primary_cookie_t pcookie;
785 pcookie = xcb_randr_get_output_primary(
conn,
root);
787 if ((
primary = xcb_randr_get_output_primary_reply(
conn, pcookie, NULL)) == NULL)
788 ELOG(
"Could not get RandR primary output\n");
792 xcb_randr_get_screen_resources_current_reply_t *res =
793 xcb_randr_get_screen_resources_current_reply(
conn, rcookie, NULL);
795 ELOG(
"Could not query screen resources.\n");
801 const xcb_timestamp_t cts = res->config_timestamp;
803 const int len = xcb_randr_get_screen_resources_current_outputs_length(res);
806 xcb_randr_output_t *randr_outputs = xcb_randr_get_screen_resources_current_outputs(res);
809 xcb_randr_get_output_info_cookie_t ocookie[len];
810 for (
int i = 0; i < len; i++)
811 ocookie[i] = xcb_randr_get_output_info(
conn, randr_outputs[i], cts);
814 for (
int i = 0; i < len; i++) {
815 xcb_randr_get_output_info_reply_t *output;
817 if ((output = xcb_randr_get_output_info_reply(
conn, ocookie[i], NULL)) == NULL)
842 DLOG(
"Active RandR output found. Disabling root output.\n");
847 DLOG(
"No active RandR output found. Enabling root output.\n");
856 DLOG(
"output %p / %s, position (%d, %d), checking for clones\n",
869 DLOG(
"output %p has the same position, his mode = %d x %d\n",
884 DLOG(
"new output mode %d x %d, other mode %d x %d\n",
895 if (output->
active && output->
con == NULL) {
960 if (output->
con != NULL) {
973 DLOG(
"Getting rid of current = %p / %s (empty, unfocused)\n", current, current->
name);
977 DLOG(
"Detaching current = %p / %s\n", current, current->
name);
979 DLOG(
"Re-attaching current = %p / %s\n", current, current->
name);
981 DLOG(
"Fixing the coordinates of floating containers\n");
990 DLOG(
"now focusing next = %p\n", next);
998 if (child->
type != CT_DOCKAREA)
1000 DLOG(
"Handling dock con %p\n", child);
1007 DLOG(
"Moving dock client %p to nc %p\n", dock, nc);
1009 DLOG(
"Re-attaching\n");
1015 DLOG(
"destroying disappearing con %p\n", output->
con);
1020 DLOG(
"Done. Should be fine now\n");
1039 const xcb_query_extension_reply_t *extreply;
1044 extreply = xcb_get_extension_data(
conn, &xcb_randr_id);
1045 if (!extreply->present) {
1046 DLOG(
"RandR is not present, activating root output.\n");
1051 xcb_generic_error_t *err;
1052 xcb_randr_query_version_reply_t *randr_version =
1053 xcb_randr_query_version_reply(
1054 conn, xcb_randr_query_version(
conn, XCB_RANDR_MAJOR_VERSION, XCB_RANDR_MINOR_VERSION), &err);
1056 ELOG(
"Could not query RandR version: X11 error code %d\n", err->error_code);
1063 (randr_version->minor_version >= 5) &&
1066 free(randr_version);
1070 if (event_base != NULL)
1071 *event_base = extreply->first_event;
1074 XCB_RANDR_NOTIFY_MASK_SCREEN_CHANGE |
1075 XCB_RANDR_NOTIFY_MASK_OUTPUT_CHANGE |
1076 XCB_RANDR_NOTIFY_MASK_CRTC_CHANGE |
1077 XCB_RANDR_NOTIFY_MASK_OUTPUT_PROPERTY);
Con * con
Pointer to the Con which represents this output.
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.
#define TAILQ_HEAD_INITIALIZER(head)
void workspace_show_by_name(const char *num)
Looks up the workspace by name and switches to it.
#define TAILQ_FOREACH(var, head, field)
static void randr_query_outputs_14(void)
static Output * get_output_by_id(xcb_randr_output_t id)
void randr_query_outputs(void)
Initializes the specified output, assigning the specified workspace to it.
xcb_screen_t * root_screen
struct outputs_head outputs
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.
Stores which workspace (by name or number) goes to which output and its gaps config.
bool changed
Internal flags, necessary for querying RandR screens (happens in two stages)
static void output_change_mode(xcb_connection_t *conn, Output *output)
static bool any_randr_output_active(void)
#define SLIST_FOREACH(var, head, field)
An Output is a physical output on your graphics driver.
Output * get_first_output(void)
Returns the first output which is active.
Rect rect
x, y, width, height
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...
xcb_randr_get_output_primary_reply_t * primary
void con_focus(Con *con)
Sets input focus to 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 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...
Output * output_containing_rect(Rect rect)
In output_containing_rect, we check if any active output contains part of the container.
void workspace_move_to_output(Con *ws, Output *output)
Move the given workspace to the specified output.
void randr_disable_output(Output *output)
Disables the output and moves its content.
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)
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.
#define SLIST_REMOVE_HEAD(head, field)
int default_orientation
Default orientation for new containers.
#define GREP_FIRST(dest, head, condition)
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.
bool active
Whether the output is currently active (has a CRTC attached with a valid mode)
void tree_render(void)
Renders the tree, that is rendering all outputs using render_con() and pushing the changes to X11 usi...
char * sstrdup(const char *str)
Safe-wrapper around strdup which exits if malloc returns NULL (meaning that there is no more memory a...
static Output * root_output
names_head
List of names for the output.
struct ws_assignments_head ws_assignments
Con * create_workspace_on_output(Output *output, Con *content)
Returns a pointer to a new workspace in the given output.
xcb_connection_t * conn
XCB connection and root screen.
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...
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 TAILQ_EMPTY(head)
static bool has_randr_1_5
static void fallback_to_root_output(void)
A 'Con' represents everything from the X11 root window down to a single X11 window.
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_INSERT_HEAD(head, elm, field)
#define SLIST_INSERT_HEAD(head, elm, field)
static bool randr_query_outputs_15(void)
void output_init_con(Output *output)
Initializes a CT_OUTPUT Con (searches existing ones from inplace restart before) to use for the given...
void match_init(Match *match)
Initializes the Match data structure.
#define TAILQ_NEXT(elm, field)
Output * create_root_output(xcb_connection_t *conn)
Creates an output covering the root window.
enum Match::@17 insert_where
Output * get_output_from_rect(Rect rect)
Returns the active output which contains the midpoint of the given rect.
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 * con_for_window(Con *con, i3Window *window, Match **store_match)
Returns the first container below 'con' which wants to swallow this window TODO: priority.
char * output_primary_name(Output *output)
Retrieves the primary name of an output.
bool tree_close_internal(Con *con, kill_window_t kill_window, bool dont_kill_parent)
Closes the given container including all children.
void workspace_show(Con *workspace)
Switches to the given workspace.
Output * get_output_by_name(const char *name, const bool require_active)
Returns the output with the given name or NULL.
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...
Con * con_new(Con *parent, i3Window *window)
A wrapper for con_new_skeleton, to retain the old con_new behaviour.
#define TAILQ_INSERT_TAIL(head, elm, field)
fullscreen_mode_t fullscreen_mode
#define SLIST_EMPTY(head)
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...
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...
void con_attach(Con *con, Con *parent, bool ignore_focus)
Attaches the given container to the given parent.
xcb_randr_output_t id
Output id, so that we can requery the output directly later.
Stores a rectangle, for example the size of a window, the child window etc.
int num
the workspace number, if this Con is of type CT_WORKSPACE and the workspace is not a named workspace ...
#define TAILQ_FIRST(head)
#define SLIST_FIRST(head)
#define TAILQ_REMOVE(head, elm, field)
void init_ws_for_output(Output *output)
Initializes at least one workspace for this output, trying the following steps until there is at leas...
Output * get_output_next_wrap(direction_t direction, Output *current)
Like get_output_next with close_far == CLOSEST_OUTPUT, but wraps.