1 #include <hikari/sheet.h>
2 
3 #include <stdio.h>
4 #include <stdlib.h>
5 
6 #include <hikari/configuration.h>
7 #include <hikari/group.h>
8 #include <hikari/layout.h>
9 #include <hikari/memory.h>
10 #include <hikari/split.h>
11 #include <hikari/view.h>
12 
13 void
hikari_sheet_init(struct hikari_sheet * sheet,int nr,struct hikari_workspace * workspace)14 hikari_sheet_init(
15     struct hikari_sheet *sheet, int nr, struct hikari_workspace *workspace)
16 {
17   assert(workspace->output != NULL);
18 
19   wl_list_init(&sheet->views);
20 
21   sheet->nr = nr;
22 
23   sheet->workspace = workspace;
24   sheet->layout = NULL;
25 }
26 
27 static struct hikari_view *
scan_next_tileable_view(struct hikari_view * view)28 scan_next_tileable_view(struct hikari_view *view)
29 {
30   assert(view != NULL);
31 
32   struct wl_list *next = view->sheet_views.next;
33 
34   while (next != &view->sheet->views) {
35     view = wl_container_of(next, view, sheet_views);
36     if (hikari_view_is_tileable(view)) {
37       assert(!hikari_view_is_hidden(view));
38       return view;
39     }
40     next = view->sheet_views.next;
41   }
42 
43   return NULL;
44 }
45 
46 struct hikari_view *
hikari_sheet_first_tileable_view(struct hikari_sheet * sheet)47 hikari_sheet_first_tileable_view(struct hikari_sheet *sheet)
48 {
49   assert(sheet != NULL);
50 
51   struct wl_list *next = sheet->views.next;
52   struct hikari_view *view = NULL;
53 
54   while (next != &sheet->views) {
55     view = wl_container_of(next, view, sheet_views);
56     if (hikari_view_is_tileable(view)) {
57       if (hikari_view_is_hidden(view)) {
58         hikari_view_show(view);
59       }
60       return view;
61     }
62     next = view->sheet_views.next;
63   }
64 
65   return NULL;
66 }
67 
68 static int
tileable_views(struct hikari_view * view)69 tileable_views(struct hikari_view *view)
70 {
71   int result;
72   if (hikari_view_is_tileable(view)) {
73     result = 1;
74   } else {
75     result = 0;
76   }
77 
78   struct wl_list *next = view->sheet_views.next;
79 
80   while (next != &view->sheet->views) {
81     view = wl_container_of(next, view, sheet_views);
82     if (hikari_view_is_tileable(view)) {
83       result++;
84     }
85     next = view->sheet_views.next;
86   }
87 
88   return result;
89 }
90 
91 static struct hikari_view *
single_layout(struct wlr_box * frame,struct hikari_view * first,int nr_of_views,bool * center)92 single_layout(struct wlr_box *frame,
93     struct hikari_view *first,
94     int nr_of_views,
95     bool *center)
96 {
97   hikari_view_tile(first, frame, *center);
98   *center = false;
99   return scan_next_tileable_view(first);
100 }
101 
102 static struct hikari_view *
empty_layout(struct wlr_box * frame,struct hikari_view * first,int nr_of_views,bool * center)103 empty_layout(struct wlr_box *frame,
104     struct hikari_view *first,
105     int nr_of_views,
106     bool *center)
107 {
108   return first;
109 }
110 
111 static struct hikari_view *
full_layout(struct wlr_box * frame,struct hikari_view * first,int nr_of_views,bool * center)112 full_layout(struct wlr_box *frame,
113     struct hikari_view *first,
114     int nr_of_views,
115     bool *center)
116 {
117   struct hikari_view *view = first;
118   for (int i = 0; i < nr_of_views && view != NULL; i++) {
119     if (hikari_view_is_tileable(view)) {
120       if (hikari_view_is_hidden(view)) {
121         hikari_view_show(view);
122       }
123       hikari_view_tile(view, frame, *center);
124       *center = false;
125     }
126     view = scan_next_tileable_view(view);
127   }
128 
129   return view;
130 }
131 
132 #define LAYOUT_VIEWS(nr_of_views, view, frame, center)                         \
133   if (nr_of_views == 0) {                                                      \
134     return NULL;                                                               \
135   } else if (nr_of_views == 1) {                                               \
136     hikari_view_tile(view, frame, *center);                                    \
137     *center = false;                                                           \
138   } else
139 
140 static struct hikari_view *
grid_layout(struct wlr_box * frame,struct hikari_view * first,int nr_of_views,bool * center)141 grid_layout(struct wlr_box *frame,
142     struct hikari_view *first,
143     int nr_of_views,
144     bool *center)
145 {
146   int nr_of_rows = 1;
147   int nr_of_cols = 1;
148 
149   for (int i = 1; i <= nr_of_views; i++) {
150     if (i > nr_of_rows * nr_of_cols) {
151       if (nr_of_cols > nr_of_rows) {
152         assert(nr_of_cols == nr_of_rows + 1);
153         nr_of_rows++;
154       } else {
155         nr_of_cols++;
156       }
157     }
158   }
159 
160   struct hikari_view *view = first;
161   LAYOUT_VIEWS(nr_of_views, first, frame, center)
162   {
163     int border_width = hikari_configuration->border;
164     int gap = hikari_configuration->gap;
165     int border = 2 * border_width;
166     int row_gaps = nr_of_rows - 1;
167     int col_gaps = nr_of_cols - 1;
168     int gaps_height = gap * row_gaps;
169     int gaps_width = gap * col_gaps;
170     int views_height = frame->height - border * nr_of_rows - gaps_height;
171     int views_width = frame->width - border * nr_of_cols - gaps_width;
172 
173     int width = views_width / nr_of_cols;
174     int height = views_height / nr_of_rows;
175 
176     int rest_width =
177         frame->width - border * col_gaps - gaps_width - width * nr_of_cols;
178 
179     int rest_height =
180         frame->height - border * row_gaps - gaps_height - height * nr_of_rows;
181 
182     struct wlr_box geometry = { .y = frame->y, .x = frame->x };
183 
184     geometry.height = height + rest_height;
185     for (int g_y = 0; g_y < nr_of_rows; g_y++) {
186       if (g_y == 1) {
187         geometry.height = height;
188       }
189       geometry.width = width + rest_width;
190       for (int g_x = 0; g_x < nr_of_cols; g_x++) {
191         if (g_x == 1) {
192           geometry.width = width;
193         }
194         hikari_view_tile(view, &geometry, *center);
195         *center = false;
196 
197         view = scan_next_tileable_view(view);
198         if (view == NULL) {
199           return NULL;
200         }
201 
202         geometry.x += gap + border + geometry.width;
203       }
204       geometry.x = frame->x;
205       geometry.y += gap + border + geometry.height;
206     }
207   }
208 
209   return scan_next_tileable_view(view);
210 }
211 
212 #define SPLIT_LAYOUT(name, x, y, width, height)                                \
213   static struct hikari_view *name##_layout(struct wlr_box *frame,              \
214       struct hikari_view *first,                                               \
215       int nr_of_views,                                                         \
216       bool *center)                                                            \
217   {                                                                            \
218     struct hikari_view *view = first;                                          \
219     int border_width = hikari_configuration->border;                           \
220     int gap = hikari_configuration->gap;                                       \
221     int border = 2 * border_width;                                             \
222     int gaps = nr_of_views - 1;                                                \
223     int gaps_##width = gap * gaps;                                             \
224                                                                                \
225     LAYOUT_VIEWS(nr_of_views, first, frame, center)                            \
226     {                                                                          \
227       int views_width = frame->width - border * gaps - gaps_##width;           \
228       int width = views_width / nr_of_views;                                   \
229       int rest = views_width - width * nr_of_views;                            \
230                                                                                \
231       struct wlr_box geometry = { .x = frame->x,                               \
232         .y = frame->y,                                                         \
233         .width = width + rest,                                                 \
234         .height = frame->height };                                             \
235                                                                                \
236       hikari_view_tile(first, &geometry, *center);                             \
237       *center = false;                                                         \
238                                                                                \
239       geometry.x += gap + border + width + rest;                               \
240       geometry.width = width;                                                  \
241       for (int n = 1; n < nr_of_views; n++) {                                  \
242         view = scan_next_tileable_view(view);                                  \
243         hikari_view_tile(view, &geometry, *center);                            \
244         *center = false;                                                       \
245         geometry.x += gap + border + width;                                    \
246       }                                                                        \
247     }                                                                          \
248                                                                                \
249     return scan_next_tileable_view(view);                                      \
250   }
251 
SPLIT_LAYOUT(queue,x,y,width,height)252 SPLIT_LAYOUT(queue, x, y, width, height)
253 SPLIT_LAYOUT(stack, y, x, height, width)
254 #undef SPLIT_LAYOUT
255 
256 #define LAYOUT(name)                                                           \
257   struct hikari_view *hikari_sheet_##name##_layout(struct hikari_sheet *sheet, \
258       struct hikari_view *first,                                               \
259       struct wlr_box *frame,                                                   \
260       int max,                                                                 \
261       bool *center)                                                            \
262   {                                                                            \
263     int nr_of_views = tileable_views(first);                                   \
264     if (nr_of_views > max) {                                                   \
265       nr_of_views = max;                                                       \
266     }                                                                          \
267     if (nr_of_views == 0 && max != 0) {                                        \
268       return NULL;                                                             \
269     }                                                                          \
270                                                                                \
271     return name##_layout(frame, first, nr_of_views, center);                   \
272   }
273 
274 LAYOUT(queue)
275 LAYOUT(stack)
276 LAYOUT(grid)
277 LAYOUT(full)
278 LAYOUT(single)
279 LAYOUT(empty)
280 #undef LAYOUT
281 
282 #undef LAYOUT_WINDOWS
283 
284 #define SHEET_VIEW(name, link)                                                 \
285   struct hikari_view *hikari_sheet_##name##_view(struct hikari_sheet *sheet)   \
286   {                                                                            \
287     struct wl_list *link = sheet->views.link;                                  \
288     struct hikari_view *view;                                                  \
289                                                                                \
290     while (link != &sheet->views) {                                            \
291       view = wl_container_of(link, view, sheet_views);                         \
292       if (!hikari_view_is_hidden(view)) {                                      \
293         return view;                                                           \
294       }                                                                        \
295       link = view->sheet_views.link;                                           \
296     }                                                                          \
297                                                                                \
298     return NULL;                                                               \
299   }
300 
301 SHEET_VIEW(first, next)
302 SHEET_VIEW(last, prev)
303 #undef SHEET_VIEW
304 
305 #define SHEET_VIEW(link, fallback)                                             \
306   struct hikari_view *hikari_sheet_##link##_view(                              \
307       struct hikari_sheet *sheet, struct hikari_view *view)                    \
308   {                                                                            \
309     struct wl_list *link = view->sheet_views.link;                             \
310                                                                                \
311     while (link != &view->sheet->views) {                                      \
312       view = wl_container_of(link, view, sheet_views);                         \
313       if (!hikari_view_is_hidden(view)) {                                      \
314         return view;                                                           \
315       }                                                                        \
316       link = view->sheet_views.link;                                           \
317     }                                                                          \
318                                                                                \
319     return hikari_sheet_##fallback##_view(sheet);                              \
320   }
321 
322 SHEET_VIEW(next, first)
323 SHEET_VIEW(prev, last)
324 #undef SHEET_VIEW
325 
326 int
327 hikari_sheet_tileable_views(struct hikari_sheet *sheet)
328 {
329   int nr_of_views = 0;
330 
331   struct hikari_view *view;
332   wl_list_for_each (view, &sheet->views, sheet_views) {
333     if (hikari_view_is_tileable(view)) {
334       nr_of_views++;
335     }
336   }
337 
338   return nr_of_views;
339 }
340 
341 struct hikari_sheet *
hikari_sheet_next(struct hikari_sheet * sheet)342 hikari_sheet_next(struct hikari_sheet *sheet)
343 {
344   struct hikari_sheet *sheets = sheet->workspace->sheets;
345 
346   if (sheet->nr == 9) {
347     return &sheets[1];
348   }
349 
350   return &sheets[sheet->nr + 1];
351 }
352 
353 struct hikari_sheet *
hikari_sheet_prev(struct hikari_sheet * sheet)354 hikari_sheet_prev(struct hikari_sheet *sheet)
355 {
356   struct hikari_sheet *sheets = sheet->workspace->sheets;
357 
358   if (sheet->nr == 0 || sheet->nr == 1) {
359     return &sheets[9];
360   }
361 
362   return &sheets[sheet->nr - 1];
363 }
364 
365 #define CYCLE_INHABITED(name)                                                  \
366   struct hikari_sheet *hikari_sheet_##name##_inhabited(                        \
367       struct hikari_sheet *sheet)                                              \
368   {                                                                            \
369     struct hikari_sheet *name = sheet;                                         \
370                                                                                \
371     do {                                                                       \
372       name = hikari_sheet_##name(name);                                        \
373     } while (wl_list_empty(&name->views) && name != sheet);                    \
374                                                                                \
375     return name;                                                               \
376   }
377 
378 CYCLE_INHABITED(next)
CYCLE_INHABITED(prev)379 CYCLE_INHABITED(prev)
380 #undef CYCLE_INHABITED
381 
382 static void
383 raise_floating(struct hikari_sheet *sheet)
384 {
385   struct hikari_view *view, *view_temp, *first = NULL;
386   wl_list_for_each_reverse_safe (view, view_temp, &sheet->views, sheet_views) {
387     if (!hikari_view_is_hidden(view) && hikari_view_is_floating(view)) {
388       if (first == view) {
389         break;
390       } else if (first == NULL) {
391         first = view;
392       }
393 
394       hikari_view_raise(view);
395     }
396   }
397 }
398 
399 void
hikari_sheet_apply_split(struct hikari_sheet * sheet,struct hikari_split * split)400 hikari_sheet_apply_split(struct hikari_sheet *sheet, struct hikari_split *split)
401 {
402   struct hikari_layout *layout;
403   if (sheet->layout != NULL) {
404     layout = sheet->layout;
405 
406     struct hikari_tile *tile;
407     wl_list_for_each (tile, &layout->tiles, layout_tiles) {
408       if (hikari_view_is_dirty(tile->view)) {
409         return;
410       }
411     }
412 
413     if (layout->split != split) {
414       hikari_split_free(layout->split);
415       layout->split = hikari_split_copy(split);
416     }
417   } else {
418     layout = hikari_malloc(sizeof(struct hikari_layout));
419     hikari_layout_init(layout, split, sheet);
420 
421     sheet->layout = layout;
422   }
423 
424   struct hikari_output *output = sheet->workspace->output;
425   struct wlr_box geometry = output->usable_area;
426   struct hikari_view *first = hikari_sheet_first_tileable_view(sheet);
427 
428   hikari_split_apply(layout->split, &geometry, first);
429 
430   raise_floating(sheet);
431 }
432 
433 bool
hikari_sheet_is_visible(struct hikari_sheet * sheet)434 hikari_sheet_is_visible(struct hikari_sheet *sheet)
435 {
436   struct hikari_sheet *sheets = sheet->workspace->sheets;
437 
438   return sheet == sheet->workspace->sheet || sheet == &sheets[0];
439 }
440 
441 #define SHOW_VIEWS(cond)                                                       \
442   {                                                                            \
443     struct hikari_view *view, *view_temp, *top = NULL;                         \
444     wl_list_for_each_reverse_safe (                                            \
445         view, view_temp, &sheet->views, sheet_views) {                         \
446       if (cond) {                                                              \
447         if (top == view) {                                                     \
448           break;                                                               \
449         }                                                                      \
450                                                                                \
451         if (top == NULL) {                                                     \
452           top = view;                                                          \
453         }                                                                      \
454                                                                                \
455         hikari_view_show(view);                                                \
456       }                                                                        \
457     }                                                                          \
458   }
459 
460 void
hikari_sheet_show_all(struct hikari_sheet * sheet)461 hikari_sheet_show_all(struct hikari_sheet *sheet)
462 {
463   SHOW_VIEWS(true);
464 }
465 
466 void
hikari_sheet_show(struct hikari_sheet * sheet)467 hikari_sheet_show(struct hikari_sheet *sheet)
468 {
469   SHOW_VIEWS(!hikari_view_is_invisible(view));
470 }
471 
472 void
hikari_sheet_show_group(struct hikari_sheet * sheet,struct hikari_group * group)473 hikari_sheet_show_group(struct hikari_sheet *sheet, struct hikari_group *group)
474 {
475   SHOW_VIEWS(view->group == group);
476 }
477 
478 void
hikari_sheet_show_invisible(struct hikari_sheet * sheet)479 hikari_sheet_show_invisible(struct hikari_sheet *sheet)
480 {
481   SHOW_VIEWS(hikari_view_is_invisible(view));
482 }
483 #undef SHOW_VIEWS
484