]> git.sur5r.net Git - i3/i3/blob - src/workspace.c
Merge branch 'master' into next
[i3/i3] / src / workspace.c
1 /*
2  * vim:ts=4:sw=4:expandtab
3  *
4  * i3 - an improved dynamic tiling window manager
5  * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
6  *
7  * workspace.c: Modifying workspaces, accessing them, moving containers to
8  *              workspaces.
9  *
10  */
11 #include "all.h"
12
13 /* Stores a copy of the name of the last used workspace for the workspace
14  * back-and-forth switching. */
15 static char *previous_workspace_name = NULL;
16
17 /*
18  * Returns a pointer to the workspace with the given number (starting at 0),
19  * creating the workspace if necessary (by allocating the necessary amount of
20  * memory and initializing the data structures correctly).
21  *
22  */
23 Con *workspace_get(const char *num, bool *created) {
24     Con *output, *workspace = NULL;
25
26     TAILQ_FOREACH(output, &(croot->nodes_head), nodes)
27         GREP_FIRST(workspace, output_get_content(output), !strcasecmp(child->name, num));
28
29     if (workspace == NULL) {
30         LOG("Creating new workspace \"%s\"\n", num);
31         /* unless an assignment is found, we will create this workspace on the current output */
32         output = con_get_output(focused);
33         /* look for assignments */
34         struct Workspace_Assignment *assignment;
35         TAILQ_FOREACH(assignment, &ws_assignments, ws_assignments) {
36             if (strcmp(assignment->name, num) != 0)
37                 continue;
38
39             LOG("Found workspace assignment to output \"%s\"\n", assignment->output);
40             GREP_FIRST(output, croot, !strcmp(child->name, assignment->output));
41             break;
42         }
43         Con *content = output_get_content(output);
44         LOG("got output %p with content %p\n", output, content);
45         /* We need to attach this container after setting its type. con_attach
46          * will handle CT_WORKSPACEs differently */
47         workspace = con_new(NULL, NULL);
48         char *name;
49         sasprintf(&name, "[i3 con] workspace %s", num);
50         x_set_name(workspace, name);
51         free(name);
52         workspace->type = CT_WORKSPACE;
53         FREE(workspace->name);
54         workspace->name = sstrdup(num);
55         /* We set ->num to the number if this workspace’s name begins with a
56          * positive number. Otherwise it’s a named ws and num will be -1. */
57         char *endptr = NULL;
58         long parsed_num = strtol(num, &endptr, 10);
59         if (parsed_num == LONG_MIN ||
60             parsed_num == LONG_MAX ||
61             parsed_num < 0 ||
62             endptr == num)
63             workspace->num = -1;
64         else workspace->num = parsed_num;
65         LOG("num = %d\n", workspace->num);
66
67         /* If default_orientation is set to NO_ORIENTATION we
68          * determine workspace orientation from workspace size.
69          * Otherwise we just set the orientation to default_orientation. */
70         if (config.default_orientation == NO_ORIENTATION) {
71             workspace->orientation = (output->rect.height > output->rect.width) ? VERT : HORIZ;
72             DLOG("Auto orientation. Output resolution set to (%d,%d), setting orientation to %d.\n",
73                  workspace->rect.width, workspace->rect.height, workspace->orientation);
74         } else {
75             workspace->orientation = config.default_orientation;
76         }
77
78         con_attach(workspace, content, false);
79
80         ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"init\"}");
81         if (created != NULL)
82             *created = true;
83     }
84     else if (created != NULL) {
85         *created = false;
86     }
87
88     return workspace;
89 }
90
91 /*
92  * Returns a pointer to a new workspace in the given output. The workspace
93  * is created attached to the tree hierarchy through the given content
94  * container.
95  *
96  */
97 Con *create_workspace_on_output(Output *output, Con *content) {
98     /* add a workspace to this output */
99     Con *out, *current;
100     char *name;
101     bool exists = true;
102     Con *ws = con_new(NULL, NULL);
103     ws->type = CT_WORKSPACE;
104
105     /* try the configured workspace bindings first to find a free name */
106     Binding *bind;
107     TAILQ_FOREACH(bind, bindings, bindings) {
108         DLOG("binding with command %s\n", bind->command);
109         if (strlen(bind->command) < strlen("workspace ") ||
110             strncasecmp(bind->command, "workspace", strlen("workspace")) != 0)
111             continue;
112         DLOG("relevant command = %s\n", bind->command);
113         char *target = bind->command + strlen("workspace ");
114         /* We check if this is the workspace
115          * next/prev/next_on_output/prev_on_output/back_and_forth/number command.
116          * Beware: The workspace names "next", "prev", "next_on_output",
117          * "prev_on_output", "number", "back_and_forth" and "current" are OK,
118          * so we check before stripping the double quotes */
119         if (strncasecmp(target, "next", strlen("next")) == 0 ||
120             strncasecmp(target, "prev", strlen("prev")) == 0 ||
121             strncasecmp(target, "next_on_output", strlen("next_on_output")) == 0 ||
122             strncasecmp(target, "prev_on_output", strlen("prev_on_output")) == 0 ||
123             strncasecmp(target, "number", strlen("number")) == 0 ||
124             strncasecmp(target, "back_and_forth", strlen("back_and_forth")) == 0 ||
125             strncasecmp(target, "current", strlen("current")) == 0)
126             continue;
127         if (*target == '"')
128             target++;
129         FREE(ws->name);
130         ws->name = strdup(target);
131         if (ws->name[strlen(ws->name)-1] == '"')
132             ws->name[strlen(ws->name)-1] = '\0';
133         DLOG("trying name *%s*\n", ws->name);
134
135         /* Ensure that this workspace is not assigned to a different output —
136          * otherwise we would create it, then move it over to its output, then
137          * find a new workspace, etc… */
138         bool assigned = false;
139         struct Workspace_Assignment *assignment;
140         TAILQ_FOREACH(assignment, &ws_assignments, ws_assignments) {
141             if (strcmp(assignment->name, ws->name) != 0 ||
142                 strcmp(assignment->output, output->name) == 0)
143                 continue;
144
145             assigned = true;
146             break;
147         }
148
149         if (assigned)
150             continue;
151
152         current = NULL;
153         TAILQ_FOREACH(out, &(croot->nodes_head), nodes)
154             GREP_FIRST(current, output_get_content(out), !strcasecmp(child->name, ws->name));
155
156         exists = (current != NULL);
157         if (!exists) {
158             /* Set ->num to the number of the workspace, if the name actually
159              * is a number or starts with a number */
160             char *endptr = NULL;
161             long parsed_num = strtol(ws->name, &endptr, 10);
162             if (parsed_num == LONG_MIN ||
163                 parsed_num == LONG_MAX ||
164                 parsed_num < 0 ||
165                 endptr == ws->name)
166                 ws->num = -1;
167             else ws->num = parsed_num;
168             LOG("Used number %d for workspace with name %s\n", ws->num, ws->name);
169
170             break;
171         }
172     }
173
174     if (exists) {
175         /* get the next unused workspace number */
176         DLOG("Getting next unused workspace by number\n");
177         int c = 0;
178         while (exists) {
179             c++;
180
181             FREE(ws->name);
182             sasprintf(&(ws->name), "%d", c);
183
184             current = NULL;
185             TAILQ_FOREACH(out, &(croot->nodes_head), nodes)
186                 GREP_FIRST(current, output_get_content(out), !strcasecmp(child->name, ws->name));
187             exists = (current != NULL);
188
189             DLOG("result for ws %s / %d: exists = %d\n", ws->name, c, exists);
190         }
191         ws->num = c;
192     }
193     con_attach(ws, content, false);
194
195     sasprintf(&name, "[i3 con] workspace %s", ws->name);
196     x_set_name(ws, name);
197     free(name);
198
199     ws->fullscreen_mode = CF_OUTPUT;
200
201     /* If default_orientation is set to NO_ORIENTATION we determine
202      * orientation depending on output resolution. */
203     if (config.default_orientation == NO_ORIENTATION) {
204         ws->orientation = (output->rect.height > output->rect.width) ? VERT : HORIZ;
205         DLOG("Auto orientation. Workspace size set to (%d,%d), setting orientation to %d.\n",
206              output->rect.width, output->rect.height, ws->orientation);
207     } else {
208         ws->orientation = config.default_orientation;
209     }
210
211     return ws;
212 }
213
214 /*
215  * Returns true if the workspace is currently visible. Especially important for
216  * multi-monitor environments, as they can have multiple currenlty active
217  * workspaces.
218  *
219  */
220 bool workspace_is_visible(Con *ws) {
221     Con *output = con_get_output(ws);
222     if (output == NULL)
223         return false;
224     Con *fs = con_get_fullscreen_con(output, CF_OUTPUT);
225     LOG("workspace visible? fs = %p, ws = %p\n", fs, ws);
226     return (fs == ws);
227 }
228
229 /*
230  * XXX: we need to clean up all this recursive walking code.
231  *
232  */
233 Con *_get_sticky(Con *con, const char *sticky_group, Con *exclude) {
234     Con *current;
235
236     TAILQ_FOREACH(current, &(con->nodes_head), nodes) {
237         if (current != exclude &&
238             current->sticky_group != NULL &&
239             current->window != NULL &&
240             strcmp(current->sticky_group, sticky_group) == 0)
241             return current;
242
243         Con *recurse = _get_sticky(current, sticky_group, exclude);
244         if (recurse != NULL)
245             return recurse;
246     }
247
248     TAILQ_FOREACH(current, &(con->floating_head), floating_windows) {
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     return NULL;
261 }
262
263 /*
264  * Reassigns all child windows in sticky containers. Called when the user
265  * changes workspaces.
266  *
267  * XXX: what about sticky containers which contain containers?
268  *
269  */
270 static void workspace_reassign_sticky(Con *con) {
271     Con *current;
272     /* 1: go through all containers */
273
274     /* handle all children and floating windows of this node */
275     TAILQ_FOREACH(current, &(con->nodes_head), nodes) {
276         if (current->sticky_group == NULL) {
277             workspace_reassign_sticky(current);
278             continue;
279         }
280
281         LOG("Ah, this one is sticky: %s / %p\n", current->name, current);
282         /* 2: find a window which we can re-assign */
283         Con *output = con_get_output(current);
284         Con *src = _get_sticky(output, current->sticky_group, current);
285
286         if (src == NULL) {
287             LOG("No window found for this sticky group\n");
288             workspace_reassign_sticky(current);
289             continue;
290         }
291
292         x_move_win(src, current);
293         current->window = src->window;
294         current->mapped = true;
295         src->window = NULL;
296         src->mapped = false;
297
298         x_reparent_child(current, src);
299
300         LOG("re-assigned window from src %p to dest %p\n", src, current);
301     }
302
303     TAILQ_FOREACH(current, &(con->floating_head), floating_windows)
304         workspace_reassign_sticky(current);
305 }
306
307
308 static void _workspace_show(Con *workspace) {
309     Con *current, *old = NULL;
310
311     /* safe-guard against showing i3-internal workspaces like __i3_scratch */
312     if (workspace->name[0] == '_' && workspace->name[1] == '_')
313         return;
314
315     /* disable fullscreen for the other workspaces and get the workspace we are
316      * currently on. */
317     TAILQ_FOREACH(current, &(workspace->parent->nodes_head), nodes) {
318         if (current->fullscreen_mode == CF_OUTPUT)
319             old = current;
320         current->fullscreen_mode = CF_NONE;
321     }
322
323     /* enable fullscreen for the target workspace. If it happens to be the
324      * same one we are currently on anyways, we can stop here. */
325     workspace->fullscreen_mode = CF_OUTPUT;
326     current = con_get_workspace(focused);
327     if (workspace == current) {
328         DLOG("Not switching, already there.\n");
329         return;
330     }
331
332     /* Remember currently focused workspace for switching back to it later with
333      * the 'workspace back_and_forth' command.
334      * NOTE: We have to duplicate the name as the original will be freed when
335      * the corresponding workspace is cleaned up. */
336
337     FREE(previous_workspace_name);
338     if (current)
339         previous_workspace_name = sstrdup(current->name);
340
341     workspace_reassign_sticky(workspace);
342
343     LOG("switching to %p\n", workspace);
344     Con *next = con_descend_focused(workspace);
345
346     if (old && TAILQ_EMPTY(&(old->nodes_head)) && TAILQ_EMPTY(&(old->floating_head))) {
347         /* check if this workspace is currently visible */
348         if (!workspace_is_visible(old)) {
349             LOG("Closing old workspace (%p / %s), it is empty\n", old, old->name);
350             tree_close(old, DONT_KILL_WINDOW, false, false);
351             ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"empty\"}");
352         }
353     }
354
355     /* Memorize current output */
356     Con *old_output = con_get_output(focused);
357
358     con_focus(next);
359     workspace->fullscreen_mode = CF_OUTPUT;
360     LOG("focused now = %p / %s\n", focused, focused->name);
361
362     /* Set mouse pointer */
363     Con *new_output = con_get_output(focused);
364     if (old_output != new_output) {
365         x_set_warp_to(&next->rect);
366     }
367
368     /* Update the EWMH hints */
369     ewmh_update_current_desktop();
370
371     ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"focus\"}");
372 }
373
374 /*
375  * Switches to the given workspace
376  *
377  */
378 void workspace_show(Con *workspace) {
379     _workspace_show(workspace);
380 }
381
382 /*
383  * Looks up the workspace by name and switches to it.
384  *
385  */
386 void workspace_show_by_name(const char *num) {
387     Con *workspace;
388     bool changed_num_workspaces;
389     workspace = workspace_get(num, &changed_num_workspaces);
390     _workspace_show(workspace);
391 }
392
393 /*
394  * Focuses the next workspace.
395  *
396  */
397 Con* workspace_next(void) {
398     Con *current = con_get_workspace(focused);
399     Con *next = NULL;
400     Con *output;
401
402     if (current->num == -1) {
403         /* If currently a named workspace, find next named workspace. */
404         next = TAILQ_NEXT(current, nodes);
405     } else {
406         /* If currently a numbered workspace, find next numbered workspace. */
407         TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
408             /* Skip outputs starting with __, they are internal. */
409             if (output->name[0] == '_' && output->name[1] == '_')
410                 continue;
411             NODES_FOREACH(output_get_content(output)) {
412                 if (child->type != CT_WORKSPACE)
413                     continue;
414                 if (child->num == -1)
415                     break;
416                 /* Need to check child against current and next because we are
417                  * traversing multiple lists and thus are not guaranteed the
418                  * relative order between the list of workspaces. */
419                 if (current->num < child->num && (!next || child->num < next->num))
420                     next = child;
421             }
422         }
423     }
424
425     /* Find next named workspace. */
426     if (!next) {
427         bool found_current = false;
428         TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
429             /* Skip outputs starting with __, they are internal. */
430             if (output->name[0] == '_' && output->name[1] == '_')
431                 continue;
432             NODES_FOREACH(output_get_content(output)) {
433                 if (child->type != CT_WORKSPACE)
434                     continue;
435                 if (child == current) {
436                     found_current = 1;
437                 } else if (child->num == -1 && (current->num != -1 || found_current)) {
438                     next = child;
439                     goto workspace_next_end;
440                 }
441             }
442         }
443     }
444
445     /* Find first workspace. */
446     if (!next) {
447         TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
448             /* Skip outputs starting with __, they are internal. */
449             if (output->name[0] == '_' && output->name[1] == '_')
450                 continue;
451             NODES_FOREACH(output_get_content(output)) {
452                 if (child->type != CT_WORKSPACE)
453                     continue;
454                 if (!next || (child->num != -1 && child->num < next->num))
455                     next = child;
456             }
457         }
458     }
459 workspace_next_end:
460     return next;
461 }
462
463 /*
464  * Focuses the previous workspace.
465  *
466  */
467 Con* workspace_prev(void) {
468     Con *current = con_get_workspace(focused);
469     Con *prev = NULL;
470     Con *output;
471
472     if (current->num == -1) {
473         /* If named workspace, find previous named workspace. */
474         prev = TAILQ_PREV(current, nodes_head, nodes);
475         if (prev && prev->num != -1)
476             prev = NULL;
477     } else {
478         /* If numbered workspace, find previous numbered workspace. */
479         TAILQ_FOREACH_REVERSE(output, &(croot->nodes_head), nodes_head, nodes) {
480             /* Skip outputs starting with __, they are internal. */
481             if (output->name[0] == '_' && output->name[1] == '_')
482                 continue;
483             NODES_FOREACH_REVERSE(output_get_content(output)) {
484                 if (child->type != CT_WORKSPACE || child->num == -1)
485                     continue;
486                 /* Need to check child against current and previous because we
487                  * are traversing multiple lists and thus are not guaranteed
488                  * the relative order between the list of workspaces. */
489                 if (current->num > child->num && (!prev || child->num > prev->num))
490                     prev = child;
491             }
492         }
493     }
494
495     /* Find previous named workspace. */
496     if (!prev) {
497         bool found_current = false;
498         TAILQ_FOREACH_REVERSE(output, &(croot->nodes_head), nodes_head, nodes) {
499             /* Skip outputs starting with __, they are internal. */
500             if (output->name[0] == '_' && output->name[1] == '_')
501                 continue;
502             NODES_FOREACH_REVERSE(output_get_content(output)) {
503                 if (child->type != CT_WORKSPACE)
504                     continue;
505                 if (child == current) {
506                     found_current = true;
507                 } else if (child->num == -1 && (current->num != -1 || found_current)) {
508                     prev = child;
509                     goto workspace_prev_end;
510                 }
511             }
512         }
513     }
514
515     /* Find last workspace. */
516     if (!prev) {
517         TAILQ_FOREACH_REVERSE(output, &(croot->nodes_head), nodes_head, nodes) {
518             /* Skip outputs starting with __, they are internal. */
519             if (output->name[0] == '_' && output->name[1] == '_')
520                 continue;
521             NODES_FOREACH_REVERSE(output_get_content(output)) {
522                 if (child->type != CT_WORKSPACE)
523                     continue;
524                 if (!prev || child->num > prev->num)
525                     prev = child;
526             }
527         }
528     }
529
530 workspace_prev_end:
531     return prev;
532 }
533
534
535 /*
536  * Focuses the next workspace on the same output.
537  *
538  */
539 Con* workspace_next_on_output(void) {
540     Con *current = con_get_workspace(focused);
541     Con *next = NULL;
542     Con *output  = con_get_output(focused);
543
544     if (current->num == -1) {
545         /* If currently a named workspace, find next named workspace. */
546         next = TAILQ_NEXT(current, nodes);
547     } else {
548         /* If currently a numbered workspace, find next numbered workspace. */
549         NODES_FOREACH(output_get_content(output)) {
550             if (child->type != CT_WORKSPACE)
551                 continue;
552             if (child->num == -1)
553                 break;
554             /* Need to check child against current and next because we are
555              * traversing multiple lists and thus are not guaranteed the
556              * relative order between the list of workspaces. */
557             if (current->num < child->num && (!next || child->num < next->num))
558                 next = child;
559             }
560         }
561
562     /* Find next named workspace. */
563     if (!next) {
564         bool found_current = false;
565         NODES_FOREACH(output_get_content(output)) {
566             if (child->type != CT_WORKSPACE)
567                 continue;
568             if (child == current) {
569                 found_current = 1;
570             } else if (child->num == -1 && (current->num != -1 || found_current)) {
571                 next = child;
572                 goto workspace_next_on_output_end;
573             }
574         }
575     }
576
577     /* Find first workspace. */
578     if (!next) {
579         NODES_FOREACH(output_get_content(output)) {
580             if (child->type != CT_WORKSPACE)
581                 continue;
582             if (!next || (child->num != -1 && child->num < next->num))
583                 next = child;
584         }
585     }
586 workspace_next_on_output_end:
587     return next;
588 }
589
590 /*
591  * Focuses the previous workspace on same output.
592  *
593  */
594 Con* workspace_prev_on_output(void) {
595     Con *current = con_get_workspace(focused);
596     Con *prev = NULL;
597     Con *output  = con_get_output(focused);
598     DLOG("output = %s\n", output->name);
599
600     if (current->num == -1) {
601         /* If named workspace, find previous named workspace. */
602         prev = TAILQ_PREV(current, nodes_head, nodes);
603         if (prev && prev->num != -1)
604             prev = NULL;
605     } else {
606         /* If numbered workspace, find previous numbered workspace. */
607         NODES_FOREACH_REVERSE(output_get_content(output)) {
608             if (child->type != CT_WORKSPACE || child->num == -1)
609                 continue;
610              /* Need to check child against current and previous because we
611              * are traversing multiple lists and thus are not guaranteed
612              * the relative order between the list of workspaces. */
613             if (current->num > child->num && (!prev || child->num > prev->num))
614                 prev = child;
615         }
616     }
617
618     /* Find previous named workspace. */
619     if (!prev) {
620         bool found_current = false;
621         NODES_FOREACH_REVERSE(output_get_content(output)) {
622             if (child->type != CT_WORKSPACE)
623                 continue;
624             if (child == current) {
625                 found_current = true;
626             } else if (child->num == -1 && (current->num != -1 || found_current)) {
627                 prev = child;
628                 goto workspace_prev_on_output_end;
629             }
630         }
631     }
632
633     /* Find last workspace. */
634     if (!prev) {
635         NODES_FOREACH_REVERSE(output_get_content(output)) {
636             if (child->type != CT_WORKSPACE)
637                 continue;
638             if (!prev || child->num > prev->num)
639                 prev = child;
640         }
641     }
642
643 workspace_prev_on_output_end:
644     return prev;
645 }
646
647 /*
648  * Focuses the previously focused workspace.
649  *
650  */
651 void workspace_back_and_forth(void) {
652     if (!previous_workspace_name) {
653         DLOG("No previous workspace name set. Not switching.");
654         return;
655     }
656
657     workspace_show_by_name(previous_workspace_name);
658 }
659
660 static bool get_urgency_flag(Con *con) {
661     Con *child;
662     TAILQ_FOREACH(child, &(con->nodes_head), nodes)
663         if (child->urgent || get_urgency_flag(child))
664             return true;
665
666     TAILQ_FOREACH(child, &(con->floating_head), floating_windows)
667         if (child->urgent || get_urgency_flag(child))
668             return true;
669
670     return false;
671 }
672
673 /*
674  * Goes through all clients on the given workspace and updates the workspace’s
675  * urgent flag accordingly.
676  *
677  */
678 void workspace_update_urgent_flag(Con *ws) {
679     bool old_flag = ws->urgent;
680     ws->urgent = get_urgency_flag(ws);
681     DLOG("Workspace urgency flag changed from %d to %d\n", old_flag, ws->urgent);
682
683     if (old_flag != ws->urgent)
684         ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"urgent\"}");
685 }
686
687 /*
688  * 'Forces' workspace orientation by moving all cons into a new split-con with
689  * the same orientation as the workspace and then changing the workspace
690  * orientation.
691  *
692  */
693 void ws_force_orientation(Con *ws, orientation_t orientation) {
694     /* 1: create a new split container */
695     Con *split = con_new(NULL, NULL);
696     split->parent = ws;
697
698     /* 2: copy layout and orientation from workspace */
699     split->layout = ws->layout;
700     split->orientation = ws->orientation;
701
702     Con *old_focused = TAILQ_FIRST(&(ws->focus_head));
703
704     /* 3: move the existing cons of this workspace below the new con */
705     DLOG("Moving cons\n");
706     while (!TAILQ_EMPTY(&(ws->nodes_head))) {
707         Con *child = TAILQ_FIRST(&(ws->nodes_head));
708         con_detach(child);
709         con_attach(child, split, true);
710     }
711
712     /* 4: switch workspace orientation */
713     ws->orientation = orientation;
714
715     /* 5: attach the new split container to the workspace */
716     DLOG("Attaching new split to ws\n");
717     con_attach(split, ws, false);
718
719     /* 6: fix the percentages */
720     con_fix_percent(ws);
721
722     if (old_focused)
723         con_focus(old_focused);
724 }
725
726 /*
727  * Called when a new con (with a window, not an empty or split con) should be
728  * attached to the workspace (for example when managing a new window or when
729  * moving an existing window to the workspace level).
730  *
731  * Depending on the workspace_layout setting, this function either returns the
732  * workspace itself (default layout) or creates a new stacked/tabbed con and
733  * returns that.
734  *
735  */
736 Con *workspace_attach_to(Con *ws) {
737     DLOG("Attaching a window to workspace %p / %s\n", ws, ws->name);
738
739     if (config.default_layout == L_DEFAULT) {
740         DLOG("Default layout, just attaching it to the workspace itself.\n");
741         return ws;
742     }
743
744     DLOG("Non-default layout, creating a new split container\n");
745     /* 1: create a new split container */
746     Con *new = con_new(NULL, NULL);
747     new->parent = ws;
748
749     /* 2: set the requested layout on the split con */
750     new->layout = config.default_layout;
751
752     /* 3: While the layout is irrelevant in stacked/tabbed mode, it needs
753      * to be set. Otherwise, this con will not be interpreted as a split
754      * container. */
755     if (config.default_orientation == NO_ORIENTATION) {
756         new->orientation = (ws->rect.height > ws->rect.width) ? VERT : HORIZ;
757     } else {
758         new->orientation = config.default_orientation;
759     }
760
761     /* 4: attach the new split container to the workspace */
762     DLOG("Attaching new split %p to workspace %p\n", new, ws);
763     con_attach(new, ws, false);
764
765     return new;
766 }