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