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