]> git.sur5r.net Git - i3/i3/blob - src/tree.c
first step of the big refactoring ("tree" branch).
[i3/i3] / src / tree.c
1 /*
2  * vim:ts=4:sw=4:expandtab
3  */
4
5 #include "all.h"
6
7 struct Con *croot;
8 struct Con *focused;
9
10 struct all_cons_head all_cons = TAILQ_HEAD_INITIALIZER(all_cons);
11
12 /*
13  * Sets input focus to the given container. Will be updated in X11 in the next
14  * run of x_push_changes().
15  *
16  */
17 void con_focus(Con *con) {
18     assert(con != NULL);
19
20     /* 1: set focused-pointer to the new con */
21     /* 2: exchange the position of the container in focus stack of the parent all the way up */
22     TAILQ_REMOVE(&(con->parent->focus_head), con, focused);
23     TAILQ_INSERT_HEAD(&(con->parent->focus_head), con, focused);
24     if (con->parent->parent != NULL)
25         con_focus(con->parent);
26
27     focused = con;
28 }
29
30 /*
31  * Loads tree from ~/.i3/_restart.json
32  *
33  */
34 bool tree_restore() {
35     char *globbed = glob_path("~/.i3/_restart.json");
36
37     if (!path_exists(globbed)) {
38         LOG("%s does not exist, not restoring tree\n", globbed);
39         free(globbed);
40         return false;
41     }
42
43     /* TODO: refactor the following */
44     croot = con_new(NULL);
45     focused = croot;
46
47     tree_append_json(globbed);
48     char *old_restart = glob_path("~/.i3/_restart.json.old");
49     unlink(old_restart);
50     rename(globbed, old_restart);
51     free(globbed);
52     free(old_restart);
53
54     printf("appended tree, using new root\n");
55     croot = TAILQ_FIRST(&(croot->nodes_head));
56     printf("new root = %p\n", croot);
57     Con *out = TAILQ_FIRST(&(croot->nodes_head));
58     printf("out = %p\n", out);
59     Con *ws = TAILQ_FIRST(&(out->nodes_head));
60     printf("ws = %p\n", ws);
61     con_focus(ws);
62
63     return true;
64 }
65
66 /*
67  * Initializes the tree by creating the root node, adding all RandR outputs
68  * to the tree (that means randr_init() has to be called before) and
69  * assigning a workspace to each RandR output.
70  *
71  */
72 void tree_init() {
73     Output *output;
74
75     croot = con_new(NULL);
76     croot->name = "root";
77     croot->type = CT_ROOT;
78
79     Con *ws;
80     /* add the outputs */
81     TAILQ_FOREACH(output, &outputs, outputs) {
82         if (!output->active)
83             continue;
84
85         Con *oc = con_new(croot);
86         oc->name = strdup(output->name);
87         oc->type = CT_OUTPUT;
88         oc->rect = output->rect;
89
90         /* add a workspace to this output */
91         ws = con_new(oc);
92         ws->name = strdup("1");
93         ws->fullscreen_mode = CF_OUTPUT;
94     }
95
96     con_focus(ws);
97 }
98
99 /*
100  * Opens an empty container in the current container
101  *
102  */
103 Con *tree_open_con(Con *con) {
104     if (con == NULL) {
105         /* every focusable Con has a parent (outputs have parent root) */
106         con = focused->parent;
107         /* If the parent is an output, we are on a workspace. In this case,
108          * the new container needs to be opened as a leaf of the workspace. */
109         if (con->type == CT_OUTPUT)
110             con = focused;
111     }
112
113     assert(con != NULL);
114
115     /* 3: re-calculate child->percent for each child */
116     con_fix_percent(con, WINDOW_ADD);
117
118     /* 4: add a new container leaf to this con */
119     Con *new = con_new(con);
120     con_focus(new);
121
122     return new;
123 }
124
125 /*
126  * Closes the given container including all children
127  *
128  */
129 void tree_close(Con *con) {
130     /* TODO: check floating clients and adjust old_parent if necessary */
131
132     /* Get the container which is next focused */
133     Con *next;
134     if (con->type == CT_FLOATING_CON) {
135         next = TAILQ_NEXT(con, floating_windows);
136         if (next == TAILQ_END(&(con->parent->floating_head)))
137             next = con->parent;
138     } else {
139         next = TAILQ_NEXT(con, focused);
140         if (next == TAILQ_END(&(con->parent->nodes_head)))
141             next = con->parent;
142     }
143
144     LOG("closing %p\n", con);
145     Con *child;
146     /* We cannot use TAILQ_FOREACH because the children get deleted
147      * in their parent’s nodes_head */
148     while (!TAILQ_EMPTY(&(con->nodes_head))) {
149         child = TAILQ_FIRST(&(con->nodes_head));
150         tree_close(child);
151     }
152
153     /* kill the X11 part of this container */
154     x_con_kill(con);
155
156     con_detach(con);
157     con_fix_percent(con->parent, WINDOW_REMOVE);
158
159     if (con->window != NULL) {
160         x_window_kill(con->window->id);
161         free(con->window);
162     }
163     free(con->name);
164     TAILQ_REMOVE(&all_cons, con, all_cons);
165     free(con);
166
167     /* TODO: check if the container (or one of its children) was focused */
168     con_focus(next);
169 }
170
171 void tree_close_con() {
172     assert(focused != NULL);
173     if (focused->parent->type == CT_OUTPUT) {
174         LOG("Cannot close workspace\n");
175         return;
176     }
177
178     /* Kill con */
179     tree_close(focused);
180 }
181
182 /*
183  * Splits (horizontally or vertically) the given container by creating a new
184  * container which contains the old one and the future ones.
185  *
186  */
187 void tree_split(Con *con, orientation_t orientation) {
188     /* 2: replace it with a new Con */
189     Con *new = con_new(NULL);
190     Con *parent = con->parent;
191     TAILQ_REPLACE(&(parent->nodes_head), con, new, nodes);
192     TAILQ_REPLACE(&(parent->focus_head), con, new, focused);
193     new->parent = parent;
194     new->orientation = orientation;
195
196     /* 3: add it as a child to the new Con */
197     con_attach(con, new);
198 }
199
200 void level_up() {
201     /* We can focus up to the workspace, but not any higher in the tree */
202     if (focused->parent->type != CT_CON) {
203         printf("cannot go up\n");
204         return;
205     }
206     con_focus(focused->parent);
207 }
208
209 void level_down() {
210     /* Go down the focus stack of the current node */
211     Con *next = TAILQ_FIRST(&(focused->focus_head));
212     if (next == TAILQ_END(&(focused->focus_head))) {
213         printf("cannot go down\n");
214         return;
215     }
216     con_focus(next);
217 }
218
219 static void mark_unmapped(Con *con) {
220     Con *current;
221
222     con->mapped = false;
223     TAILQ_FOREACH(current, &(con->nodes_head), nodes)
224         mark_unmapped(current);
225 }
226
227 void tree_render() {
228     if (croot == NULL)
229         return;
230
231     printf("-- BEGIN RENDERING --\n");
232     /* Reset map state for all nodes in tree */
233     /* TODO: a nicer method to walk all nodes would be good, maybe? */
234     mark_unmapped(croot);
235     croot->mapped = true;
236
237     /* We start rendering at an output */
238     Con *output;
239     TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
240         printf("output %p / %s\n", output, output->name);
241         render_con(output);
242     }
243     x_push_changes(croot);
244     printf("-- END RENDERING --\n");
245 }
246
247 void tree_next(char way, orientation_t orientation) {
248     /* 1: get the first parent with the same orientation */
249     Con *parent = focused->parent;
250     while (parent->orientation != orientation) {
251         LOG("need to go one level further up\n");
252         /* if the current parent is an output, we are at a workspace
253          * and the orientation still does not match */
254         if (parent->parent->type == CT_OUTPUT)
255             return;
256         parent = parent->parent;
257     }
258     Con *current = TAILQ_FIRST(&(parent->focus_head));
259     assert(current != TAILQ_END(&(parent->focus_head)));
260
261     /* 2: chose next (or previous) */
262     Con *next;
263     if (way == 'n') {
264         next = TAILQ_NEXT(current, nodes);
265         /* if we are at the end of the list, we need to wrap */
266         if (next == TAILQ_END(&(parent->nodes_head)))
267             next = TAILQ_FIRST(&(parent->nodes_head));
268     } else {
269         next = TAILQ_PREV(current, nodes_head, nodes);
270         /* if we are at the end of the list, we need to wrap */
271         if (next == TAILQ_END(&(parent->nodes_head)))
272             next = TAILQ_LAST(&(parent->nodes_head), nodes_head);
273     }
274
275     /* 3: focus choice comes in here. at the moment we will go down
276      * until we find a window */
277     /* TODO: check for window, atm we only go down as far as possible */
278     while (TAILQ_FIRST(&(next->focus_head)) != TAILQ_END(&(next->focus_head)))
279         next = TAILQ_FIRST(&(next->focus_head));
280
281     con_focus(next);
282 }
283
284 void tree_move(char way, orientation_t orientation) {
285     /* 1: get the first parent with the same orientation */
286     Con *parent = focused->parent;
287     bool level_changed = false;
288     while (parent->orientation != orientation) {
289         LOG("need to go one level further up\n");
290         /* if the current parent is an output, we are at a workspace
291          * and the orientation still does not match */
292         if (parent->parent->type == CT_OUTPUT)
293             return;
294         parent = parent->parent;
295         level_changed = true;
296     }
297     Con *current = TAILQ_FIRST(&(parent->focus_head));
298     assert(current != TAILQ_END(&(parent->focus_head)));
299
300     /* 2: chose next (or previous) */
301     Con *next = current;
302     if (way == 'n') {
303         LOG("i would insert it after %p / %s\n", next, next->name);
304         if (!level_changed) {
305             next = TAILQ_NEXT(next, nodes);
306             if (next == TAILQ_END(&(next->parent->nodes_head))) {
307                 LOG("cannot move further to the right\n");
308                 return;
309             }
310         }
311
312         con_detach(focused);
313         focused->parent = next->parent;
314
315         TAILQ_INSERT_AFTER(&(next->parent->nodes_head), next, focused, nodes);
316         TAILQ_INSERT_HEAD(&(next->parent->focus_head), focused, focused);
317         /* TODO: don’t influence focus handling? */
318     } else {
319         LOG("i would insert it before %p / %s\n", current, current->name);
320         if (!level_changed) {
321             next = TAILQ_PREV(next, nodes_head, nodes);
322             if (next == TAILQ_END(&(next->parent->nodes_head))) {
323                 LOG("cannot move further\n");
324                 return;
325             }
326         }
327
328         con_detach(focused);
329         focused->parent = next->parent;
330
331         TAILQ_INSERT_BEFORE(next, focused, nodes);
332         TAILQ_INSERT_HEAD(&(next->parent->focus_head), focused, focused);
333         /* TODO: don’t influence focus handling? */
334     }
335 }