1 #include "frame.h"
2 #include "debug.h"
3 #include "util/xmalloc.h"
4 #include "window.h"
5 
6 Frame *root_frame;
7 
get_min_w(const Frame * f)8 static int get_min_w(const Frame *f)
9 {
10     if (f->window) {
11         return 8;
12     }
13 
14     if (f->vertical) {
15         int max = 0;
16         for (size_t i = 0, n = f->frames.count; i < n; i++) {
17             int w = get_min_w(f->frames.ptrs[i]);
18             if (w > max) {
19                 max = w;
20             }
21         }
22         return max;
23     } else {
24         int w = f->frames.count - 1; // Separators
25         for (size_t i = 0, n = f->frames.count; i < n; i++) {
26             w += get_min_w(f->frames.ptrs[i]);
27         }
28         return w;
29     }
30 }
31 
get_min_h(const Frame * f)32 static int get_min_h(const Frame *f)
33 {
34     if (f->window) {
35         return 3;
36     }
37 
38     if (!f->vertical) {
39         int max = 0;
40         for (size_t i = 0, n = f->frames.count; i < n; i++) {
41             int h = get_min_h(f->frames.ptrs[i]);
42             if (h > max) {
43                 max = h;
44             }
45         }
46         return max;
47     } else {
48         int h = 0; // No separators
49         for (size_t i = 0, n = f->frames.count; i < n; i++) {
50             h += get_min_h(f->frames.ptrs[i]);
51         }
52         return h;
53     }
54 }
55 
get_min(const Frame * f)56 static int get_min(const Frame *f)
57 {
58     if (f->parent->vertical) {
59         return get_min_h(f);
60     }
61     return get_min_w(f);
62 }
63 
get_size(const Frame * f)64 static int get_size(const Frame *f)
65 {
66     if (f->parent->vertical) {
67         return f->h;
68     }
69     return f->w;
70 }
71 
get_container_size(const Frame * f)72 static int get_container_size(const Frame *f)
73 {
74     if (f->vertical) {
75         return f->h;
76     }
77     return f->w;
78 }
79 
set_size(Frame * f,int size)80 static void set_size(Frame *f, int size)
81 {
82     if (f->parent->vertical) {
83         set_frame_size(f, f->parent->w, size);
84     } else {
85         set_frame_size(f, size, f->parent->h);
86     }
87 }
88 
divide_equally(const Frame * f)89 static void divide_equally(const Frame *f)
90 {
91     size_t count = f->frames.count;
92     BUG_ON(count == 0);
93 
94     int *size = xnew0(int, count);
95     int *min = xnew(int, count);
96     for (size_t i = 0; i < count; i++) {
97         min[i] = get_min(f->frames.ptrs[i]);
98     }
99 
100     int q, r, used;
101     int s = get_container_size(f);
102     size_t n = count;
103 
104     // Consume q and r as equally as possible
105     do {
106         used = 0;
107         q = s / n;
108         r = s % n;
109         for (size_t i = 0; i < count; i++) {
110             if (size[i] == 0 && min[i] > q) {
111                 size[i] = min[i];
112                 used += min[i];
113                 n--;
114             }
115         }
116         s -= used;
117     } while (used && n > 0);
118 
119     for (size_t i = 0; i < count; i++) {
120         Frame *c = f->frames.ptrs[i];
121         if (size[i] == 0) {
122             size[i] = q + (r-- > 0);
123         }
124         set_size(c, size[i]);
125     }
126 
127     free(size);
128     free(min);
129 }
130 
fix_size(const Frame * f)131 static void fix_size(const Frame *f)
132 {
133     size_t count = f->frames.count;
134     int *size = xnew0(int, count);
135     int *min = xnew(int, count);
136     int total = 0;
137     for (size_t i = 0; i < count; i++) {
138         const Frame *c = f->frames.ptrs[i];
139         min[i] = get_min(c);
140         size[i] = get_size(c);
141         if (size[i] < min[i]) {
142             size[i] = min[i];
143         }
144         total += size[i];
145     }
146 
147     int s = get_container_size(f);
148     if (total > s) {
149         int n = total - s;
150         for (ssize_t i = count - 1; n > 0 && i >= 0; i--) {
151             int new_size = size[i] - n;
152             if (new_size < min[i]) {
153                 new_size = min[i];
154             }
155             n -= size[i] - new_size;
156             size[i] = new_size;
157         }
158     } else {
159         size[count - 1] += s - total;
160     }
161 
162     for (size_t i = 0; i < count; i++) {
163         set_size(f->frames.ptrs[i], size[i]);
164     }
165 
166     free(size);
167     free(min);
168 }
169 
add_to_sibling_size(Frame * f,int count)170 static void add_to_sibling_size(Frame *f, int count)
171 {
172     const Frame *parent = f->parent;
173     size_t idx = ptr_array_idx(&parent->frames, f);
174     if (idx == parent->frames.count - 1) {
175         f = parent->frames.ptrs[idx - 1];
176     } else {
177         f = parent->frames.ptrs[idx + 1];
178     }
179     set_size(f, get_size(f) + count);
180 }
181 
sub(Frame * f,int count)182 static int sub(Frame *f, int count)
183 {
184     int min = get_min(f);
185     int old = get_size(f);
186     int new = old - count;
187     if (new < min) {
188         new = min;
189     }
190     if (new != old) {
191         set_size(f, new);
192     }
193     return count - (old - new);
194 }
195 
subtract_from_sibling_size(const Frame * f,int count)196 static void subtract_from_sibling_size(const Frame *f, int count)
197 {
198     const Frame *parent = f->parent;
199     size_t idx = ptr_array_idx(&parent->frames, f);
200 
201     for (size_t i = idx + 1, n = parent->frames.count; i < n; i++) {
202         count = sub(parent->frames.ptrs[i], count);
203         if (count == 0) {
204             return;
205         }
206     }
207 
208     for (size_t i = idx; i > 0; i--) {
209         count = sub(parent->frames.ptrs[i - 1], count);
210         if (count == 0) {
211             return;
212         }
213     }
214 }
215 
resize_to(Frame * f,int size)216 static void resize_to(Frame *f, int size)
217 {
218     const Frame *parent = f->parent;
219     int total = parent->vertical ? parent->h : parent->w;
220     int count = parent->frames.count;
221     int min = get_min(f);
222     int max = total - (count - 1) * min;
223     int change;
224 
225     if (max < min) {
226         max = min;
227     }
228     if (size < min) {
229         size = min;
230     }
231     if (size > max) {
232         size = max;
233     }
234 
235     change = size - get_size(f);
236     if (change == 0) {
237         return;
238     }
239 
240     set_size(f, size);
241     if (change < 0) {
242         add_to_sibling_size(f, -change);
243     } else {
244         subtract_from_sibling_size(f, change);
245     }
246 }
247 
rightmost_frame(const Frame * f)248 static bool rightmost_frame(const Frame *f)
249 {
250     const Frame *parent = f->parent;
251 
252     if (parent == NULL) {
253         return true;
254     }
255     if (!parent->vertical) {
256         if (f != parent->frames.ptrs[parent->frames.count - 1]) {
257             return false;
258         }
259     }
260     return rightmost_frame(parent);
261 }
262 
new_frame(void)263 static Frame *new_frame(void)
264 {
265     Frame *f = xnew0(Frame, 1);
266     f->equal_size = true;
267     return f;
268 }
269 
add_frame(Frame * parent,Window * w,size_t idx)270 static Frame *add_frame(Frame *parent, Window *w, size_t idx)
271 {
272     Frame *f = new_frame();
273     f->parent = parent;
274     f->window = w;
275     w->frame = f;
276     if (parent != NULL) {
277         BUG_ON(idx > parent->frames.count);
278         ptr_array_insert(&parent->frames, f, idx);
279         parent->window = NULL;
280     }
281     return f;
282 }
283 
new_root_frame(Window * w)284 Frame *new_root_frame(Window *w)
285 {
286     return add_frame(NULL, w, 0);
287 }
288 
find_resizable(Frame * f,ResizeDirection dir)289 static Frame *find_resizable(Frame *f, ResizeDirection dir)
290 {
291     if (dir == RESIZE_DIRECTION_AUTO) {
292         return f;
293     }
294 
295     while (f->parent) {
296         if (dir == RESIZE_DIRECTION_VERTICAL && f->parent->vertical) {
297             return f;
298         }
299         if (dir == RESIZE_DIRECTION_HORIZONTAL && !f->parent->vertical) {
300             return f;
301         }
302         f = f->parent;
303     }
304     return NULL;
305 }
306 
set_frame_size(Frame * f,int w,int h)307 void set_frame_size(Frame *f, int w, int h)
308 {
309     int min_w = get_min_w(f);
310     int min_h = get_min_h(f);
311 
312     if (w < min_w) {
313         w = min_w;
314     }
315     if (h < min_h) {
316         h = min_h;
317     }
318     f->w = w;
319     f->h = h;
320     if (f->window) {
321         if (!rightmost_frame(f)) {
322             w--; // Separator
323         }
324         set_window_size(f->window, w, h);
325         return;
326     }
327 
328     if (f->equal_size) {
329         divide_equally(f);
330     } else {
331         fix_size(f);
332     }
333 }
334 
equalize_frame_sizes(Frame * parent)335 void equalize_frame_sizes(Frame *parent)
336 {
337     parent->equal_size = true;
338     divide_equally(parent);
339     update_window_coordinates();
340 }
341 
add_to_frame_size(Frame * f,ResizeDirection dir,int amount)342 void add_to_frame_size(Frame *f, ResizeDirection dir, int amount)
343 {
344     f = find_resizable(f, dir);
345     if (f == NULL) {
346         return;
347     }
348 
349     f->parent->equal_size = false;
350     if (f->parent->vertical) {
351         resize_to(f, f->h + amount);
352     } else {
353         resize_to(f, f->w + amount);
354     }
355     update_window_coordinates();
356 }
357 
resize_frame(Frame * f,ResizeDirection dir,int size)358 void resize_frame(Frame *f, ResizeDirection dir, int size)
359 {
360     f = find_resizable(f, dir);
361     if (f == NULL) {
362         return;
363     }
364 
365     f->parent->equal_size = false;
366     resize_to(f, size);
367     update_window_coordinates();
368 }
369 
update_frame_coordinates(const Frame * f,int x,int y)370 static void update_frame_coordinates(const Frame *f, int x, int y)
371 {
372     if (f->window) {
373         set_window_coordinates(f->window, x, y);
374         return;
375     }
376 
377     for (size_t i = 0, n = f->frames.count; i < n; i++) {
378         const Frame *c = f->frames.ptrs[i];
379         update_frame_coordinates(c, x, y);
380         if (f->vertical) {
381             y += c->h;
382         } else {
383             x += c->w;
384         }
385     }
386 }
387 
update_window_coordinates(void)388 void update_window_coordinates(void)
389 {
390     update_frame_coordinates(root_frame, 0, 0);
391 }
392 
split_frame(Window * w,bool vertical,bool before)393 Frame *split_frame(Window *w, bool vertical, bool before)
394 {
395     Frame *f = w->frame;
396     Frame *parent = f->parent;
397     if (parent == NULL || parent->vertical != vertical) {
398         // Reparent w
399         f->vertical = vertical;
400         add_frame(f, w, 0);
401         parent = f;
402     }
403 
404     size_t idx = ptr_array_idx(&parent->frames, w->frame);
405     if (!before) {
406         idx++;
407     }
408     Window *neww = new_window();
409     f = add_frame(parent, neww, idx);
410     parent->equal_size = true;
411 
412     // Recalculate
413     set_frame_size(parent, parent->w, parent->h);
414     update_window_coordinates();
415     return f;
416 }
417 
418 // Doesn't really split root but adds new frame between root and its contents
split_root(bool vertical,bool before)419 Frame *split_root(bool vertical, bool before)
420 {
421     Frame *new_root, *f;
422 
423     new_root = new_frame();
424     new_root->vertical = vertical;
425     f = new_frame();
426     f->parent = new_root;
427     f->window = new_window();
428     f->window->frame = f;
429     if (before) {
430         ptr_array_add(&new_root->frames, f);
431         ptr_array_add(&new_root->frames, root_frame);
432     } else {
433         ptr_array_add(&new_root->frames, root_frame);
434         ptr_array_add(&new_root->frames, f);
435     }
436 
437     root_frame->parent = new_root;
438     set_frame_size(new_root, root_frame->w, root_frame->h);
439     root_frame = new_root;
440     update_window_coordinates();
441     return f;
442 }
443 
444 // NOTE: does not remove f from f->parent->frames
free_frame(Frame * f)445 static void free_frame(Frame *f)
446 {
447     f->parent = NULL;
448     ptr_array_free_cb(&f->frames, FREE_FUNC(free_frame));
449     if (f->window != NULL) {
450         window_free(f->window);
451         f->window = NULL;
452     }
453     free(f);
454 }
455 
remove_frame(Frame * f)456 void remove_frame(Frame *f)
457 {
458     Frame *parent = f->parent;
459 
460     if (parent == NULL) {
461         free_frame(f);
462         return;
463     }
464     ptr_array_remove(&parent->frames, f);
465     free_frame(f);
466 
467     if (parent->frames.count == 1) {
468         // Replace parent with the only child frame
469         Frame *gp = parent->parent;
470         Frame *c = parent->frames.ptrs[0];
471 
472         c->parent = gp;
473         c->w = parent->w;
474         c->h = parent->h;
475         if (gp) {
476             size_t idx = ptr_array_idx(&gp->frames, parent);
477             gp->frames.ptrs[idx] = c;
478         } else {
479             root_frame = c;
480         }
481         free(parent->frames.ptrs);
482         free(parent);
483         parent = c;
484     }
485 
486     // Recalculate
487     set_frame_size(parent, parent->w, parent->h);
488     update_window_coordinates();
489 }
490 
491 #ifdef DEBUG_FRAMES
debug_frame(const Frame * f,int level)492 static void debug_frame(const Frame *f, int level)
493 {
494     d_print (
495         "%*s%dx%d %d %d %zu\n",
496         level * 4, "",
497         f->w, f->h,
498         f->vertical, f->equal_size,
499         f->frames.count
500     );
501 
502     if (f->window) {
503         d_print (
504             "%*swindow %d,%d %dx%d\n",
505             (level + 1) * 4, "",
506             f->window->x,
507             f->window->y,
508             f->window->w,
509             f->window->h
510         );
511     }
512 
513     BUG_ON(f->window && f->frames.count);
514     if (f->window) {
515         BUG_ON(f != f->window->frame);
516     }
517 
518     for (size_t i = 0, n = f->frames.count; i < n; i++) {
519         const Frame *c = f->frames.ptrs[i];
520         BUG_ON(c->parent != f);
521         debug_frame(c, level + 1);
522     }
523 }
524 
debug_frames(void)525 void debug_frames(void)
526 {
527     debug_frame(root_frame, 0);
528 }
529 #endif
530