i3
randr.c
Go to the documentation of this file.
1 /*
2  * vim:ts=4:sw=4:expandtab
3  *
4  * i3 - an improved dynamic tiling window manager
5  * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
6  *
7  * For more information on RandR, please see the X.org RandR specification at
8  * https://cgit.freedesktop.org/xorg/proto/randrproto/tree/randrproto.txt
9  * (take your time to read it completely, it answers all questions).
10  *
11  */
12 #include "all.h"
13 
14 #include <time.h>
15 #include <xcb/randr.h>
16 
17 /* Pointer to the result of the query for primary output */
18 xcb_randr_get_output_primary_reply_t *primary;
19 
20 /* Stores all outputs available in your current session. */
21 struct outputs_head outputs = TAILQ_HEAD_INITIALIZER(outputs);
22 
23 /* This is the output covering the root window */
25 static bool has_randr_1_5 = false;
26 
27 /*
28  * Get a specific output by its internal X11 id. Used by randr_query_outputs
29  * to check if the output is new (only in the first scan) or if we are
30  * re-scanning.
31  *
32  */
33 static Output *get_output_by_id(xcb_randr_output_t id) {
34  Output *output;
35  TAILQ_FOREACH(output, &outputs, outputs)
36  if (output->id == id)
37  return output;
38 
39  return NULL;
40 }
41 
42 /*
43  * Returns the output with the given name or NULL.
44  * If require_active is true, only active outputs are considered.
45  *
46  */
47 Output *get_output_by_name(const char *name, const bool require_active) {
48  Output *output;
49  bool get_primary = (strcasecmp("primary", name) == 0);
50  TAILQ_FOREACH(output, &outputs, outputs) {
51  if (output->primary && get_primary) {
52  return output;
53  }
54  if (require_active && !output->active) {
55  continue;
56  }
57  struct output_name *output_name;
59  if (strcasecmp(output_name->name, name) == 0) {
60  return output;
61  }
62  }
63  }
64 
65  return NULL;
66 }
67 
68 /*
69  * Returns the first output which is active.
70  *
71  */
73  Output *output;
74 
75  TAILQ_FOREACH(output, &outputs, outputs)
76  if (output->active)
77  return output;
78 
79  die("No usable outputs available.\n");
80 }
81 
82 /*
83  * Check whether there are any active outputs (excluding the root output).
84  *
85  */
86 static bool any_randr_output_active(void) {
87  Output *output;
88 
89  TAILQ_FOREACH(output, &outputs, outputs) {
90  if (output != root_output && !output->to_be_disabled && output->active)
91  return true;
92  }
93 
94  return false;
95 }
96 
97 /*
98  * Returns the active (!) output which contains the coordinates x, y or NULL
99  * if there is no output which contains these coordinates.
100  *
101  */
102 Output *get_output_containing(unsigned int x, unsigned int y) {
103  Output *output;
104  TAILQ_FOREACH(output, &outputs, outputs) {
105  if (!output->active)
106  continue;
107  DLOG("comparing x=%d y=%d with x=%d and y=%d width %d height %d\n",
108  x, y, output->rect.x, output->rect.y, output->rect.width, output->rect.height);
109  if (x >= output->rect.x && x < (output->rect.x + output->rect.width) &&
110  y >= output->rect.y && y < (output->rect.y + output->rect.height))
111  return output;
112  }
113 
114  return NULL;
115 }
116 
117 /*
118  * Returns the active output which contains the midpoint of the given rect. If
119  * such an output doesn't exist, returns the output which contains most of the
120  * rectangle or NULL if there is no output which intersects with it.
121  *
122  */
124  unsigned int mid_x = rect.x + rect.width / 2;
125  unsigned int mid_y = rect.y + rect.height / 2;
126  Output *output = get_output_containing(mid_x, mid_y);
127 
128  return output ? output : output_containing_rect(rect);
129 }
130 
131 /*
132  * Returns the active output which spans exactly the area specified by
133  * rect or NULL if there is no output like this.
134  *
135  */
137  Output *output;
138  TAILQ_FOREACH(output, &outputs, outputs) {
139  if (!output->active)
140  continue;
141  DLOG("comparing x=%d y=%d %dx%d with x=%d and y=%d %dx%d\n",
142  rect.x, rect.y, rect.width, rect.height,
143  output->rect.x, output->rect.y, output->rect.width, output->rect.height);
144  if (rect.x == output->rect.x && rect.width == output->rect.width &&
145  rect.y == output->rect.y && rect.height == output->rect.height)
146  return output;
147  }
148 
149  return NULL;
150 }
151 
152 /*
153  * In output_containing_rect, we check if any active output contains part of the container.
154  * We do this by checking if the output rect is intersected by the Rect.
155  * This is the 2-dimensional counterpart of get_output_containing.
156  * Returns the output with the maximum intersecting area.
157  *
158  */
160  Output *output;
161  int lx = rect.x, uy = rect.y;
162  int rx = rect.x + rect.width, by = rect.y + rect.height;
163  long max_area = 0;
164  Output *result = NULL;
165  TAILQ_FOREACH(output, &outputs, outputs) {
166  if (!output->active)
167  continue;
168  int lx_o = (int)output->rect.x, uy_o = (int)output->rect.y;
169  int rx_o = (int)(output->rect.x + output->rect.width), by_o = (int)(output->rect.y + output->rect.height);
170  DLOG("comparing x=%d y=%d with x=%d and y=%d width %d height %d\n",
171  rect.x, rect.y, output->rect.x, output->rect.y, output->rect.width, output->rect.height);
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) {
179  result = output;
180  }
181  }
182  }
183  return result;
184 }
185 
186 /*
187  * Like get_output_next with close_far == CLOSEST_OUTPUT, but wraps.
188  *
189  * For example if get_output_next(D_DOWN, x, FARTHEST_OUTPUT) = NULL, then
190  * get_output_next_wrap(D_DOWN, x) will return the topmost output.
191  *
192  * This function always returns a output: if no active outputs can be found,
193  * current itself is returned.
194  *
195  */
197  Output *best = get_output_next(direction, current, CLOSEST_OUTPUT);
198  /* If no output can be found, wrap */
199  if (!best) {
200  direction_t opposite;
201  if (direction == D_RIGHT)
202  opposite = D_LEFT;
203  else if (direction == D_LEFT)
204  opposite = D_RIGHT;
205  else if (direction == D_DOWN)
206  opposite = D_UP;
207  else
208  opposite = D_DOWN;
209  best = get_output_next(opposite, current, FARTHEST_OUTPUT);
210  }
211  if (!best)
212  best = current;
213  DLOG("current = %s, best = %s\n", output_primary_name(current), output_primary_name(best));
214  return best;
215 }
216 
217 /*
218  * Gets the output which is the next one in the given direction.
219  *
220  * If close_far == CLOSEST_OUTPUT, then the output next to the current one will
221  * selected. If close_far == FARTHEST_OUTPUT, the output which is the last one
222  * in the given direction will be selected.
223  *
224  * NULL will be returned when no active outputs are present in the direction
225  * specified (note that “current” counts as such an output).
226  *
227  */
228 Output *get_output_next(direction_t direction, Output *current, output_close_far_t close_far) {
229  Rect *cur = &(current->rect),
230  *other;
231  Output *output,
232  *best = NULL;
233  TAILQ_FOREACH(output, &outputs, outputs) {
234  if (!output->active)
235  continue;
236 
237  other = &(output->rect);
238 
239  if ((direction == D_RIGHT && other->x > cur->x) ||
240  (direction == D_LEFT && other->x < cur->x)) {
241  /* Skip the output when it doesn’t overlap the other one’s y
242  * coordinate at all. */
243  if ((other->y + other->height) <= cur->y ||
244  (cur->y + cur->height) <= other->y)
245  continue;
246  } else if ((direction == D_DOWN && other->y > cur->y) ||
247  (direction == D_UP && other->y < cur->y)) {
248  /* Skip the output when it doesn’t overlap the other one’s x
249  * coordinate at all. */
250  if ((other->x + other->width) <= cur->x ||
251  (cur->x + cur->width) <= other->x)
252  continue;
253  } else
254  continue;
255 
256  /* No candidate yet? Start with this one. */
257  if (!best) {
258  best = output;
259  continue;
260  }
261 
262  if (close_far == CLOSEST_OUTPUT) {
263  /* Is this output better (closer to the current output) than our
264  * current best bet? */
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)) {
269  best = output;
270  continue;
271  }
272  } else {
273  /* Is this output better (farther to the current output) than our
274  * current best bet? */
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)) {
279  best = output;
280  continue;
281  }
282  }
283  }
284 
285  DLOG("current = %s, best = %s\n", output_primary_name(current), (best ? output_primary_name(best) : "NULL"));
286  return best;
287 }
288 
289 /*
290  * Creates an output covering the root window.
291  *
292  */
293 Output *create_root_output(xcb_connection_t *conn) {
294  Output *s = scalloc(1, sizeof(Output));
295 
296  s->active = false;
297  s->rect.x = 0;
298  s->rect.y = 0;
299  s->rect.width = root_screen->width_in_pixels;
300  s->rect.height = root_screen->height_in_pixels;
301 
302  struct output_name *output_name = scalloc(1, sizeof(struct output_name));
303  output_name->name = "xroot-0";
304  SLIST_INIT(&s->names_head);
306 
307  return s;
308 }
309 
310 /*
311  * Initializes a CT_OUTPUT Con (searches existing ones from inplace restart
312  * before) to use for the given Output.
313  *
314  */
315 void output_init_con(Output *output) {
316  Con *con = NULL, *current;
317  bool reused = false;
318 
319  DLOG("init_con for output %s\n", output_primary_name(output));
320 
321  /* Search for a Con with that name directly below the root node. There
322  * might be one from a restored layout. */
323  TAILQ_FOREACH(current, &(croot->nodes_head), nodes) {
324  if (strcmp(current->name, output_primary_name(output)) != 0)
325  continue;
326 
327  con = current;
328  reused = true;
329  DLOG("Using existing con %p / %s\n", con, con->name);
330  break;
331  }
332 
333  if (con == NULL) {
334  con = con_new(croot, NULL);
335  FREE(con->name);
336  con->name = sstrdup(output_primary_name(output));
337  con->type = CT_OUTPUT;
338  con->layout = L_OUTPUT;
340  }
341  con->rect = output->rect;
342  output->con = con;
343 
344  char *name;
345  sasprintf(&name, "[i3 con] output %s", con->name);
346  x_set_name(con, name);
347  FREE(name);
348 
349  if (reused) {
350  DLOG("Not adding workspace, this was a reused con\n");
351  return;
352  }
353 
354  DLOG("Changing layout, adding top/bottom dockarea\n");
355  Con *topdock = con_new(NULL, NULL);
356  topdock->type = CT_DOCKAREA;
357  topdock->layout = L_DOCKAREA;
358  /* this container swallows dock clients */
359  Match *match = scalloc(1, sizeof(Match));
360  match_init(match);
361  match->dock = M_DOCK_TOP;
362  match->insert_where = M_BELOW;
363  TAILQ_INSERT_TAIL(&(topdock->swallow_head), match, matches);
364 
365  FREE(topdock->name);
366  topdock->name = sstrdup("topdock");
367 
368  sasprintf(&name, "[i3 con] top dockarea %s", con->name);
369  x_set_name(topdock, name);
370  FREE(name);
371  DLOG("attaching\n");
372  con_attach(topdock, con, false);
373 
374  /* content container */
375 
376  DLOG("adding main content container\n");
377  Con *content = con_new(NULL, NULL);
378  content->type = CT_CON;
379  content->layout = L_SPLITH;
380  FREE(content->name);
381  content->name = sstrdup("content");
382 
383  sasprintf(&name, "[i3 con] content %s", con->name);
384  x_set_name(content, name);
385  FREE(name);
386  con_attach(content, con, false);
387 
388  /* bottom dock container */
389  Con *bottomdock = con_new(NULL, NULL);
390  bottomdock->type = CT_DOCKAREA;
391  bottomdock->layout = L_DOCKAREA;
392  /* this container swallows dock clients */
393  match = scalloc(1, sizeof(Match));
394  match_init(match);
395  match->dock = M_DOCK_BOTTOM;
396  match->insert_where = M_BELOW;
397  TAILQ_INSERT_TAIL(&(bottomdock->swallow_head), match, matches);
398 
399  FREE(bottomdock->name);
400  bottomdock->name = sstrdup("bottomdock");
401 
402  sasprintf(&name, "[i3 con] bottom dockarea %s", con->name);
403  x_set_name(bottomdock, name);
404  FREE(name);
405  DLOG("attaching\n");
406  con_attach(bottomdock, con, false);
407 
408  /* Change focus to the content container */
409  TAILQ_REMOVE(&(con->focus_head), content, focused);
410  TAILQ_INSERT_HEAD(&(con->focus_head), content, focused);
411 }
412 
413 /*
414  * Initializes at least one workspace for this output, trying the following
415  * steps until there is at least one workspace:
416  *
417  * • Move existing workspaces, which are assigned to be on the given output, to
418  * the output.
419  * • Create the first assigned workspace for this output.
420  * • Create the first unused workspace.
421  *
422  */
423 void init_ws_for_output(Output *output) {
424  Con *content = output_get_content(output->con);
425  Con *previous_focus = con_get_workspace(focused);
426 
427  /* go through all assignments and move the existing workspaces to this output */
428  struct Workspace_Assignment *assignment;
430  if (!output_triggers_assignment(output, assignment)) {
431  continue;
432  }
433  Con *workspace = get_existing_workspace_by_name(assignment->name);
434  if (workspace == NULL)
435  continue;
436 
437  /* check that this workspace is not already attached (that means the
438  * user configured this assignment twice) */
439  Con *workspace_out = con_get_output(workspace);
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",
444  workspace->name, output_primary_name(output));
445  continue;
446  }
447 
448  DLOG("Moving workspace \"%s\" from output \"%s\" to \"%s\" due to assignment\n",
449  workspace->name, workspace_out->name, output_primary_name(output));
450  /* Need to copy output's rect since content is not yet rendered. We
451  * can't call render_con here because render_output only proceeds if a
452  * workspace exists. */
453  content->rect = output->con->rect;
454  workspace_move_to_output(workspace, output);
455  }
456 
457  /* Temporarily set the focused container, might not be initialized yet. */
458  focused = content;
459 
460  /* if a workspace exists, we are done now */
461  if (!TAILQ_EMPTY(&(content->nodes_head))) {
462  /* ensure that one of the workspaces is actually visible (in fullscreen
463  * mode), if they were invisible before, this might not be the case. */
464  Con *visible = NULL;
465  GREP_FIRST(visible, content, child->fullscreen_mode == CF_OUTPUT);
466  if (!visible) {
467  visible = TAILQ_FIRST(&(content->nodes_head));
468  workspace_show(visible);
469  }
470  goto restore_focus;
471  }
472 
473  /* otherwise, we create the first assigned ws for this output */
475  if (!output_triggers_assignment(output, assignment)) {
476  continue;
477  }
478 
479  LOG("Initializing first assigned workspace \"%s\" for output \"%s\"\n",
480  assignment->name, assignment->output);
481  workspace_show_by_name(assignment->name);
482  goto restore_focus;
483  }
484 
485  /* if there is still no workspace, we create the first free workspace */
486  DLOG("Now adding a workspace\n");
488 
489 restore_focus:
490  if (previous_focus) {
491  workspace_show(previous_focus);
492  }
493 }
494 
495 /*
496  * This function needs to be called when changing the mode of an output when
497  * it already has some workspaces (or a bar window) assigned.
498  *
499  * It reconfigures the bar window for the new mode, copies the new rect into
500  * each workspace on this output and forces all windows on the affected
501  * workspaces to be reconfigured.
502  *
503  * It is necessary to call render_layout() afterwards.
504  *
505  */
506 static void output_change_mode(xcb_connection_t *conn, Output *output) {
507  DLOG("Output mode changed, updating rect\n");
508  assert(output->con != NULL);
509  output->con->rect = output->rect;
510 
511  Con *content, *workspace, *child;
512 
513  /* Point content to the container of the workspaces */
514  content = output_get_content(output->con);
515 
516  /* Fix the position of all floating windows on this output.
517  * The 'rect' of each workspace will be updated in src/render.c. */
518  TAILQ_FOREACH(workspace, &(content->nodes_head), nodes) {
519  TAILQ_FOREACH(child, &(workspace->floating_head), floating_windows) {
520  floating_fix_coordinates(child, &(workspace->rect), &(output->con->rect));
521  }
522  }
523 
524  /* If default_orientation is NO_ORIENTATION, we change the orientation of
525  * the workspaces and their children depending on output resolution. This is
526  * only done for workspaces with maximum one child. */
528  TAILQ_FOREACH(workspace, &(content->nodes_head), nodes) {
529  /* Workspaces with more than one child are left untouched because
530  * we do not want to change an existing layout. */
531  if (con_num_children(workspace) > 1)
532  continue;
533 
534  workspace->layout = (output->rect.height > output->rect.width) ? L_SPLITV : L_SPLITH;
535  DLOG("Setting workspace [%d,%s]'s layout to %d.\n", workspace->num, workspace->name, workspace->layout);
536  if ((child = TAILQ_FIRST(&(workspace->nodes_head)))) {
537  if (child->layout == L_SPLITV || child->layout == L_SPLITH)
538  child->layout = workspace->layout;
539  DLOG("Setting child [%d,%s]'s layout to %d.\n", child->num, child->name, child->layout);
540  }
541  }
542  }
543 }
544 
545 /*
546  * randr_query_outputs_15 uses RandR ≥ 1.5 to update outputs.
547  *
548  */
549 static bool randr_query_outputs_15(void) {
550 #if XCB_RANDR_MINOR_VERSION < 5
551  return false;
552 #else
553  /* RandR 1.5 available at compile-time, i.e. libxcb is new enough */
554  if (!has_randr_1_5) {
555  return false;
556  }
557  /* RandR 1.5 available at run-time (supported by the server and not
558  * disabled by the user) */
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(
563  conn, xcb_randr_get_monitors(conn, root, true), &err);
564  if (err != NULL) {
565  ELOG("Could not get RandR monitors: X11 error code %d\n", err->error_code);
566  free(err);
567  /* Fall back to RandR ≤ 1.4 */
568  return false;
569  }
570 
571  /* Mark all outputs as to_be_disabled, since xcb_randr_get_monitors() will
572  * only return active outputs. */
573  Output *output;
575  if (output != root_output) {
576  output->to_be_disabled = true;
577  }
578  }
579 
580  DLOG("%d RandR monitors found (timestamp %d)\n",
581  xcb_randr_get_monitors_monitors_length(monitors),
582  monitors->timestamp);
583 
584  xcb_randr_monitor_info_iterator_t iter;
585  for (iter = xcb_randr_get_monitors_monitors_iterator(monitors);
586  iter.rem;
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);
592  if (err != NULL) {
593  ELOG("Could not get RandR monitor name: X11 error code %d\n", err->error_code);
594  free(err);
595  continue;
596  }
597  char *name;
598  sasprintf(&name, "%.*s",
599  xcb_get_atom_name_name_length(atom_reply),
600  xcb_get_atom_name_name(atom_reply));
601  free(atom_reply);
602 
603  Output *new = get_output_by_name(name, false);
604  if (new == NULL) {
605  new = scalloc(1, sizeof(Output));
606 
607  SLIST_INIT(&new->names_head);
608 
609  /* Register associated output names in addition to the monitor name */
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];
614 
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),
618  NULL);
619 
620  if (info != NULL && info->crtc != XCB_NONE) {
621  char *oname;
622  sasprintf(&oname, "%.*s",
623  xcb_randr_get_output_info_name_length(info),
624  xcb_randr_get_output_info_name(info));
625 
626  if (strcmp(name, oname) != 0) {
627  struct output_name *output_name = scalloc(1, sizeof(struct output_name));
628  output_name->name = sstrdup(oname);
629  SLIST_INSERT_HEAD(&new->names_head, output_name, names);
630  } else {
631  free(oname);
632  }
633  }
634  FREE(info);
635  }
636 
637  /* Insert the monitor name last, so that it's used as the primary name */
638  struct output_name *output_name = scalloc(1, sizeof(struct output_name));
640  SLIST_INSERT_HEAD(&new->names_head, output_name, names);
641 
642  if (monitor_info->primary) {
644  } else {
646  }
647  }
648  /* We specified get_active == true in xcb_randr_get_monitors(), so we
649  * will only receive active outputs. */
650  new->active = true;
651  new->to_be_disabled = false;
652 
653  new->primary = monitor_info->primary;
654 
655  new->changed =
656  update_if_necessary(&(new->rect.x), monitor_info->x) |
657  update_if_necessary(&(new->rect.y), monitor_info->y) |
658  update_if_necessary(&(new->rect.width), monitor_info->width) |
659  update_if_necessary(&(new->rect.height), monitor_info->height);
660 
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",
662  name,
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);
666  free(name);
667  }
668  free(monitors);
669  return true;
670 #endif
671 }
672 
673 /*
674  * Gets called by randr_query_outputs_14() for each output. The function adds
675  * new outputs to the list of outputs, checks if the mode of existing outputs
676  * has been changed or if an existing output has been disabled. It will then
677  * change either the "changed" or the "to_be_deleted" flag of the output, if
678  * appropriate.
679  *
680  */
681 static void handle_output(xcb_connection_t *conn, xcb_randr_output_t id,
682  xcb_randr_get_output_info_reply_t *output,
683  xcb_timestamp_t cts,
684  xcb_randr_get_screen_resources_current_reply_t *res) {
685  /* each CRT controller has a position in which we are interested in */
686  xcb_randr_get_crtc_info_reply_t *crtc;
687 
688  Output *new = get_output_by_id(id);
689  bool existing = (new != NULL);
690  if (!existing) {
691  new = scalloc(1, sizeof(Output));
692  SLIST_INIT(&new->names_head);
693  }
694  new->id = id;
695  new->primary = (primary && primary->output == id);
696  while (!SLIST_EMPTY(&new->names_head)) {
697  FREE(SLIST_FIRST(&new->names_head)->name);
698  struct output_name *old_head = SLIST_FIRST(&new->names_head);
699  SLIST_REMOVE_HEAD(&new->names_head, names);
700  FREE(old_head);
701  }
702  struct output_name *output_name = scalloc(1, sizeof(struct output_name));
703  sasprintf(&output_name->name, "%.*s",
704  xcb_randr_get_output_info_name_length(output),
705  xcb_randr_get_output_info_name(output));
706  SLIST_INSERT_HEAD(&new->names_head, output_name, names);
707 
708  DLOG("found output with name %s\n", output_primary_name(new));
709 
710  /* Even if no CRTC is used at the moment, we store the output so that
711  * we do not need to change the list ever again (we only update the
712  * position/size) */
713  if (output->crtc == XCB_NONE) {
714  if (!existing) {
715  if (new->primary)
717  else
719  } else if (new->active)
720  new->to_be_disabled = true;
721  return;
722  }
723 
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",
728  output_primary_name(new), crtc);
729  free(new);
730  return;
731  }
732 
733  bool updated = update_if_necessary(&(new->rect.x), crtc->x) |
734  update_if_necessary(&(new->rect.y), crtc->y) |
735  update_if_necessary(&(new->rect.width), crtc->width) |
736  update_if_necessary(&(new->rect.height), crtc->height);
737  free(crtc);
738  new->active = (new->rect.width != 0 && new->rect.height != 0);
739  if (!new->active) {
740  DLOG("width/height 0/0, disabling output\n");
741  return;
742  }
743 
744  DLOG("mode: %dx%d+%d+%d\n", new->rect.width, new->rect.height,
745  new->rect.x, new->rect.y);
746 
747  /* If we don’t need to change an existing output or if the output
748  * does not exist in the first place, the case is simple: we either
749  * need to insert the new output or we are done. */
750  if (!updated || !existing) {
751  if (!existing) {
752  if (new->primary)
754  else
756  }
757  return;
758  }
759 
760  new->changed = true;
761 }
762 
763 /*
764  * randr_query_outputs_14 uses RandR ≤ 1.4 to update outputs.
765  *
766  */
767 static void randr_query_outputs_14(void) {
768  DLOG("Querying outputs using RandR ≤ 1.4\n");
769 
770  /* Get screen resources (primary output, crtcs, outputs, modes) */
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);
775 
776  if ((primary = xcb_randr_get_output_primary_reply(conn, pcookie, NULL)) == NULL)
777  ELOG("Could not get RandR primary output\n");
778  else
779  DLOG("primary output is %08x\n", primary->output);
780 
781  xcb_randr_get_screen_resources_current_reply_t *res =
782  xcb_randr_get_screen_resources_current_reply(conn, rcookie, NULL);
783  if (res == NULL) {
784  ELOG("Could not query screen resources.\n");
785  return;
786  }
787 
788  /* timestamp of the configuration so that we get consistent replies to all
789  * requests (if the configuration changes between our different calls) */
790  const xcb_timestamp_t cts = res->config_timestamp;
791 
792  const int len = xcb_randr_get_screen_resources_current_outputs_length(res);
793 
794  /* an output is VGA-1, LVDS-1, etc. (usually physical video outputs) */
795  xcb_randr_output_t *randr_outputs = xcb_randr_get_screen_resources_current_outputs(res);
796 
797  /* Request information for each output */
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);
801 
802  /* Loop through all outputs available for this X11 screen */
803  for (int i = 0; i < len; i++) {
804  xcb_randr_get_output_info_reply_t *output;
805 
806  if ((output = xcb_randr_get_output_info_reply(conn, ocookie[i], NULL)) == NULL)
807  continue;
808 
809  handle_output(conn, randr_outputs[i], output, cts, res);
810  free(output);
811  }
812 
813  FREE(res);
814 }
815 
816 /*
817  * (Re-)queries the outputs via RandR and stores them in the list of outputs.
818  *
819  * If no outputs are found use the root window.
820  *
821  */
823  Output *output, *other;
824 
825  if (!randr_query_outputs_15()) {
827  }
828 
829  /* If there's no randr output, enable the output covering the root window. */
830  if (any_randr_output_active()) {
831  DLOG("Active RandR output found. Disabling root output.\n");
832  if (root_output && root_output->active) {
833  root_output->to_be_disabled = true;
834  }
835  } else {
836  DLOG("No active RandR output found. Enabling root output.\n");
837  root_output->active = true;
838  }
839 
840  /* Check for clones, disable the clones and reduce the mode to the
841  * lowest common mode */
842  TAILQ_FOREACH(output, &outputs, outputs) {
843  if (!output->active || output->to_be_disabled)
844  continue;
845  DLOG("output %p / %s, position (%d, %d), checking for clones\n",
846  output, output_primary_name(output), output->rect.x, output->rect.y);
847 
848  for (other = output;
849  other != TAILQ_END(&outputs);
850  other = TAILQ_NEXT(other, outputs)) {
851  if (other == output || !other->active || other->to_be_disabled)
852  continue;
853 
854  if (other->rect.x != output->rect.x ||
855  other->rect.y != output->rect.y)
856  continue;
857 
858  DLOG("output %p has the same position, his mode = %d x %d\n",
859  other, other->rect.width, other->rect.height);
860  uint32_t width = min(other->rect.width, output->rect.width);
861  uint32_t height = min(other->rect.height, output->rect.height);
862 
863  if (update_if_necessary(&(output->rect.width), width) |
864  update_if_necessary(&(output->rect.height), height))
865  output->changed = true;
866 
867  update_if_necessary(&(other->rect.width), width);
869 
870  DLOG("disabling output %p (%s)\n", other, output_primary_name(other));
871  other->to_be_disabled = true;
872 
873  DLOG("new output mode %d x %d, other mode %d x %d\n",
874  output->rect.width, output->rect.height,
875  other->rect.width, other->rect.height);
876  }
877  }
878 
879  /* Ensure that all outputs which are active also have a con. This is
880  * necessary because in the next step, a clone might get disabled. Example:
881  * LVDS1 active, VGA1 gets activated as a clone of LVDS1 (has no con).
882  * LVDS1 gets disabled. */
883  TAILQ_FOREACH(output, &outputs, outputs) {
884  if (output->active && output->con == NULL) {
885  DLOG("Need to initialize a Con for output %s\n", output_primary_name(output));
886  output_init_con(output);
887  output->changed = false;
888  }
889  }
890 
891  /* Handle outputs which have a new mode or are disabled now (either
892  * because the user disabled them or because they are clones) */
893  TAILQ_FOREACH(output, &outputs, outputs) {
894  if (output->to_be_disabled) {
895  randr_disable_output(output);
896  }
897 
898  if (output->changed) {
899  output_change_mode(conn, output);
900  output->changed = false;
901  }
902  }
903 
904  /* Just go through each active output and assign one workspace */
905  TAILQ_FOREACH(output, &outputs, outputs) {
906  if (!output->active)
907  continue;
908  Con *content = output_get_content(output->con);
909  if (!TAILQ_EMPTY(&(content->nodes_head)))
910  continue;
911  DLOG("Should add ws for output %s\n", output_primary_name(output));
912  init_ws_for_output(output);
913  }
914 
915  /* Focus the primary screen, if possible */
916  TAILQ_FOREACH(output, &outputs, outputs) {
917  if (!output->primary || !output->con)
918  continue;
919 
920  DLOG("Focusing primary output %s\n", output_primary_name(output));
921  Con *content = output_get_content(output->con);
922  Con *ws = TAILQ_FIRST(&(content)->focus_head);
923  workspace_show(ws);
924  }
925 
926  /* render_layout flushes */
927  tree_render();
928 
929  FREE(primary);
930 }
931 
932 /*
933  * Disables the output and moves its content.
934  *
935  */
937  assert(output->to_be_disabled);
938 
939  output->active = false;
940  DLOG("Output %s disabled, re-assigning workspaces/docks\n", output_primary_name(output));
941 
942  Output *first = get_first_output();
943 
944  /* TODO: refactor the following code into a nice function. maybe
945  * use an on_destroy callback which is implement differently for
946  * different container types (CT_CONTENT vs. CT_DOCKAREA)? */
947  Con *first_content = output_get_content(first->con);
948 
949  if (output->con != NULL) {
950  /* We need to move the workspaces from the disappearing output to the first output */
951  /* 1: Get the con to focus next */
952  Con *next = focused;
953 
954  /* 2: iterate through workspaces and re-assign them, fixing the coordinates
955  * of floating containers as we go */
956  Con *current;
957  Con *old_content = output_get_content(output->con);
958  while (!TAILQ_EMPTY(&(old_content->nodes_head))) {
959  current = TAILQ_FIRST(&(old_content->nodes_head));
960  if (current != next && TAILQ_EMPTY(&(current->focus_head))) {
961  /* the workspace is empty and not focused, get rid of it */
962  DLOG("Getting rid of current = %p / %s (empty, unfocused)\n", current, current->name);
963  tree_close_internal(current, DONT_KILL_WINDOW, false);
964  continue;
965  }
966  DLOG("Detaching current = %p / %s\n", current, current->name);
967  con_detach(current);
968  DLOG("Re-attaching current = %p / %s\n", current, current->name);
969  con_attach(current, first_content, false);
970  DLOG("Fixing the coordinates of floating containers\n");
971  Con *floating_con;
972  TAILQ_FOREACH(floating_con, &(current->floating_head), floating_windows) {
973  floating_fix_coordinates(floating_con, &(output->con->rect), &(first->con->rect));
974  }
975  }
976 
977  /* Restore focus after con_detach / con_attach. next can be NULL, see #3523. */
978  if (next) {
979  DLOG("now focusing next = %p\n", next);
980  con_focus(next);
982  }
983 
984  /* 3: move the dock clients to the first output */
985  Con *child;
986  TAILQ_FOREACH(child, &(output->con->nodes_head), nodes) {
987  if (child->type != CT_DOCKAREA)
988  continue;
989  DLOG("Handling dock con %p\n", child);
990  Con *dock;
991  while (!TAILQ_EMPTY(&(child->nodes_head))) {
992  dock = TAILQ_FIRST(&(child->nodes_head));
993  Con *nc;
994  Match *match;
995  nc = con_for_window(first->con, dock->window, &match);
996  DLOG("Moving dock client %p to nc %p\n", dock, nc);
997  con_detach(dock);
998  DLOG("Re-attaching\n");
999  con_attach(dock, nc, false);
1000  DLOG("Done\n");
1001  }
1002  }
1003 
1004  DLOG("destroying disappearing con %p\n", output->con);
1005  Con *con = output->con;
1006  /* clear the pointer before calling tree_close_internal in which the memory is freed */
1007  output->con = NULL;
1009  DLOG("Done. Should be fine now\n");
1010  }
1011 
1012  output->to_be_disabled = false;
1013  output->changed = false;
1014 }
1015 
1016 static void fallback_to_root_output(void) {
1017  root_output->active = true;
1020 }
1021 
1022 /*
1023  * We have just established a connection to the X server and need the initial
1024  * XRandR information to setup workspaces for each screen.
1025  *
1026  */
1027 void randr_init(int *event_base, const bool disable_randr15) {
1028  const xcb_query_extension_reply_t *extreply;
1029 
1032 
1033  extreply = xcb_get_extension_data(conn, &xcb_randr_id);
1034  if (!extreply->present) {
1035  DLOG("RandR is not present, activating root output.\n");
1037  return;
1038  }
1039 
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);
1044  if (err != NULL) {
1045  ELOG("Could not query RandR version: X11 error code %d\n", err->error_code);
1046  free(err);
1048  return;
1049  }
1050 
1051  has_randr_1_5 = (randr_version->major_version >= 1) &&
1052  (randr_version->minor_version >= 5) &&
1053  !disable_randr15;
1054 
1055  free(randr_version);
1056 
1058 
1059  if (event_base != NULL)
1060  *event_base = extreply->first_event;
1061 
1062  xcb_randr_select_input(conn, root,
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);
1067 
1068  xcb_flush(conn);
1069 }
void con_fix_percent(Con *con)
Updates the percent attribute of the children of the given container.
Definition: con.c:950
#define FREE(pointer)
Definition: util.h:47
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.
Definition: x.c:1384
enum Con::@20 type
void con_detach(Con *con)
Detaches the given container from its current parent.
Definition: con.c:206
xcb_window_t root
Definition: main.c:57
Stores a rectangle, for example the size of a window, the child window etc.
Definition: data.h:175
struct outputs_head outputs
Definition: randr.c:21
#define SLIST_FOREACH(var, head, field)
Definition: queue.h:114
void workspace_show(Con *workspace)
Switches to the given workspace.
Definition: workspace.c:437
uint32_t y
Definition: data.h:177
char * name
Definition: data.h:381
Output * output_containing_rect(Rect rect)
In output_containing_rect, we check if any active output contains part of the container.
Definition: randr.c:159
Rect rect
x, y, width, height
Definition: data.h:418
Definition: data.h:57
bool to_be_disabled
Definition: data.h:405
uint32_t width
Definition: data.h:178
struct Con * croot
Definition: tree.c:12
int max(int a, int b)
Definition: util.c:31
#define DLOG(fmt,...)
Definition: libi3.h:104
static bool any_randr_output_active(void)
Definition: randr.c:86
#define TAILQ_EMPTY(head)
Definition: queue.h:344
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...
int min(int a, int b)
Definition: util.c:27
xcb_connection_t * conn
XCB connection and root screen.
Definition: main.c:44
static void fallback_to_root_output(void)
Definition: randr.c:1016
floating_head
Definition: data.h:714
void con_focus(Con *con)
Sets input focus to the given container.
Definition: con.c:222
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...
Definition: workspace.c:118
#define SLIST_EMPTY(head)
Definition: queue.h:111
void init_ws_for_output(Output *output)
Initializes at least one workspace for this output, trying the following steps until there is at leas...
Definition: randr.c:423
names_head
List of names for the output.
Definition: data.h:412
void randr_query_outputs(void)
Initializes the specified output, assigning the specified workspace to it.
Definition: randr.c:822
xcb_screen_t * root_screen
Definition: main.c:56
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...
Definition: randr.c:1027
An Output is a physical output on your graphics driver.
Definition: data.h:394
fullscreen_mode_t fullscreen_mode
Definition: data.h:725
#define TAILQ_INSERT_HEAD(head, elm, field)
Definition: queue.h:366
Output * get_output_by_name(const char *name, const bool require_active)
Returns the output with the given name or NULL.
Definition: randr.c:47
static void output_change_mode(xcb_connection_t *conn, Output *output)
Definition: randr.c:506
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.
Definition: randr.c:228
#define TAILQ_REMOVE(head, elm, field)
Definition: queue.h:402
struct Rect rect
Definition: data.h:672
xcb_randr_output_t id
Output id, so that we can requery the output directly later.
Definition: data.h:396
void output_init_con(Output *output)
Initializes a CT_OUTPUT Con (searches existing ones from inplace restart before) to use for the given...
Definition: randr.c:315
nodes_head
Definition: data.h:717
#define SLIST_INIT(head)
Definition: queue.h:127
static Output * root_output
Definition: randr.c:24
uint32_t width
Definition: data.h:139
Stores which workspace (by name or number) goes to which output and its gaps config.
Definition: data.h:225
char * name
Definition: data.h:682
#define TAILQ_FIRST(head)
Definition: queue.h:336
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...
Definition: floating.c:1000
Definition: data.h:58
Output * get_first_output(void)
Returns the first output which is active.
Definition: randr.c:72
void workspace_show_by_name(const char *num)
Looks up the workspace by name and switches to it.
Definition: workspace.c:569
Con * con_get_workspace(Con *con)
Gets the workspace container this node is on.
Definition: con.c:418
Con * con_get_output(Con *con)
Gets the output container (first container with CT_OUTPUT in hierarchy) this node is on.
Definition: con.c:404
int con_num_children(Con *con)
Returns the number of children of this container.
Definition: con.c:887
#define SLIST_FIRST(head)
Definition: queue.h:109
#define TAILQ_NEXT(elm, field)
Definition: queue.h:338
void tree_render(void)
Renders the tree, that is rendering all outputs using render_con() and pushing the changes to X11 usi...
Definition: tree.c:449
static bool randr_query_outputs_15(void)
Definition: randr.c:549
focus_head
Definition: data.h:720
static bool has_randr_1_5
Definition: randr.c:25
swallow_head
Definition: data.h:723
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.
Definition: util.c:121
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)
Definition: data.h:404
Definition: data.h:108
#define TAILQ_END(head)
Definition: queue.h:337
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...
Definition: randr.c:136
bool primary
Definition: data.h:406
#define LOG(fmt,...)
Definition: libi3.h:94
Con * output_get_content(Con *output)
Returns the output container below the given output container.
Definition: output.c:16
A "match" is a data structure which acts like a mask or expression to match certain windows or not.
Definition: data.h:521
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)
Definition: randr.c:681
enum Match::@17 insert_where
Definition: data.h:107
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.
Definition: con.c:792
struct Con * focused
Definition: tree.c:13
Output * get_output_from_rect(Rect rect)
Returns the active output which contains the midpoint of the given rect.
Definition: randr.c:123
void workspace_move_to_output(Con *ws, Output *output)
Move the given workspace to the specified output.
Definition: workspace.c:984
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.
Definition: workspace.c:30
uint32_t height
Definition: data.h:140
bool tree_close_internal(Con *con, kill_window_t kill_window, bool dont_kill_parent)
Closes the given container including all children.
Definition: tree.c:191
Con * con_new(Con *parent, i3Window *window)
A wrapper for con_new_skeleton, to retain the old con_new behaviour.
Definition: con.c:69
Definition: data.h:106
#define ELOG(fmt,...)
Definition: libi3.h:99
#define SLIST_REMOVE_HEAD(head, field)
Definition: queue.h:149
int default_orientation
Default orientation for new containers.
#define GREP_FIRST(dest, head, condition)
Definition: util.h:38
Output * get_output_next_wrap(direction_t direction, Output *current)
Like get_output_next with close_far == CLOSEST_OUTPUT, but wraps.
Definition: randr.c:196
bool active
Whether the output is currently active (has a CRTC attached with a valid mode)
Definition: data.h:400
struct ws_assignments_head ws_assignments
Definition: main.c:87
#define SLIST_INSERT_HEAD(head, elm, field)
Definition: queue.h:138
direction_t
Definition: data.h:56
Con * create_workspace_on_output(Output *output, Con *content)
Returns a pointer to a new workspace in the given output.
Definition: workspace.c:251
int num
the workspace number, if this Con is of type CT_WORKSPACE and the workspace is not a named workspace ...
Definition: data.h:663
Definition: data.h:56
void con_attach(Con *con, Con *parent, bool ignore_focus)
Attaches the given container to the given parent.
Definition: con.c:198
A 'Con' represents everything from the X11 root window down to a single X11 window.
Definition: data.h:633
void randr_disable_output(Output *output)
Disables the output and moves its content.
Definition: randr.c:936
uint32_t height
Definition: data.h:179
Config config
Definition: config.c:17
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...
Definition: randr.c:102
struct Window * window
Definition: data.h:704
void match_init(Match *match)
Initializes the Match data structure.
Definition: match.c:26
uint32_t y
Definition: data.h:138
layout_t layout
Definition: data.h:746
char * output_primary_name(Output *output)
Retrieves the primary name of an output.
Definition: output.c:51
static void randr_query_outputs_14(void)
Definition: randr.c:767
Con * con
Pointer to the Con which represents this output.
Definition: data.h:415
static Output * get_output_by_id(xcb_randr_output_t id)
Definition: randr.c:33
Definition: data.h:59
Output * create_root_output(xcb_connection_t *conn)
Creates an output covering the root window.
Definition: randr.c:293
#define TAILQ_HEAD_INITIALIZER(head)
Definition: queue.h:324
#define TAILQ_INSERT_TAIL(head, elm, field)
Definition: queue.h:376
uint32_t x
Definition: data.h:137
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:347
output_close_far_t
Definition: randr.h:22
uint32_t x
Definition: data.h:176
#define die(...)
Definition: util.h:19
enum Match::@15 dock
xcb_randr_get_output_primary_reply_t * primary
Definition: randr.c:18