i3
workspace.c
Go to the documentation of this file.
1 #undef I3__FILE__
2 #define I3__FILE__ "workspace.c"
3 /*
4  * vim:ts=4:sw=4:expandtab
5  *
6  * i3 - an improved dynamic tiling window manager
7  * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
8  *
9  * workspace.c: Modifying workspaces, accessing them, moving containers to
10  * workspaces.
11  *
12  */
13 #include "all.h"
14 
15 /* Stores a copy of the name of the last used workspace for the workspace
16  * back-and-forth switching. */
17 static char *previous_workspace_name = NULL;
18 
19 /*
20  * Sets ws->layout to splith/splitv if default_orientation was specified in the
21  * configfile. Otherwise, it uses splith/splitv depending on whether the output
22  * is higher than wide.
23  *
24  */
26  /* If default_orientation is set to NO_ORIENTATION we determine
27  * orientation depending on output resolution. */
29  Con *output = con_get_output(ws);
30  ws->layout = (output->rect.height > output->rect.width) ? L_SPLITV : L_SPLITH;
31  DLOG("Auto orientation. Workspace size set to (%d,%d), setting layout to %d.\n",
32  output->rect.width, output->rect.height, ws->layout);
33  } else {
35  }
36 }
37 
38 /*
39  * Returns a pointer to the workspace with the given number (starting at 0),
40  * creating the workspace if necessary (by allocating the necessary amount of
41  * memory and initializing the data structures correctly).
42  *
43  */
44 Con *workspace_get(const char *num, bool *created) {
45  Con *output, *workspace = NULL;
46 
47  TAILQ_FOREACH (output, &(croot->nodes_head), nodes)
48  GREP_FIRST(workspace, output_get_content(output), !strcasecmp(child->name, num));
49 
50  if (workspace == NULL) {
51  LOG("Creating new workspace \"%s\"\n", num);
52  /* unless an assignment is found, we will create this workspace on the current output */
53  output = con_get_output(focused);
54  /* look for assignments */
55  struct Workspace_Assignment *assignment;
56 
57  /* We set workspace->num to the number if this workspace’s name begins
58  * with a positive number. Otherwise it’s a named ws and num will be
59  * -1. */
60  long parsed_num = ws_name_to_number(num);
61 
63  if (strcmp(assignment->name, num) == 0) {
64  DLOG("Found workspace name assignment to output \"%s\"\n", assignment->output);
65  GREP_FIRST(output, croot, !strcmp(child->name, assignment->output));
66  break;
67  } else if (parsed_num != -1 && name_is_digits(assignment->name) && ws_name_to_number(assignment->name) == parsed_num) {
68  DLOG("Found workspace number assignment to output \"%s\"\n", assignment->output);
69  GREP_FIRST(output, croot, !strcmp(child->name, assignment->output));
70  }
71  }
72 
73  Con *content = output_get_content(output);
74  LOG("got output %p with content %p\n", output, content);
75  /* We need to attach this container after setting its type. con_attach
76  * will handle CT_WORKSPACEs differently */
77  workspace = con_new(NULL, NULL);
78  char *name;
79  sasprintf(&name, "[i3 con] workspace %s", num);
80  x_set_name(workspace, name);
81  free(name);
82  workspace->type = CT_WORKSPACE;
83  FREE(workspace->name);
84  workspace->name = sstrdup(num);
86  workspace->num = parsed_num;
87  LOG("num = %d\n", workspace->num);
88 
89  workspace->parent = content;
91 
92  con_attach(workspace, content, false);
93 
94  ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"init\"}");
95  if (created != NULL)
96  *created = true;
97  } else if (created != NULL) {
98  *created = false;
99  }
100 
101  return workspace;
102 }
103 
104 /*
105  * Returns a pointer to a new workspace in the given output. The workspace
106  * is created attached to the tree hierarchy through the given content
107  * container.
108  *
109  */
111  /* add a workspace to this output */
112  Con *out, *current;
113  char *name;
114  bool exists = true;
115  Con *ws = con_new(NULL, NULL);
116  ws->type = CT_WORKSPACE;
117 
118  /* try the configured workspace bindings first to find a free name */
119  Binding *bind;
120  TAILQ_FOREACH (bind, bindings, bindings) {
121  DLOG("binding with command %s\n", bind->command);
122  if (strlen(bind->command) < strlen("workspace ") ||
123  strncasecmp(bind->command, "workspace", strlen("workspace")) != 0)
124  continue;
125  DLOG("relevant command = %s\n", bind->command);
126  char *target = bind->command + strlen("workspace ");
127  while ((*target == ' ' || *target == '\t') && target != '\0')
128  target++;
129  /* We check if this is the workspace
130  * next/prev/next_on_output/prev_on_output/back_and_forth/number command.
131  * Beware: The workspace names "next", "prev", "next_on_output",
132  * "prev_on_output", "number", "back_and_forth" and "current" are OK,
133  * so we check before stripping the double quotes */
134  if (strncasecmp(target, "next", strlen("next")) == 0 ||
135  strncasecmp(target, "prev", strlen("prev")) == 0 ||
136  strncasecmp(target, "next_on_output", strlen("next_on_output")) == 0 ||
137  strncasecmp(target, "prev_on_output", strlen("prev_on_output")) == 0 ||
138  strncasecmp(target, "number", strlen("number")) == 0 ||
139  strncasecmp(target, "back_and_forth", strlen("back_and_forth")) == 0 ||
140  strncasecmp(target, "current", strlen("current")) == 0)
141  continue;
142  if (*target == '"')
143  target++;
144  if (strncasecmp(target, "__", strlen("__")) == 0) {
145  LOG("Cannot create workspace \"%s\". Names starting with __ are i3-internal.\n", target);
146  continue;
147  }
148  FREE(ws->name);
149  ws->name = strdup(target);
150  if (ws->name[strlen(ws->name) - 1] == '"')
151  ws->name[strlen(ws->name) - 1] = '\0';
152  DLOG("trying name *%s*\n", ws->name);
153 
154  /* Ensure that this workspace is not assigned to a different output —
155  * otherwise we would create it, then move it over to its output, then
156  * find a new workspace, etc… */
157  bool assigned = false;
158  struct Workspace_Assignment *assignment;
160  if (strcmp(assignment->name, ws->name) != 0 ||
161  strcmp(assignment->output, output->name) == 0)
162  continue;
163 
164  assigned = true;
165  break;
166  }
167 
168  if (assigned)
169  continue;
170 
171  current = NULL;
172  TAILQ_FOREACH (out, &(croot->nodes_head), nodes)
173  GREP_FIRST(current, output_get_content(out), !strcasecmp(child->name, ws->name));
174 
175  exists = (current != NULL);
176  if (!exists) {
177  /* Set ->num to the number of the workspace, if the name actually
178  * is a number or starts with a number */
179  char *endptr = NULL;
180  long parsed_num = strtol(ws->name, &endptr, 10);
181  if (parsed_num == LONG_MIN ||
182  parsed_num == LONG_MAX ||
183  parsed_num < 0 ||
184  endptr == ws->name)
185  ws->num = -1;
186  else
187  ws->num = parsed_num;
188  LOG("Used number %d for workspace with name %s\n", ws->num, ws->name);
189 
190  break;
191  }
192  }
193 
194  if (exists) {
195  /* get the next unused workspace number */
196  DLOG("Getting next unused workspace by number\n");
197  int c = 0;
198  while (exists) {
199  c++;
200 
201  ws->num = c;
202 
203  current = NULL;
204  TAILQ_FOREACH (out, &(croot->nodes_head), nodes)
205  GREP_FIRST(current, output_get_content(out), child->num == ws->num);
206  exists = (current != NULL);
207 
208  DLOG("result for ws %d: exists = %d\n", c, exists);
209  }
210  sasprintf(&(ws->name), "%d", c);
211  }
212  con_attach(ws, content, false);
213 
214  sasprintf(&name, "[i3 con] workspace %s", ws->name);
215  x_set_name(ws, name);
216  free(name);
217 
219 
222 
223  return ws;
224 }
225 
226 /*
227  * Returns true if the workspace is currently visible. Especially important for
228  * multi-monitor environments, as they can have multiple currenlty active
229  * workspaces.
230  *
231  */
233  Con *output = con_get_output(ws);
234  if (output == NULL)
235  return false;
236  Con *fs = con_get_fullscreen_con(output, CF_OUTPUT);
237  LOG("workspace visible? fs = %p, ws = %p\n", fs, ws);
238  return (fs == ws);
239 }
240 
241 /*
242  * XXX: we need to clean up all this recursive walking code.
243  *
244  */
245 Con *_get_sticky(Con *con, const char *sticky_group, Con *exclude) {
246  Con *current;
247 
248  TAILQ_FOREACH (current, &(con->nodes_head), nodes) {
249  if (current != exclude &&
250  current->sticky_group != NULL &&
251  current->window != NULL &&
252  strcmp(current->sticky_group, sticky_group) == 0)
253  return current;
254 
255  Con *recurse = _get_sticky(current, sticky_group, exclude);
256  if (recurse != NULL)
257  return recurse;
258  }
259 
260  TAILQ_FOREACH (current, &(con->floating_head), floating_windows) {
261  if (current != exclude &&
262  current->sticky_group != NULL &&
263  current->window != NULL &&
264  strcmp(current->sticky_group, sticky_group) == 0)
265  return current;
266 
267  Con *recurse = _get_sticky(current, sticky_group, exclude);
268  if (recurse != NULL)
269  return recurse;
270  }
271 
272  return NULL;
273 }
274 
275 /*
276  * Reassigns all child windows in sticky containers. Called when the user
277  * changes workspaces.
278  *
279  * XXX: what about sticky containers which contain containers?
280  *
281  */
282 static void workspace_reassign_sticky(Con *con) {
283  Con *current;
284  /* 1: go through all containers */
285 
286  /* handle all children and floating windows of this node */
287  TAILQ_FOREACH (current, &(con->nodes_head), nodes) {
288  if (current->sticky_group == NULL) {
289  workspace_reassign_sticky(current);
290  continue;
291  }
292 
293  LOG("Ah, this one is sticky: %s / %p\n", current->name, current);
294  /* 2: find a window which we can re-assign */
295  Con *output = con_get_output(current);
296  Con *src = _get_sticky(output, current->sticky_group, current);
297 
298  if (src == NULL) {
299  LOG("No window found for this sticky group\n");
300  workspace_reassign_sticky(current);
301  continue;
302  }
303 
304  x_move_win(src, current);
305  current->window = src->window;
306  current->mapped = true;
307  src->window = NULL;
308  src->mapped = false;
309 
310  x_reparent_child(current, src);
311 
312  LOG("re-assigned window from src %p to dest %p\n", src, current);
313  }
314 
315  TAILQ_FOREACH (current, &(con->floating_head), floating_windows)
316  workspace_reassign_sticky(current);
317 }
318 
319 /*
320  * Callback to reset the urgent flag of the given con to false. May be started by
321  * _workspace_show to avoid urgency hints being lost by switching to a workspace
322  * focusing the con.
323  *
324  */
325 static void workspace_defer_update_urgent_hint_cb(EV_P_ ev_timer *w, int revents) {
326  Con *con = w->data;
327 
328  DLOG("Resetting urgency flag of con %p by timer\n", con);
329  con->urgent = false;
332  tree_render();
333 
334  ev_timer_stop(main_loop, con->urgency_timer);
335  FREE(con->urgency_timer);
336 }
337 
338 static void _workspace_show(Con *workspace) {
339  Con *current, *old = NULL;
340 
341  /* safe-guard against showing i3-internal workspaces like __i3_scratch */
342  if (con_is_internal(workspace))
343  return;
344 
345  /* disable fullscreen for the other workspaces and get the workspace we are
346  * currently on. */
347  TAILQ_FOREACH (current, &(workspace->parent->nodes_head), nodes) {
348  if (current->fullscreen_mode == CF_OUTPUT)
349  old = current;
350  current->fullscreen_mode = CF_NONE;
351  }
352 
353  /* enable fullscreen for the target workspace. If it happens to be the
354  * same one we are currently on anyways, we can stop here. */
355  workspace->fullscreen_mode = CF_OUTPUT;
356  current = con_get_workspace(focused);
357  if (workspace == current) {
358  DLOG("Not switching, already there.\n");
359  return;
360  }
361 
362  /* Remember currently focused workspace for switching back to it later with
363  * the 'workspace back_and_forth' command.
364  * NOTE: We have to duplicate the name as the original will be freed when
365  * the corresponding workspace is cleaned up.
366  * NOTE: Internal cons such as __i3_scratch (when a scratchpad window is
367  * focused) are skipped, see bug #868. */
368  if (current && !con_is_internal(current)) {
370  if (current) {
372  DLOG("Setting previous_workspace_name = %s\n", previous_workspace_name);
373  }
374  }
375 
376  workspace_reassign_sticky(workspace);
377 
378  DLOG("switching to %p / %s\n", workspace, workspace->name);
379  Con *next = con_descend_focused(workspace);
380 
381  /* Memorize current output */
382  Con *old_output = con_get_output(focused);
383 
384  /* Display urgency hint for a while if the newly visible workspace would
385  * focus and thereby immediately destroy it */
386  if (next->urgent && (int)(config.workspace_urgency_timer * 1000) > 0) {
387  /* focus for now… */
388  con_focus(next);
389 
390  /* … but immediately reset urgency flags; they will be set to false by
391  * the timer callback in case the container is focused at the time of
392  * its expiration */
393  focused->urgent = true;
394  workspace->urgent = true;
395 
396  if (focused->urgency_timer == NULL) {
397  DLOG("Deferring reset of urgency flag of con %p on newly shown workspace %p\n",
398  focused, workspace);
399  focused->urgency_timer = scalloc(sizeof(struct ev_timer));
400  /* use a repeating timer to allow for easy resets */
403  focused->urgency_timer->data = focused;
404  ev_timer_start(main_loop, focused->urgency_timer);
405  } else {
406  DLOG("Resetting urgency timer of con %p on workspace %p\n",
407  focused, workspace);
408  ev_timer_again(main_loop, focused->urgency_timer);
409  }
410  } else
411  con_focus(next);
412 
413  ipc_send_workspace_focus_event(workspace, current);
414 
415  DLOG("old = %p / %s\n", old, (old ? old->name : "(null)"));
416  /* Close old workspace if necessary. This must be done *after* doing
417  * urgency handling, because tree_close() will do a con_focus() on the next
418  * client, which will clear the urgency flag too early. Also, there is no
419  * way for con_focus() to know about when to clear urgency immediately and
420  * when to defer it. */
421  if (old && TAILQ_EMPTY(&(old->nodes_head)) && TAILQ_EMPTY(&(old->floating_head))) {
422  /* check if this workspace is currently visible */
423  if (!workspace_is_visible(old)) {
424  LOG("Closing old workspace (%p / %s), it is empty\n", old, old->name);
425  tree_close(old, DONT_KILL_WINDOW, false, false);
426  ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"empty\"}");
427  }
428  }
429 
430  workspace->fullscreen_mode = CF_OUTPUT;
431  LOG("focused now = %p / %s\n", focused, focused->name);
432 
433  /* Set mouse pointer */
434  Con *new_output = con_get_output(focused);
435  if (old_output != new_output) {
436  x_set_warp_to(&next->rect);
437  }
438 
439  /* Update the EWMH hints */
441 }
442 
443 /*
444  * Switches to the given workspace
445  *
446  */
447 void workspace_show(Con *workspace) {
448  _workspace_show(workspace);
449 }
450 
451 /*
452  * Looks up the workspace by name and switches to it.
453  *
454  */
455 void workspace_show_by_name(const char *num) {
456  Con *workspace;
457  workspace = workspace_get(num, NULL);
458  _workspace_show(workspace);
459 }
460 
461 /*
462  * Focuses the next workspace.
463  *
464  */
466  Con *current = con_get_workspace(focused);
467  Con *next = NULL;
468  Con *output;
469 
470  if (current->num == -1) {
471  /* If currently a named workspace, find next named workspace. */
472  next = TAILQ_NEXT(current, nodes);
473  } else {
474  /* If currently a numbered workspace, find next numbered workspace. */
475  TAILQ_FOREACH (output, &(croot->nodes_head), nodes) {
476  /* Skip outputs starting with __, they are internal. */
477  if (con_is_internal(output))
478  continue;
480  if (child->type != CT_WORKSPACE)
481  continue;
482  if (child->num == -1)
483  break;
484  /* Need to check child against current and next because we are
485  * traversing multiple lists and thus are not guaranteed the
486  * relative order between the list of workspaces. */
487  if (current->num < child->num && (!next || child->num < next->num))
488  next = child;
489  }
490  }
491  }
492 
493  /* Find next named workspace. */
494  if (!next) {
495  bool found_current = false;
496  TAILQ_FOREACH (output, &(croot->nodes_head), nodes) {
497  /* Skip outputs starting with __, they are internal. */
498  if (con_is_internal(output))
499  continue;
501  if (child->type != CT_WORKSPACE)
502  continue;
503  if (child == current) {
504  found_current = 1;
505  } else if (child->num == -1 && (current->num != -1 || found_current)) {
506  next = child;
507  goto workspace_next_end;
508  }
509  }
510  }
511  }
512 
513  /* Find first workspace. */
514  if (!next) {
515  TAILQ_FOREACH (output, &(croot->nodes_head), nodes) {
516  /* Skip outputs starting with __, they are internal. */
517  if (con_is_internal(output))
518  continue;
520  if (child->type != CT_WORKSPACE)
521  continue;
522  if (!next || (child->num != -1 && child->num < next->num))
523  next = child;
524  }
525  }
526  }
527 workspace_next_end:
528  return next;
529 }
530 
531 /*
532  * Focuses the previous workspace.
533  *
534  */
536  Con *current = con_get_workspace(focused);
537  Con *prev = NULL;
538  Con *output;
539 
540  if (current->num == -1) {
541  /* If named workspace, find previous named workspace. */
542  prev = TAILQ_PREV(current, nodes_head, nodes);
543  if (prev && prev->num != -1)
544  prev = NULL;
545  } else {
546  /* If numbered workspace, find previous numbered workspace. */
547  TAILQ_FOREACH_REVERSE (output, &(croot->nodes_head), nodes_head, nodes) {
548  /* Skip outputs starting with __, they are internal. */
549  if (con_is_internal(output))
550  continue;
552  if (child->type != CT_WORKSPACE || child->num == -1)
553  continue;
554  /* Need to check child against current and previous because we
555  * are traversing multiple lists and thus are not guaranteed
556  * the relative order between the list of workspaces. */
557  if (current->num > child->num && (!prev || child->num > prev->num))
558  prev = child;
559  }
560  }
561  }
562 
563  /* Find previous named workspace. */
564  if (!prev) {
565  bool found_current = false;
566  TAILQ_FOREACH_REVERSE (output, &(croot->nodes_head), nodes_head, nodes) {
567  /* Skip outputs starting with __, they are internal. */
568  if (con_is_internal(output))
569  continue;
571  if (child->type != CT_WORKSPACE)
572  continue;
573  if (child == current) {
574  found_current = true;
575  } else if (child->num == -1 && (current->num != -1 || found_current)) {
576  prev = child;
577  goto workspace_prev_end;
578  }
579  }
580  }
581  }
582 
583  /* Find last workspace. */
584  if (!prev) {
585  TAILQ_FOREACH_REVERSE (output, &(croot->nodes_head), nodes_head, nodes) {
586  /* Skip outputs starting with __, they are internal. */
587  if (con_is_internal(output))
588  continue;
590  if (child->type != CT_WORKSPACE)
591  continue;
592  if (!prev || child->num > prev->num)
593  prev = child;
594  }
595  }
596  }
597 
598 workspace_prev_end:
599  return prev;
600 }
601 
602 /*
603  * Focuses the next workspace on the same output.
604  *
605  */
607  Con *current = con_get_workspace(focused);
608  Con *next = NULL;
610 
611  if (current->num == -1) {
612  /* If currently a named workspace, find next named workspace. */
613  next = TAILQ_NEXT(current, nodes);
614  } else {
615  /* If currently a numbered workspace, find next numbered workspace. */
617  if (child->type != CT_WORKSPACE)
618  continue;
619  if (child->num == -1)
620  break;
621  /* Need to check child against current and next because we are
622  * traversing multiple lists and thus are not guaranteed the
623  * relative order between the list of workspaces. */
624  if (current->num < child->num && (!next || child->num < next->num))
625  next = child;
626  }
627  }
628 
629  /* Find next named workspace. */
630  if (!next) {
631  bool found_current = false;
633  if (child->type != CT_WORKSPACE)
634  continue;
635  if (child == current) {
636  found_current = 1;
637  } else if (child->num == -1 && (current->num != -1 || found_current)) {
638  next = child;
639  goto workspace_next_on_output_end;
640  }
641  }
642  }
643 
644  /* Find first workspace. */
645  if (!next) {
647  if (child->type != CT_WORKSPACE)
648  continue;
649  if (!next || (child->num != -1 && child->num < next->num))
650  next = child;
651  }
652  }
653 workspace_next_on_output_end:
654  return next;
655 }
656 
657 /*
658  * Focuses the previous workspace on same output.
659  *
660  */
662  Con *current = con_get_workspace(focused);
663  Con *prev = NULL;
665  DLOG("output = %s\n", output->name);
666 
667  if (current->num == -1) {
668  /* If named workspace, find previous named workspace. */
669  prev = TAILQ_PREV(current, nodes_head, nodes);
670  if (prev && prev->num != -1)
671  prev = NULL;
672  } else {
673  /* If numbered workspace, find previous numbered workspace. */
675  if (child->type != CT_WORKSPACE || child->num == -1)
676  continue;
677  /* Need to check child against current and previous because we
678  * are traversing multiple lists and thus are not guaranteed
679  * the relative order between the list of workspaces. */
680  if (current->num > child->num && (!prev || child->num > prev->num))
681  prev = child;
682  }
683  }
684 
685  /* Find previous named workspace. */
686  if (!prev) {
687  bool found_current = false;
689  if (child->type != CT_WORKSPACE)
690  continue;
691  if (child == current) {
692  found_current = true;
693  } else if (child->num == -1 && (current->num != -1 || found_current)) {
694  prev = child;
695  goto workspace_prev_on_output_end;
696  }
697  }
698  }
699 
700  /* Find last workspace. */
701  if (!prev) {
703  if (child->type != CT_WORKSPACE)
704  continue;
705  if (!prev || child->num > prev->num)
706  prev = child;
707  }
708  }
709 
710 workspace_prev_on_output_end:
711  return prev;
712 }
713 
714 /*
715  * Focuses the previously focused workspace.
716  *
717  */
720  DLOG("No previous workspace name set. Not switching.");
721  return;
722  }
723 
725 }
726 
727 /*
728  * Returns the previously focused workspace con, or NULL if unavailable.
729  *
730  */
733  DLOG("no previous workspace name set.");
734  return NULL;
735  }
736 
737  Con *workspace;
738  workspace = workspace_get(previous_workspace_name, NULL);
739 
740  return workspace;
741 }
742 
743 static bool get_urgency_flag(Con *con) {
744  Con *child;
745  TAILQ_FOREACH (child, &(con->nodes_head), nodes)
746  if (child->urgent || get_urgency_flag(child))
747  return true;
748 
749  TAILQ_FOREACH (child, &(con->floating_head), floating_windows)
750  if (child->urgent || get_urgency_flag(child))
751  return true;
752 
753  return false;
754 }
755 
756 /*
757  * Goes through all clients on the given workspace and updates the workspace’s
758  * urgent flag accordingly.
759  *
760  */
762  bool old_flag = ws->urgent;
763  ws->urgent = get_urgency_flag(ws);
764  DLOG("Workspace urgency flag changed from %d to %d\n", old_flag, ws->urgent);
765 
766  if (old_flag != ws->urgent)
767  ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"urgent\"}");
768 }
769 
770 /*
771  * 'Forces' workspace orientation by moving all cons into a new split-con with
772  * the same layout as the workspace and then changing the workspace layout.
773  *
774  */
775 void ws_force_orientation(Con *ws, orientation_t orientation) {
776  /* 1: create a new split container */
777  Con *split = con_new(NULL, NULL);
778  split->parent = ws;
779 
780  /* 2: copy layout from workspace */
781  split->layout = ws->layout;
782 
783  Con *old_focused = TAILQ_FIRST(&(ws->focus_head));
784 
785  /* 3: move the existing cons of this workspace below the new con */
786  DLOG("Moving cons\n");
787  while (!TAILQ_EMPTY(&(ws->nodes_head))) {
788  Con *child = TAILQ_FIRST(&(ws->nodes_head));
789  con_detach(child);
790  con_attach(child, split, true);
791  }
792 
793  /* 4: switch workspace layout */
794  ws->layout = (orientation == HORIZ) ? L_SPLITH : L_SPLITV;
795  DLOG("split->layout = %d, ws->layout = %d\n", split->layout, ws->layout);
796 
797  /* 5: attach the new split container to the workspace */
798  DLOG("Attaching new split (%p) to ws (%p)\n", split, ws);
799  con_attach(split, ws, false);
800 
801  /* 6: fix the percentages */
802  con_fix_percent(ws);
803 
804  if (old_focused)
805  con_focus(old_focused);
806 }
807 
808 /*
809  * Called when a new con (with a window, not an empty or split con) should be
810  * attached to the workspace (for example when managing a new window or when
811  * moving an existing window to the workspace level).
812  *
813  * Depending on the workspace_layout setting, this function either returns the
814  * workspace itself (default layout) or creates a new stacked/tabbed con and
815  * returns that.
816  *
817  */
819  DLOG("Attaching a window to workspace %p / %s\n", ws, ws->name);
820 
821  if (ws->workspace_layout == L_DEFAULT) {
822  DLOG("Default layout, just attaching it to the workspace itself.\n");
823  return ws;
824  }
825 
826  DLOG("Non-default layout, creating a new split container\n");
827  /* 1: create a new split container */
828  Con *new = con_new(NULL, NULL);
829  new->parent = ws;
830 
831  /* 2: set the requested layout on the split con */
832  new->layout = ws->workspace_layout;
833 
834  /* 4: attach the new split container to the workspace */
835  DLOG("Attaching new split %p to workspace %p\n", new, ws);
836  con_attach(new, ws, false);
837 
838  return new;
839 }
840 
848  if (TAILQ_EMPTY(&(ws->nodes_head))) {
849  ELOG("Workspace %p / %s has no children to encapsulate\n", ws, ws->name);
850  return NULL;
851  }
852 
853  Con *new = con_new(NULL, NULL);
854  new->parent = ws;
855  new->layout = ws->layout;
856 
857  DLOG("Moving children of workspace %p / %s into container %p\n",
858  ws, ws->name, new);
859 
860  Con *child;
861  while (!TAILQ_EMPTY(&(ws->nodes_head))) {
862  child = TAILQ_FIRST(&(ws->nodes_head));
863  con_detach(child);
864  con_attach(child, new, true);
865  }
866 
867  con_attach(new, ws, true);
868 
869  return new;
870 }
struct Con * parent
Definition: data.h:512
long ws_name_to_number(const char *name)
Parses the workspace name as a number.
Definition: util.c:76
char * sstrdup(const char *str)
Safe-wrapper around strdup which exits if malloc returns NULL (meaning that there is no more memory a...
void ipc_send_workspace_focus_event(Con *current, Con *old)
For the workspace &quot;focus&quot; event we send, along the usual &quot;change&quot; field, also the current and previou...
Definition: ipc.c:1058
bool mapped
Definition: data.h:480
bool urgent
Definition: data.h:484
char * name
Name of the output.
Definition: data.h:316
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:507
Con * workspace_attach_to(Con *ws)
Called when a new con (with a window, not an empty or split con) should be attached to the workspace ...
Definition: workspace.c:818
void con_attach(Con *con, Con *parent, bool ignore_focus)
Attaches the given container to the given parent.
Definition: con.c:103
Config config
Definition: config.c:19
Con * workspace_encapsulate(Con *ws)
Creates a new container and re-parents all of children from the given workspace into it...
Definition: workspace.c:847
char * sticky_group
Definition: data.h:525
Con * con_get_workspace(Con *con)
Gets the workspace container this node is on.
Definition: con.c:317
Stores which workspace (by name or number) goes to which output.
Definition: data.h:172
Con * workspace_next(void)
Returns the next workspace.
Definition: workspace.c:465
#define LOG(fmt,...)
Definition: libi3.h:76
bool workspace_is_visible(Con *ws)
Returns true if the workspace is currently visible.
Definition: workspace.c:232
void con_detach(Con *con)
Detaches the given container from its current parent.
Definition: con.c:197
Con * workspace_prev_on_output(void)
Returns the previous workspace on the same output.
Definition: workspace.c:661
void workspace_show(Con *workspace)
Switches to the given workspace.
Definition: workspace.c:447
Con * con_descend_focused(Con *con)
Returns the focused con inside this client, descending the tree as far as possible.
Definition: con.c:983
struct Rect rect
Definition: data.h:514
#define TAILQ_FOREACH_REVERSE(var, head, headname, field)
Definition: queue.h:339
struct ev_loop * main_loop
Definition: main.c:69
#define TAILQ_FIRST(head)
Definition: queue.h:323
float workspace_urgency_timer
By default, urgency is cleared immediately when switching to another workspace leads to focusing the ...
Definition: config.h:168
struct Window * window
Definition: data.h:547
void x_reparent_child(Con *con, Con *old)
Reparents the child window of the given container (necessary for sticky containers).
Definition: x.c:186
static void _workspace_show(Con *workspace)
Definition: workspace.c:338
Con * create_workspace_on_output(Output *output, Con *content)
Definition: workspace.c:110
An Output is a physical output on your graphics driver.
Definition: data.h:301
#define TAILQ_NEXT(elm, field)
Definition: queue.h:325
Con * con_new(Con *parent, i3Window *window)
Definition: con.c:87
bool con_is_internal(Con *con)
Returns true if the container is internal, such as __i3_scratch.
Definition: con.c:411
void workspace_show_by_name(const char *num)
Looks up the workspace by name and switches to it.
Definition: workspace.c:455
Holds a keybinding, consisting of a keycode combined with modifiers and the command which is executed...
Definition: data.h:231
void x_set_warp_to(Rect *rect)
Set warp_to coordinates.
Definition: x.c:1142
void ipc_send_event(const char *event, uint32_t message_type, const char *payload)
Sends the specified event to all IPC clients which are currently connected and subscribed to this kin...
Definition: ipc.c:75
int default_orientation
Default orientation for new containers.
Definition: config.h:104
static void _workspace_apply_default_orientation(Con *ws)
Definition: workspace.c:25
enum Con::@18 type
void con_update_parents_urgency(Con *con)
Make all parent containers urgent if con is urgent or clear the urgent flag of all parent containers ...
Definition: con.c:1554
Definition: data.h:85
#define DLOG(fmt,...)
Definition: libi3.h:86
static bool get_urgency_flag(Con *con)
Definition: workspace.c:743
Con * workspace_back_and_forth_get(void)
Returns the previously focused workspace con, or NULL if unavailable.
Definition: workspace.c:731
char * command
Command, like in command mode.
Definition: data.h:272
Con * _get_sticky(Con *con, const char *sticky_group, Con *exclude)
Definition: workspace.c:245
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...
Con * con_get_fullscreen_con(Con *con, fullscreen_mode_t fullscreen_mode)
Returns the first fullscreen node below this node.
Definition: con.c:365
bool tree_close(Con *con, kill_window_t kill_window, bool dont_kill_parent, bool force_set_focus)
Closes the given container including all children.
Definition: tree.c:190
void workspace_back_and_forth(void)
Focuses the previously focused workspace.
Definition: workspace.c:718
Definition: data.h:473
void x_move_win(Con *src, Con *dest)
Moves a child window from Container src to Container dest.
Definition: x.c:201
uint32_t height
Definition: data.h:126
Con * focused
Definition: tree.c:15
int num
the workspace number, if this Con is of type CT_WORKSPACE and the workspace is not a named workspace ...
Definition: data.h:510
char * name
Definition: data.h:520
fullscreen_mode_t fullscreen_mode
Definition: data.h:563
Definition: data.h:91
void ewmh_update_current_desktop(void)
Updates _NET_CURRENT_DESKTOP with the current desktop number.
Definition: ewmh.c:21
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:1100
void con_focus(Con *con)
Sets input focus to the given container.
Definition: con.c:213
#define TAILQ_EMPTY(head)
Definition: queue.h:331
void workspace_update_urgent_flag(Con *ws)
Goes through all clients on the given workspace and updates the workspace’s urgent flag accordingly...
Definition: workspace.c:761
void ws_force_orientation(Con *ws, orientation_t orientation)
&#39;Forces&#39; workspace orientation by moving all cons into a new split-con with the same orientation as t...
Definition: workspace.c:775
#define TAILQ_PREV(elm, headname, field)
Definition: queue.h:329
#define ELOG(fmt,...)
Definition: libi3.h:81
Definition: data.h:55
#define GREP_FIRST(dest, head, condition)
Definition: util.h:37
layout_t layout
Definition: data.h:578
A &#39;Con&#39; represents everything from the X11 root window down to a single X11 window.
Definition: data.h:479
void * scalloc(size_t size)
Safe-wrapper around calloc which exits if malloc returns NULL (meaning that there is no more memory a...
struct bindings_head * bindings
Definition: main.c:80
Con * workspace_get(const char *num, bool *created)
Returns a pointer to the workspace with the given number (starting at 0), creating the workspace if n...
Definition: workspace.c:44
layout_t workspace_layout
Definition: data.h:578
orientation_t
Definition: data.h:55
Con * workspace_prev(void)
Returns the previous workspace.
Definition: workspace.c:535
Con * workspace_next_on_output(void)
Returns the next workspace on the same output.
Definition: workspace.c:606
struct Con * croot
Definition: tree.c:14
static void workspace_defer_update_urgent_hint_cb(EV_P_ ev_timer *w, int revents)
Definition: workspace.c:325
static void workspace_reassign_sticky(Con *con)
Definition: workspace.c:282
Con * output_get_content(Con *output)
Returns the output container below the given output container.
Definition: output.c:18
Con * con_get_output(Con *con)
Gets the output container (first container with CT_OUTPUT in hierarchy) this node is on...
Definition: con.c:303
layout_t default_layout
Definition: config.h:97
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:334
void con_fix_percent(Con *con)
Updates the percent attribute of the children of the given container.
Definition: con.c:542
static char * previous_workspace_name
Definition: workspace.c:17
#define NODES_FOREACH_REVERSE(head)
Definition: util.h:32
#define FREE(pointer)
Definition: util.h:46
struct ws_assignments_head ws_assignments
Definition: main.c:93
#define NODES_FOREACH(head)
Definition: util.h:28
uint32_t width
Definition: data.h:125
Definition: data.h:90
struct ev_timer * urgency_timer
Definition: data.h:550