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) {
59 if (strcasecmp(output_name->
name, name) == 0) {
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",
127 DLOG(
"comparing x=%d y=%d %dx%d with x=%d and y=%d %dx%d\n",
148 int lx = rect.
x, uy = rect.
y;
153 DLOG(
"comparing x=%d y=%d with x=%d and y=%d width %d height %d\n",
179 else if (direction ==
D_LEFT)
181 else if (direction ==
D_DOWN)
213 other = &(output->
rect);
215 if ((direction ==
D_RIGHT && other->x > cur->
x) ||
216 (direction ==
D_LEFT && other->x < cur->
x)) {
219 if ((other->y + other->height) <= cur->
y ||
220 (cur->
y + cur->
height) <= other->y)
222 }
else if ((direction ==
D_DOWN && other->y > cur->
y) ||
223 (direction ==
D_UP && other->y < cur->
y)) {
226 if ((other->x + other->width) <= cur->
x ||
227 (cur->
x + cur->
width) <= other->x)
241 if ((direction ==
D_RIGHT && other->x < best->
rect.
x) ||
242 (direction ==
D_LEFT && other->x > best->
rect.
x) ||
243 (direction ==
D_DOWN && other->y < best->
rect.
y) ||
244 (direction ==
D_UP && other->y > best->
rect.
y)) {
251 if ((direction ==
D_RIGHT && other->x > best->
rect.
x) ||
252 (direction ==
D_LEFT && other->x < best->
rect.
x) ||
253 (direction ==
D_DOWN && other->y > best->
rect.
y) ||
254 (direction ==
D_UP && other->y < best->
rect.
y)) {
279 output_name->
name =
"xroot-0";
292 Con *con = NULL, *current;
305 DLOG(
"Using existing con %p / %s\n", con, con->
name);
313 con->
type = CT_OUTPUT;
326 DLOG(
"Not adding workspace, this was a reused con\n");
330 DLOG(
"Changing layout, adding top/bottom dockarea\n");
332 topdock->
type = CT_DOCKAREA;
337 match->
dock = M_DOCK_TOP;
352 DLOG(
"adding main content container\n");
354 content->
type = CT_CON;
366 bottomdock->
type = CT_DOCKAREA;
371 match->
dock = M_DOCK_BOTTOM;
407 Con *workspace = NULL, *out;
410 !strcasecmp(child->name, assignment->
name));
411 if (workspace == NULL)
417 if (workspace_out == output->
con) {
418 LOG(
"Workspace \"%s\" assigned to output \"%s\", but it is already " 419 "there. Do you have two assignment directives for the same " 420 "workspace in your configuration file?\n",
426 LOG(
"Moving workspace \"%s\" from output \"%s\" to \"%s\" due to assignment\n",
433 Con *previous = NULL;
435 LOG(
"Switching to previously used workspace \"%s\" on output \"%s\"\n",
436 previous->
name, workspace_out->
name);
459 if (visible && previous == NULL) {
460 LOG(
"There is no workspace left on \"%s\", re-initializing\n",
461 workspace_out->
name);
487 LOG(
"Initializing first assigned workspace \"%s\" for output \"%s\"\n",
495 DLOG(
"Now adding a workspace\n");
514 DLOG(
"Output mode changed, updating rect\n");
515 assert(output->
con != NULL);
518 Con *content, *workspace, *child;
542 DLOG(
"Setting workspace [%d,%s]'s layout to %d.\n", workspace->
num, workspace->
name, workspace->
layout);
546 DLOG(
"Setting child [%d,%s]'s layout to %d.\n", child->
num, child->
name, child->
layout);
557 #if XCB_RANDR_MINOR_VERSION < 5 566 DLOG(
"Querying outputs using RandR 1.5\n");
567 xcb_generic_error_t *err;
568 xcb_randr_get_monitors_reply_t *monitors =
569 xcb_randr_get_monitors_reply(
572 ELOG(
"Could not get RandR monitors: X11 error code %d\n", err->error_code);
582 if (output != root_output) {
587 DLOG(
"%d RandR monitors found (timestamp %d)\n",
588 xcb_randr_get_monitors_monitors_length(monitors),
589 monitors->timestamp);
591 xcb_randr_monitor_info_iterator_t iter;
592 for (iter = xcb_randr_get_monitors_monitors_iterator(monitors);
594 xcb_randr_monitor_info_next(&iter)) {
595 const xcb_randr_monitor_info_t *monitor_info = iter.data;
596 xcb_get_atom_name_reply_t *atom_reply =
597 xcb_get_atom_name_reply(
598 conn, xcb_get_atom_name(
conn, monitor_info->name), &err);
600 ELOG(
"Could not get RandR monitor name: X11 error code %d\n", err->error_code);
606 xcb_get_atom_name_name_length(atom_reply),
607 xcb_get_atom_name_name(atom_reply));
617 xcb_randr_output_t *randr_outputs = xcb_randr_monitor_info_outputs(monitor_info);
618 int randr_output_len = xcb_randr_monitor_info_outputs_length(monitor_info);
619 for (
int i = 0; i < randr_output_len; i++) {
620 xcb_randr_output_t randr_output = randr_outputs[i];
622 xcb_randr_get_output_info_reply_t *info =
623 xcb_randr_get_output_info_reply(
conn,
624 xcb_randr_get_output_info(
conn, randr_output, monitors->timestamp),
627 if (info != NULL && info->crtc != XCB_NONE) {
630 xcb_randr_get_output_info_name_length(info),
631 xcb_randr_get_output_info_name(info));
633 if (strcmp(name, oname) != 0) {
649 if (monitor_info->primary) {
658 new->to_be_disabled =
false;
660 new->primary = monitor_info->primary;
668 DLOG(
"name %s, x %d, y %d, width %d px, height %d px, width %d mm, height %d mm, primary %d, automatic %d\n",
670 monitor_info->x, monitor_info->y, monitor_info->width, monitor_info->height,
671 monitor_info->width_in_millimeters, monitor_info->height_in_millimeters,
672 monitor_info->primary, monitor_info->automatic);
689 xcb_randr_get_output_info_reply_t *output,
691 xcb_randr_get_screen_resources_current_reply_t *res) {
693 xcb_randr_get_crtc_info_reply_t *crtc;
696 bool existing = (
new != NULL);
711 xcb_randr_get_output_info_name_length(output),
712 xcb_randr_get_output_info_name(output));
720 if (output->crtc == XCB_NONE) {
726 }
else if (new->active)
727 new->to_be_disabled =
true;
731 xcb_randr_get_crtc_info_cookie_t icookie;
732 icookie = xcb_randr_get_crtc_info(conn, output->crtc, cts);
733 if ((crtc = xcb_randr_get_crtc_info_reply(conn, icookie, NULL)) == NULL) {
734 DLOG(
"Skipping output %s: could not get CRTC (%p)\n",
745 new->active = (
new->rect.width != 0 &&
new->rect.height != 0);
747 DLOG(
"width/height 0/0, disabling output\n");
751 DLOG(
"mode: %dx%d+%d+%d\n", new->rect.width, new->rect.height,
752 new->rect.x, new->rect.y);
757 if (!updated || !existing) {
775 DLOG(
"Querying outputs using RandR ≤ 1.4\n");
778 xcb_randr_get_screen_resources_current_cookie_t rcookie;
779 rcookie = xcb_randr_get_screen_resources_current(
conn,
root);
780 xcb_randr_get_output_primary_cookie_t pcookie;
781 pcookie = xcb_randr_get_output_primary(
conn,
root);
783 if ((
primary = xcb_randr_get_output_primary_reply(
conn, pcookie, NULL)) == NULL)
784 ELOG(
"Could not get RandR primary output\n");
788 xcb_randr_get_screen_resources_current_reply_t *res =
789 xcb_randr_get_screen_resources_current_reply(
conn, rcookie, NULL);
791 ELOG(
"Could not query screen resources.\n");
797 const xcb_timestamp_t cts = res->config_timestamp;
799 const int len = xcb_randr_get_screen_resources_current_outputs_length(res);
802 xcb_randr_output_t *randr_outputs = xcb_randr_get_screen_resources_current_outputs(res);
805 xcb_randr_get_output_info_cookie_t ocookie[len];
806 for (
int i = 0; i < len; i++)
807 ocookie[i] = xcb_randr_get_output_info(
conn, randr_outputs[i], cts);
810 for (
int i = 0; i < len; i++) {
811 xcb_randr_get_output_info_reply_t *output;
813 if ((output = xcb_randr_get_output_info_reply(
conn, ocookie[i], NULL)) == NULL)
838 DLOG(
"Active RandR output found. Disabling root output.\n");
842 DLOG(
"No active RandR output found. Enabling root output.\n");
843 root_output->
active =
true;
851 DLOG(
"output %p / %s, position (%d, %d), checking for clones\n",
864 DLOG(
"output %p has the same position, his mode = %d x %d\n",
879 DLOG(
"new output mode %d x %d, other mode %d x %d\n",
890 if (output->
active && output->
con == NULL) {
953 if (output->
con != NULL) {
958 DLOG(
"This output (%p) was focused! Getting next\n", output->
con);
960 DLOG(
"next = %p\n", next);
971 DLOG(
"Getting rid of current = %p / %s (empty, unfocused)\n", current, current->
name);
975 DLOG(
"Detaching current = %p / %s\n", current, current->
name);
977 DLOG(
"Re-attaching current = %p / %s\n", current, current->
name);
979 DLOG(
"Fixing the coordinates of floating containers\n");
984 DLOG(
"Done, next\n");
986 DLOG(
"re-attached all workspaces\n");
989 DLOG(
"now focusing next = %p\n", next);
997 if (child->
type != CT_DOCKAREA)
999 DLOG(
"Handling dock con %p\n", child);
1006 DLOG(
"Moving dock client %p to nc %p\n", dock, nc);
1008 DLOG(
"Re-attaching\n");
1014 DLOG(
"destroying disappearing con %p\n", output->
con);
1019 DLOG(
"Done. Should be fine now\n");
1027 root_output->
active =
true;
1038 const xcb_query_extension_reply_t *extreply;
1043 extreply = xcb_get_extension_data(
conn, &xcb_randr_id);
1044 if (!extreply->present) {
1045 DLOG(
"RandR is not present, activating root output.\n");
1050 xcb_generic_error_t *err;
1051 xcb_randr_query_version_reply_t *randr_version =
1052 xcb_randr_query_version_reply(
1053 conn, xcb_randr_query_version(
conn, XCB_RANDR_MAJOR_VERSION, XCB_RANDR_MINOR_VERSION), &err);
1055 ELOG(
"Could not query RandR version: X11 error code %d\n", err->error_code);
1062 (randr_version->minor_version >= 5) &&
1065 free(randr_version);
1069 if (event_base != NULL)
1070 *event_base = extreply->first_event;
1073 XCB_RANDR_NOTIFY_MASK_SCREEN_CHANGE |
1074 XCB_RANDR_NOTIFY_MASK_OUTPUT_CHANGE |
1075 XCB_RANDR_NOTIFY_MASK_CRTC_CHANGE |
1076 XCB_RANDR_NOTIFY_MASK_OUTPUT_PROPERTY);
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.
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...
#define TAILQ_INSERT_HEAD(head, elm, field)
struct outputs_head outputs
#define TAILQ_HEAD_INITIALIZER(head)
char * sstrdup(const char *str)
Safe-wrapper around strdup which exits if malloc returns NULL (meaning that there is no more memory a...
#define TAILQ_EMPTY(head)
A 'Con' represents everything from the X11 root window down to a single X11 window.
static bool any_randr_output_active(void)
#define SLIST_EMPTY(head)
xcb_connection_t * conn
XCB connection and root screen.
static void fallback_to_root_output(void)
bool tree_close_internal(Con *con, kill_window_t kill_window, bool dont_kill_parent, bool force_set_focus)
Closes the given container including all children.
void workspace_show(Con *workspace)
Switches to the given workspace.
bool contained_by_output(Rect rect)
xcb_randr_output_t id
Output id, so that we can requery the output directly later.
void con_focus(Con *con)
Sets input focus to the given container.
#define TAILQ_INSERT_TAIL(head, elm, field)
void init_ws_for_output(Output *output, Con *content)
Initializes at least one workspace for this output, trying the following steps until there is at leas...
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...
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.
int default_orientation
Default orientation for new containers.
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
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 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.
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_FOREACH(var, head, field)
#define TAILQ_FIRST(head)
static bool randr_query_outputs_15(void)
static bool has_randr_1_5
fullscreen_mode_t fullscreen_mode
#define TAILQ_REMOVE(head, elm, field)
void render_con(Con *con, bool render_fullscreen)
"Renders" the given container (and its children), meaning that all rects are updated correctly...
void workspace_show_by_name(const char *num)
Looks up the workspace by name and switches to it.
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...
Stores which workspace (by name or number) goes to which output.
Con * output_get_content(Con *output)
Returns the output container below the given output container.
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...
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 * con_for_window(Con *con, i3Window *window, Match **store_match)
Returns the first container below 'con' which wants to swallow this window TODO: priority.
#define SLIST_INSERT_HEAD(head, elm, field)
An Output is a physical output on your graphics driver.
Con * con_descend_focused(Con *con)
Returns the focused con inside this client, descending the tree as far as possible.
enum Match::@17 insert_where
Con * con_new(Con *parent, i3Window *window)
#define SLIST_FOREACH(var, head, field)
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...
#define TAILQ_NEXT(elm, field)
A "match" is a data structure which acts like a mask or expression to match certain windows or not...
Output * get_output_next_wrap(direction_t direction, Output *current)
Like get_output_next with close_far == CLOSEST_OUTPUT, but wraps.
#define SLIST_FIRST(head)
Con * con
Pointer to the Con which represents this output.
struct ws_assignments_head ws_assignments
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.
void randr_disable_output(Output *output)
Disables the output and moves its content.
names_head
List of names for the 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...
void tree_render(void)
Renders the tree, that is rendering all outputs using render_con() and pushing the changes to X11 usi...
Rect rect
x, y, width, height
void match_init(Match *match)
char * output_primary_name(Output *output)
Retrieves the primary name of an output.
#define GREP_FIRST(dest, head, condition)
#define SLIST_REMOVE_HEAD(head, field)
static void randr_query_outputs_14(void)
Stores a rectangle, for example the size of a window, the child window etc.
static Output * get_output_by_id(xcb_randr_output_t id)
bool workspace_is_visible(Con *ws)
Returns true if the workspace is currently visible.
bool changed
Internal flags, necessary for querying RandR screens (happens in two stages)
Output * create_root_output(xcb_connection_t *conn)
Con * create_workspace_on_output(Output *output, Con *content)
Returns a pointer to a new workspace in the given output.
bool active
Whether the output is currently active (has a CRTC attached with a valid mode)
xcb_randr_get_output_primary_reply_t * primary