1 #include <hikari/workspace.h>
2 
3 #include <stdbool.h>
4 #include <stdlib.h>
5 
6 #include <wlr/backend.h>
7 #include <wlr/render/wlr_renderer.h>
8 #include <wlr/render/wlr_texture.h>
9 #include <wlr/types/wlr_matrix.h>
10 #ifdef HAVE_XWAYLAND
11 #include <wlr/xwayland.h>
12 #endif
13 
14 #include <hikari/color.h>
15 #include <hikari/configuration.h>
16 #include <hikari/group_assign_mode.h>
17 #include <hikari/indicator.h>
18 #include <hikari/indicator_frame.h>
19 #include <hikari/input_grab_mode.h>
20 #include <hikari/layout.h>
21 #include <hikari/mark_assign_mode.h>
22 #include <hikari/mark_select_mode.h>
23 #include <hikari/normal_mode.h>
24 #include <hikari/server.h>
25 #include <hikari/sheet.h>
26 #include <hikari/xdg_view.h>
27 #ifdef HAVE_XWAYLAND
28 #include <hikari/xwayland_unmanaged_view.h>
29 #include <hikari/xwayland_view.h>
30 #endif
31 
32 #define FOCUS_GUARD(workspace, view)                                           \
33   struct hikari_view *view = workspace->focus_view;                            \
34                                                                                \
35   if (focus_view == NULL) {                                                    \
36     return;                                                                    \
37   }
38 
39 void
hikari_workspace_init(struct hikari_workspace * workspace,struct hikari_output * output)40 hikari_workspace_init(
41     struct hikari_workspace *workspace, struct hikari_output *output)
42 {
43   wl_list_init(&workspace->views);
44   wl_list_init(&hikari_server.visible_groups);
45   workspace->output = output;
46   workspace->focus_view = NULL;
47   workspace->sheets =
48       hikari_calloc(HIKARI_NR_OF_SHEETS, sizeof(struct hikari_sheet));
49 
50   for (int i = 0; i < HIKARI_NR_OF_SHEETS; i++) {
51     hikari_sheet_init(&workspace->sheets[i], i, workspace);
52   }
53 
54   workspace->alternate_sheet = &workspace->sheets[0];
55   workspace->sheet = &workspace->sheets[1];
56 
57 #ifdef HAVE_LAYERSHELL
58   workspace->focus_layer = NULL;
59 #endif
60 }
61 
62 void
hikari_workspace_fini(struct hikari_workspace * workspace)63 hikari_workspace_fini(struct hikari_workspace *workspace)
64 {
65   hikari_free(workspace->sheets);
66 }
67 
68 #define CYCLE_WORKSPACE(name)                                                  \
69   struct hikari_workspace *hikari_workspace_##name(                            \
70       struct hikari_workspace *workspace)                                      \
71   {                                                                            \
72     struct hikari_output *output = hikari_output_##name(workspace->output);    \
73                                                                                \
74     return output->workspace;                                                  \
75   }
76 
77 CYCLE_WORKSPACE(next)
CYCLE_WORKSPACE(prev)78 CYCLE_WORKSPACE(prev)
79 #undef CYCLE_WORKSPACE
80 
81 void
82 hikari_workspace_merge(
83     struct hikari_workspace *workspace, struct hikari_workspace *into)
84 {
85   assert(workspace != NULL);
86   assert(into != NULL);
87 
88 #ifndef NDEBUG
89   printf("WORKSPACE MERGE %p INTO %p\n", workspace, into);
90 #endif
91 
92   for (int i = 0; i < HIKARI_NR_OF_SHEETS; i++) {
93     struct hikari_sheet *from = &workspace->sheets[i];
94     struct hikari_sheet *to = &into->sheets[i];
95     struct hikari_view *view, *view_temp;
96     wl_list_for_each_reverse_safe (view, view_temp, &from->views, sheet_views) {
97       hikari_view_evacuate(view, to);
98     }
99   }
100 
101 #ifdef HAVE_XWAYLAND
102   struct hikari_xwayland_unmanaged_view *unmanaged_xwayland_view,
103       *unmanaged_xwayland_view_temp;
104   wl_list_for_each_reverse_safe (unmanaged_xwayland_view,
105       unmanaged_xwayland_view_temp,
106       &workspace->output->unmanaged_xwayland_views,
107       unmanaged_output_views) {
108     hikari_xwayland_unmanaged_evacuate(unmanaged_xwayland_view, into);
109   }
110 #endif
111 }
112 
113 void
hikari_workspace_quit_view(struct hikari_workspace * workspace)114 hikari_workspace_quit_view(struct hikari_workspace *workspace)
115 {
116   struct hikari_view *focus_view = workspace->focus_view;
117 
118 #ifdef HAVE_LAYERSHELL
119   struct hikari_layer *focus_layer = workspace->focus_layer;
120 
121   if (focus_layer != NULL) {
122     assert(focus_view == NULL);
123     wlr_layer_surface_v1_close(focus_layer->surface);
124     return;
125   }
126 #endif
127 
128   if (focus_view != NULL) {
129 #ifdef HAVE_LAYERSHELL
130     assert(focus_layer == NULL);
131 #endif
132 
133     hikari_view_quit(focus_view);
134     hikari_server_cursor_focus();
135   }
136 }
137 
138 void
hikari_workspace_clear(struct hikari_workspace * workspace)139 hikari_workspace_clear(struct hikari_workspace *workspace)
140 {
141   struct hikari_view *view = NULL, *view_tmp = NULL;
142 
143   wl_list_for_each_reverse_safe (
144       view, view_tmp, &(workspace->views), workspace_views) {
145     hikari_view_hide(view);
146   }
147 
148   hikari_server_cursor_focus();
149 }
150 
151 static void
display_sheet(struct hikari_workspace * workspace,struct hikari_sheet * sheet)152 display_sheet(struct hikari_workspace *workspace, struct hikari_sheet *sheet)
153 {
154   hikari_workspace_clear(workspace);
155 
156   if (sheet != workspace->sheet) {
157     workspace->alternate_sheet = workspace->sheet;
158     workspace->sheet = sheet;
159   }
160 
161   hikari_sheet_show(&workspace->sheets[0]);
162 
163   if (sheet->nr != 0) {
164     hikari_sheet_show(sheet);
165   }
166 
167   hikari_server_cursor_focus();
168 }
169 
170 #define DISPLAY_SHEET(name, sheet)                                             \
171   void hikari_workspace_display_sheet_##name(                                  \
172       struct hikari_workspace *workspace)                                      \
173   {                                                                            \
174     display_sheet(workspace, sheet);                                           \
175   }
176 
177 DISPLAY_SHEET(0, &workspace->sheets[0])
178 DISPLAY_SHEET(1, &workspace->sheets[1])
179 DISPLAY_SHEET(2, &workspace->sheets[2])
180 DISPLAY_SHEET(3, &workspace->sheets[3])
181 DISPLAY_SHEET(4, &workspace->sheets[4])
182 DISPLAY_SHEET(5, &workspace->sheets[5])
183 DISPLAY_SHEET(6, &workspace->sheets[6])
184 DISPLAY_SHEET(7, &workspace->sheets[7])
185 DISPLAY_SHEET(8, &workspace->sheets[8])
186 DISPLAY_SHEET(9, &workspace->sheets[9])
187 DISPLAY_SHEET(alternate, workspace->alternate_sheet)
188 DISPLAY_SHEET(current, workspace->sheet)
189 DISPLAY_SHEET(next, hikari_sheet_next(workspace->sheet))
190 DISPLAY_SHEET(prev, hikari_sheet_prev(workspace->sheet))
191 #undef DISPLAY_SHEET
192 
193 void
hikari_workspace_switch_sheet(struct hikari_workspace * workspace,struct hikari_sheet * sheet)194 hikari_workspace_switch_sheet(
195     struct hikari_workspace *workspace, struct hikari_sheet *sheet)
196 {
197   assert(workspace == sheet->workspace);
198 
199   display_sheet(workspace, sheet);
200 }
201 
202 void
hikari_workspace_switch_to_next_sheet(struct hikari_workspace * workspace)203 hikari_workspace_switch_to_next_sheet(struct hikari_workspace *workspace)
204 {
205   if (workspace->sheet->nr == 0) {
206     return;
207   }
208 
209   uint8_t nr = workspace->sheet->nr == 9 ? 1 : workspace->sheet->nr + 1;
210 
211   display_sheet(workspace, &workspace->sheets[nr]);
212 }
213 
214 void
hikari_workspace_switch_to_prev_sheet(struct hikari_workspace * workspace)215 hikari_workspace_switch_to_prev_sheet(struct hikari_workspace *workspace)
216 {
217   if (workspace->sheet->nr == 0) {
218     return;
219   }
220 
221   uint8_t nr = workspace->sheet->nr == 1 ? 9 : workspace->sheet->nr - 1;
222 
223   display_sheet(workspace, &workspace->sheets[nr]);
224 }
225 
226 void
hikari_workspace_switch_to_next_inhabited_sheet(struct hikari_workspace * workspace)227 hikari_workspace_switch_to_next_inhabited_sheet(
228     struct hikari_workspace *workspace)
229 {
230   struct hikari_sheet *sheet = hikari_sheet_next_inhabited(workspace->sheet);
231 
232   if (sheet == workspace->sheet) {
233     return;
234   }
235 
236   display_sheet(workspace, sheet);
237 }
238 
239 void
hikari_workspace_switch_to_prev_inhabited_sheet(struct hikari_workspace * workspace)240 hikari_workspace_switch_to_prev_inhabited_sheet(
241     struct hikari_workspace *workspace)
242 {
243   struct hikari_sheet *sheet = hikari_sheet_prev_inhabited(workspace->sheet);
244 
245   if (sheet == workspace->sheet) {
246     return;
247   }
248 
249   display_sheet(workspace, sheet);
250 }
251 
252 #define CYCLE_LAYOUT_VIEW(fallback, link)                                      \
253                                                                                \
254   struct hikari_view *hikari_workspace_##fallback##_layout_view(               \
255       struct hikari_workspace *workspace)                                      \
256   {                                                                            \
257     struct hikari_layout *layout = workspace->sheet->layout;                   \
258                                                                                \
259     if (layout == NULL) {                                                      \
260       return NULL;                                                             \
261     }                                                                          \
262                                                                                \
263     return hikari_layout_##fallback##_view(layout);                            \
264   }                                                                            \
265                                                                                \
266   struct hikari_view *hikari_workspace_##link##_layout_view(                   \
267       struct hikari_workspace *workspace)                                      \
268   {                                                                            \
269     struct hikari_layout *layout = workspace->sheet->layout;                   \
270                                                                                \
271     if (layout == NULL) {                                                      \
272       return NULL;                                                             \
273     }                                                                          \
274                                                                                \
275     struct hikari_view *focus_view = workspace->focus_view;                    \
276                                                                                \
277     if (focus_view == NULL || !hikari_view_is_tiled(focus_view) ||             \
278         focus_view->sheet->layout != layout) {                                 \
279       return hikari_layout_##fallback##_view(layout);                          \
280     } else {                                                                   \
281       return hikari_tile_##link##_view(focus_view->tile);                      \
282     }                                                                          \
283   }
284 
CYCLE_LAYOUT_VIEW(first,next)285 CYCLE_LAYOUT_VIEW(first, next)
286 CYCLE_LAYOUT_VIEW(last, prev)
287 #undef CYCLE_LAYOUT_VIEW
288 
289 #define CYCLE_GROUP_VIEW(link)                                                 \
290   struct hikari_view *hikari_workspace_##link##_group_view(                    \
291       struct hikari_workspace *workspace)                                      \
292   {                                                                            \
293     struct hikari_view *focus_view = workspace->focus_view;                    \
294     if (focus_view == NULL) {                                                  \
295       return NULL;                                                             \
296     }                                                                          \
297                                                                                \
298     return hikari_group_##link##_view(focus_view->group);                      \
299   }
300 
301 CYCLE_GROUP_VIEW(first)
302 CYCLE_GROUP_VIEW(last)
303 #undef CYCLE_GROUP_VIEW
304 
305 #define CYCLE_GROUP_VIEW(name, link)                                           \
306   struct hikari_view *hikari_workspace_##name##_group_view(                    \
307       struct hikari_workspace *workspace)                                      \
308   {                                                                            \
309     struct hikari_view *focus_view = workspace->focus_view;                    \
310     if (focus_view == NULL) {                                                  \
311       return NULL;                                                             \
312     }                                                                          \
313                                                                                \
314     struct hikari_view *view;                                                  \
315     struct hikari_group *group = focus_view->group;                            \
316     struct wl_list *link = focus_view->visible_group_views.link;               \
317                                                                                \
318     if (link != &group->visible_views) {                                       \
319       view = wl_container_of(                                                  \
320           focus_view->visible_group_views.link, view, visible_group_views);    \
321     } else {                                                                   \
322       view = wl_container_of(                                                  \
323           group->visible_views.link, view, visible_group_views);               \
324     }                                                                          \
325                                                                                \
326     return view;                                                               \
327   }
328 
329 CYCLE_GROUP_VIEW(next, prev)
330 CYCLE_GROUP_VIEW(prev, next)
331 #undef CYCLE_GROUP_VIEW
332 
333 #define CYCLE_GROUP(name, link)                                                \
334   struct hikari_view *hikari_workspace_##name##_group(                         \
335       struct hikari_workspace *workspace)                                      \
336   {                                                                            \
337     if (wl_list_empty(&hikari_server.visible_groups)) {                        \
338       return NULL;                                                             \
339     }                                                                          \
340                                                                                \
341     struct hikari_view *focus_view = workspace->focus_view;                    \
342                                                                                \
343     struct hikari_group *group;                                                \
344     if (focus_view == NULL) {                                                  \
345       group = wl_container_of(                                                 \
346           hikari_server.visible_groups.link, group, visible_server_groups);    \
347     } else {                                                                   \
348       struct wl_list *link = focus_view->group->visible_server_groups.link;    \
349                                                                                \
350       if (link != &hikari_server.visible_groups) {                             \
351         group = wl_container_of(link, group, visible_server_groups);           \
352       } else {                                                                 \
353         group = wl_container_of(                                               \
354             hikari_server.visible_groups.link, group, visible_server_groups);  \
355       }                                                                        \
356                                                                                \
357       if (group == focus_view->group) {                                        \
358         return focus_view;                                                     \
359       }                                                                        \
360     }                                                                          \
361                                                                                \
362     struct hikari_view *view =                                                 \
363         wl_container_of(group->visible_views.next, view, visible_group_views); \
364                                                                                \
365     return view;                                                               \
366   }
367 
368 CYCLE_GROUP(next, prev)
369 CYCLE_GROUP(prev, next)
370 #undef CYCLE_GROUP
371 
372 void
373 hikari_workspace_focus_view(
374     struct hikari_workspace *workspace, struct hikari_view *view)
375 {
376   assert(hikari_server_in_normal_mode());
377 
378   struct wlr_seat *seat = hikari_server.seat;
379 
380   struct hikari_workspace *current_workspace = hikari_server.workspace;
381   struct hikari_view *focus_view = current_workspace->focus_view;
382 
383   if (focus_view != NULL) {
384     if (hikari_server_is_indicating()) {
385       hikari_group_damage(focus_view->group);
386       hikari_indicator_damage(&hikari_server.indicator, focus_view);
387     } else {
388       hikari_view_damage_border(focus_view);
389     }
390     hikari_view_activate(focus_view, false);
391   }
392 
393   wlr_seat_keyboard_end_grab(seat);
394   wlr_seat_keyboard_clear_focus(seat);
395   wlr_seat_pointer_clear_focus(seat);
396 
397 #ifdef HAVE_LAYERSHELL
398   workspace->focus_layer = NULL;
399 #endif
400 
401   if (view != NULL) {
402     assert(!hikari_view_is_hidden(view));
403 
404     struct wlr_keyboard *wlr_keyboard = wlr_seat_get_keyboard(seat);
405 
406     hikari_view_activate(view, true);
407 
408     if (wlr_keyboard != NULL) {
409       wlr_seat_keyboard_notify_enter(seat,
410           view->surface,
411           wlr_keyboard->keycodes,
412           wlr_keyboard->num_keycodes,
413           &wlr_keyboard->modifiers);
414     }
415 
416     if (hikari_server_is_indicating()) {
417       if (focus_view == NULL || focus_view->group != view->group) {
418         hikari_group_damage(view->group);
419       }
420     } else {
421       hikari_view_damage_border(view);
422     }
423 
424     hikari_indicator_update(&hikari_server.indicator, view);
425   } else {
426     hikari_cursor_reset_image(&hikari_server.cursor);
427   }
428 
429   hikari_server.workspace = workspace;
430   workspace->focus_view = view;
431 }
432 
433 void
hikari_workspace_raise_view(struct hikari_workspace * workspace)434 hikari_workspace_raise_view(struct hikari_workspace *workspace)
435 {
436   FOCUS_GUARD(workspace, focus_view);
437 
438   struct hikari_group *group = focus_view->group;
439   struct hikari_view *first = hikari_group_first_view(group);
440 
441   hikari_view_raise(focus_view);
442 
443   hikari_view_damage_border(first);
444   hikari_indicator_damage(&hikari_server.indicator, focus_view);
445 }
446 
447 void
hikari_workspace_raise_group(struct hikari_workspace * workspace)448 hikari_workspace_raise_group(struct hikari_workspace *workspace)
449 {
450   FOCUS_GUARD(workspace, focus_view);
451 
452   hikari_group_raise(focus_view->group, focus_view);
453   hikari_indicator_damage(&hikari_server.indicator, focus_view);
454 }
455 
456 void
hikari_workspace_lower_view(struct hikari_workspace * workspace)457 hikari_workspace_lower_view(struct hikari_workspace *workspace)
458 {
459   FOCUS_GUARD(workspace, focus_view);
460 
461   hikari_server_unset_cycling();
462 
463   hikari_view_lower(focus_view);
464 
465   struct hikari_group *group = focus_view->group;
466   struct hikari_view *first = hikari_group_first_view(group);
467 
468   hikari_view_damage_border(first);
469 
470   hikari_server_cursor_focus();
471 }
472 
473 void
hikari_workspace_lower_group(struct hikari_workspace * workspace)474 hikari_workspace_lower_group(struct hikari_workspace *workspace)
475 {
476   FOCUS_GUARD(workspace, focus_view);
477 
478   hikari_group_lower(focus_view->group, focus_view);
479   hikari_indicator_damage(&hikari_server.indicator, focus_view);
480 
481   hikari_server_cursor_focus();
482 }
483 
484 void
hikari_workspace_hide_view(struct hikari_workspace * workspace)485 hikari_workspace_hide_view(struct hikari_workspace *workspace)
486 {
487   FOCUS_GUARD(workspace, focus_view);
488 
489   hikari_view_hide(focus_view);
490 
491   hikari_server_cursor_focus();
492 }
493 
494 void
hikari_workspace_only_view(struct hikari_workspace * workspace)495 hikari_workspace_only_view(struct hikari_workspace *workspace)
496 {
497   FOCUS_GUARD(workspace, focus_view);
498 
499   struct hikari_view *view = NULL;
500   struct hikari_view *view_tmp;
501   wl_list_for_each_safe (view, view_tmp, &workspace->views, workspace_views) {
502     if (view != focus_view) {
503       hikari_view_hide(view);
504     }
505   }
506 
507   hikari_server_cursor_focus();
508 }
509 
510 void
hikari_workspace_move_resize_view(struct hikari_workspace * workspace,int dx,int dy,int dwidth,int dheight)511 hikari_workspace_move_resize_view(
512     struct hikari_workspace *workspace, int dx, int dy, int dwidth, int dheight)
513 {
514   FOCUS_GUARD(workspace, focus_view);
515 
516   hikari_server_set_cycling();
517 
518   hikari_view_move_resize(focus_view, dx, dy, dwidth, dheight);
519 }
520 
521 #define MOVE(pos)                                                              \
522   void hikari_workspace_move_view_##pos(struct hikari_workspace *workspace)    \
523   {                                                                            \
524     FOCUS_GUARD(workspace, focus_view);                                        \
525                                                                                \
526     hikari_server_set_cycling();                                               \
527                                                                                \
528     hikari_view_move_##pos(focus_view);                                        \
529   }
530 
531 MOVE(bottom_left)
MOVE(bottom_middle)532 MOVE(bottom_middle)
533 MOVE(bottom_right)
534 MOVE(center_left)
535 MOVE(center)
536 MOVE(center_right)
537 MOVE(top_left)
538 MOVE(top_middle)
539 MOVE(top_right)
540 #undef MOVE
541 
542 void
543 hikari_workspace_snap_view_up(struct hikari_workspace *workspace)
544 {
545   FOCUS_GUARD(workspace, focus_view);
546 
547   hikari_server_set_cycling();
548 
549   struct wlr_box *view_geometry = hikari_view_geometry(focus_view);
550 
551   int border = hikari_configuration->border;
552   int gap = hikari_configuration->gap;
553   int lookahead = view_geometry->y - border - gap;
554   int view_right = view_geometry->x + view_geometry->width;
555   int y;
556 
557   bool found = false;
558   struct hikari_view *view = NULL;
559   struct wlr_box *geometry = NULL;
560   wl_list_for_each (view, &workspace->views, workspace_views) {
561     if (view == focus_view) {
562       continue;
563     }
564 
565     geometry = hikari_view_geometry(view);
566 
567     int right = geometry->x + geometry->width;
568 
569     if (geometry->y + geometry->height + border < lookahead &&
570         ((view_right >= geometry->x && geometry->x >= view_geometry->x) ||
571             (right >= view_geometry->x && view_geometry->x >= geometry->x))) {
572       found = true;
573       y = geometry->y + gap + geometry->height + border;
574       break;
575     }
576   }
577 
578   if (!found) {
579     struct hikari_output *output = focus_view->output;
580     y = border + output->usable_area.y;
581   }
582 
583   hikari_view_move_absolute(focus_view, view_geometry->x, y);
584 }
585 
586 void
hikari_workspace_snap_view_down(struct hikari_workspace * workspace)587 hikari_workspace_snap_view_down(struct hikari_workspace *workspace)
588 {
589   FOCUS_GUARD(workspace, focus_view);
590 
591   hikari_server_set_cycling();
592 
593   struct wlr_box *view_geometry = hikari_view_geometry(focus_view);
594 
595   int border = hikari_configuration->border;
596   int gap = hikari_configuration->gap;
597   int lookahead = view_geometry->y + view_geometry->height + border + gap;
598   int view_right = view_geometry->x + view_geometry->width;
599   int y;
600 
601   bool found = false;
602   struct hikari_view *view = NULL;
603   struct wlr_box *geometry = NULL;
604   wl_list_for_each (view, &workspace->views, workspace_views) {
605     if (view == focus_view) {
606       continue;
607     }
608 
609     geometry = hikari_view_geometry(view);
610 
611     int right = geometry->x + geometry->width;
612 
613     if (geometry->y > lookahead &&
614         ((view_right >= geometry->x && geometry->x >= view_geometry->x) ||
615             (right >= view_geometry->x && view_geometry->x >= geometry->x))) {
616       found = true;
617       y = geometry->y - gap - border - view_geometry->height;
618       break;
619     }
620   }
621 
622   if (!found) {
623     struct hikari_output *output = focus_view->output;
624     y = output->usable_area.y + output->usable_area.height -
625         view_geometry->height - border;
626   }
627 
628   hikari_view_move_absolute(focus_view, view_geometry->x, y);
629 }
630 
631 void
hikari_workspace_snap_view_left(struct hikari_workspace * workspace)632 hikari_workspace_snap_view_left(struct hikari_workspace *workspace)
633 {
634   FOCUS_GUARD(workspace, focus_view);
635 
636   hikari_server_set_cycling();
637 
638   struct wlr_box *view_geometry = hikari_view_geometry(focus_view);
639 
640   int border = hikari_configuration->border;
641   int gap = hikari_configuration->gap;
642   int lookahead = view_geometry->x - gap - border;
643   int view_bottom = view_geometry->y + view_geometry->height;
644   int x;
645 
646   bool found = false;
647   struct hikari_view *view = NULL;
648   struct wlr_box *geometry = NULL;
649   wl_list_for_each (view, &workspace->views, workspace_views) {
650     if (view == focus_view) {
651       continue;
652     }
653 
654     geometry = hikari_view_geometry(view);
655 
656     int bottom = geometry->y + geometry->height;
657 
658     if (geometry->x + geometry->width + border < lookahead &&
659         ((view_bottom >= geometry->y && geometry->y >= view_geometry->y) ||
660             (bottom >= view_geometry->y && view_geometry->y >= geometry->y))) {
661       found = true;
662       x = geometry->x + geometry->width + gap + border;
663       break;
664     }
665   }
666 
667   if (!found) {
668     struct hikari_output *output = focus_view->output;
669     x = border + output->usable_area.x;
670   }
671 
672   hikari_view_move_absolute(focus_view, x, view_geometry->y);
673 }
674 
675 void
hikari_workspace_snap_view_right(struct hikari_workspace * workspace)676 hikari_workspace_snap_view_right(struct hikari_workspace *workspace)
677 {
678   FOCUS_GUARD(workspace, focus_view);
679 
680   hikari_server_set_cycling();
681 
682   struct wlr_box *view_geometry = hikari_view_geometry(focus_view);
683 
684   int border = hikari_configuration->border;
685   int gap = hikari_configuration->gap;
686   int lookahead = view_geometry->x + view_geometry->width + gap + border;
687   int view_bottom = view_geometry->y + view_geometry->height;
688   int x;
689 
690   bool found = false;
691   struct hikari_view *view = NULL;
692   struct wlr_box *geometry = NULL;
693   wl_list_for_each (view, &workspace->views, workspace_views) {
694     if (view == focus_view) {
695       continue;
696     }
697 
698     geometry = hikari_view_geometry(view);
699 
700     int bottom = geometry->y + geometry->height;
701 
702     if (geometry->x > lookahead &&
703         ((view_bottom >= geometry->y && geometry->y >= view_geometry->y) ||
704             (bottom >= view_geometry->y && view_geometry->y >= geometry->y))) {
705       found = true;
706       x = geometry->x - gap - view_geometry->width - border;
707       break;
708     }
709   }
710 
711   if (!found) {
712     struct hikari_output *output = focus_view->output;
713     x = output->usable_area.x + output->usable_area.width -
714         view_geometry->width - border;
715   }
716 
717   hikari_view_move_absolute(focus_view, x, view_geometry->y);
718 }
719 
720 static void
pin_to_sheet(struct hikari_workspace * workspace,struct hikari_sheet * sheet)721 pin_to_sheet(struct hikari_workspace *workspace, struct hikari_sheet *sheet)
722 {
723   FOCUS_GUARD(workspace, focus_view);
724 
725   hikari_view_pin_to_sheet(focus_view, sheet);
726 
727   if (!hikari_view_is_hidden(focus_view)) {
728     struct hikari_output *output = workspace->output;
729 
730     hikari_indicator_update_sheet(
731         &hikari_server.indicator, output, focus_view->sheet, focus_view->flags);
732   }
733 }
734 
735 #define PIN_TO_SHEET(name, sheet)                                              \
736   void hikari_workspace_pin_view_to_sheet_##name(                              \
737       struct hikari_workspace *workspace)                                      \
738   {                                                                            \
739     pin_to_sheet(workspace, sheet);                                            \
740   }
741 
742 PIN_TO_SHEET(0, &workspace->sheets[0])
743 PIN_TO_SHEET(1, &workspace->sheets[1])
744 PIN_TO_SHEET(2, &workspace->sheets[2])
745 PIN_TO_SHEET(3, &workspace->sheets[3])
746 PIN_TO_SHEET(4, &workspace->sheets[4])
747 PIN_TO_SHEET(5, &workspace->sheets[5])
748 PIN_TO_SHEET(6, &workspace->sheets[6])
749 PIN_TO_SHEET(7, &workspace->sheets[7])
750 PIN_TO_SHEET(8, &workspace->sheets[8])
751 PIN_TO_SHEET(9, &workspace->sheets[9])
752 PIN_TO_SHEET(alternate, workspace->alternate_sheet)
753 PIN_TO_SHEET(current, workspace->sheet)
754 PIN_TO_SHEET(next, hikari_sheet_next(workspace->sheet))
755 PIN_TO_SHEET(prev, hikari_sheet_prev(workspace->sheet))
756 #undef PIN_TO_SHEET
757 
758 #define MAXIMIZE(n)                                                            \
759   void hikari_workspace_toggle_view_##n##_maximize(                            \
760       struct hikari_workspace *workspace)                                      \
761   {                                                                            \
762     FOCUS_GUARD(workspace, focus_view);                                        \
763                                                                                \
764     hikari_view_toggle_##n##_maximize(focus_view);                             \
765   }
766 
MAXIMIZE(full)767 MAXIMIZE(full)
768 MAXIMIZE(vertical)
769 MAXIMIZE(horizontal)
770 #undef MAXIMIZE
771 
772 void
773 hikari_workspace_toggle_view_invisible(struct hikari_workspace *workspace)
774 {
775   FOCUS_GUARD(workspace, focus_view);
776 
777   hikari_view_toggle_invisible(focus_view);
778 
779   struct hikari_output *output = workspace->output;
780   struct hikari_indicator *indicator = &hikari_server.indicator;
781   struct wlr_box *geometry = hikari_view_border_geometry(focus_view);
782 
783   if (hikari_server_is_indicating()) {
784     hikari_indicator_damage_sheet(indicator, output, geometry);
785   }
786 
787   hikari_indicator_update_sheet(
788       indicator, output, focus_view->sheet, focus_view->flags);
789 
790   if (hikari_server_is_indicating()) {
791     hikari_indicator_damage_sheet(indicator, output, geometry);
792   }
793 }
794 
795 void
hikari_workspace_toggle_view_public(struct hikari_workspace * workspace)796 hikari_workspace_toggle_view_public(struct hikari_workspace *workspace)
797 {
798   FOCUS_GUARD(workspace, focus_view);
799 
800   hikari_view_toggle_public(focus_view);
801 
802   struct hikari_output *output = workspace->output;
803   struct hikari_indicator *indicator = &hikari_server.indicator;
804   struct wlr_box *geometry = hikari_view_border_geometry(focus_view);
805 
806   if (hikari_server_is_indicating()) {
807     hikari_indicator_damage_sheet(indicator, output, geometry);
808   }
809 
810   hikari_indicator_update_sheet(
811       indicator, output, focus_view->sheet, focus_view->flags);
812 
813   if (hikari_server_is_indicating()) {
814     hikari_indicator_damage_sheet(indicator, output, geometry);
815   }
816 }
817 
818 void
hikari_workspace_toggle_view_floating(struct hikari_workspace * workspace)819 hikari_workspace_toggle_view_floating(struct hikari_workspace *workspace)
820 {
821   FOCUS_GUARD(workspace, focus_view);
822 
823   hikari_view_toggle_floating(focus_view);
824 
825   struct hikari_output *output = workspace->output;
826   struct hikari_indicator *indicator = &hikari_server.indicator;
827   struct wlr_box *geometry = hikari_view_border_geometry(focus_view);
828 
829   if (hikari_server_is_indicating()) {
830     hikari_indicator_damage_sheet(indicator, output, geometry);
831   }
832 
833   hikari_indicator_update_sheet(
834       indicator, output, focus_view->sheet, focus_view->flags);
835 
836   if (hikari_server_is_indicating()) {
837     hikari_indicator_damage_sheet(indicator, output, geometry);
838   }
839 }
840 
841 void
hikari_workspace_show_invisible_sheet_views(struct hikari_workspace * workspace)842 hikari_workspace_show_invisible_sheet_views(struct hikari_workspace *workspace)
843 {
844   hikari_workspace_clear(workspace);
845   hikari_sheet_show_invisible(workspace->sheet);
846   hikari_server_cursor_focus();
847 }
848 
849 void
hikari_workspace_reset_view_geometry(struct hikari_workspace * workspace)850 hikari_workspace_reset_view_geometry(struct hikari_workspace *workspace)
851 {
852   FOCUS_GUARD(workspace, focus_view);
853 
854   hikari_view_reset_geometry(focus_view);
855 }
856 
857 #define VIEW_EXCHANGE(link)                                                    \
858   void hikari_workspace_exchange_##link##_view(                                \
859       struct hikari_workspace *workspace)                                      \
860   {                                                                            \
861     struct hikari_view *focus_view = workspace->focus_view;                    \
862     struct hikari_layout *layout = workspace->sheet->layout;                   \
863                                                                                \
864     if (focus_view == NULL || layout == NULL ||                                \
865         !hikari_view_is_tiled(focus_view)) {                                   \
866       return;                                                                  \
867     }                                                                          \
868                                                                                \
869     struct hikari_view *link = hikari_tile_##link##_view(focus_view->tile);    \
870                                                                                \
871     assert(!hikari_view_is_hidden(link));                                      \
872                                                                                \
873     if (focus_view == link) {                                                  \
874       return;                                                                  \
875     }                                                                          \
876                                                                                \
877     hikari_view_exchange(focus_view, link);                                    \
878   }
879 
880 VIEW_EXCHANGE(prev)
VIEW_EXCHANGE(next)881 VIEW_EXCHANGE(next)
882 #undef VIEW_EXCHANGE
883 
884 void
885 hikari_workspace_exchange_main_layout_view(struct hikari_workspace *workspace)
886 {
887   struct hikari_view *focus_view = workspace->focus_view;
888   struct hikari_layout *layout = workspace->sheet->layout;
889 
890   if (focus_view == NULL || layout == NULL ||
891       !hikari_view_is_tiled(focus_view) || hikari_view_is_dirty(focus_view)) {
892     return;
893   }
894 
895   struct hikari_view *first = hikari_layout_first_view(layout);
896 
897   if (focus_view == first || hikari_view_is_hidden(first) ||
898       hikari_view_is_dirty(first)) {
899     return;
900   }
901 
902   hikari_view_exchange(focus_view, first);
903   hikari_view_center_cursor(first);
904 }
905 
906 void
hikari_workspace_only_group(struct hikari_workspace * workspace)907 hikari_workspace_only_group(struct hikari_workspace *workspace)
908 {
909   FOCUS_GUARD(workspace, focus_view);
910 
911   struct hikari_group *group = focus_view->group;
912 
913   struct hikari_view *view = NULL, *view_temp = NULL;
914   wl_list_for_each_safe (view, view_temp, &workspace->views, workspace_views) {
915     if (view->group != group) {
916       hikari_view_hide(view);
917     }
918   }
919 }
920 
921 void
hikari_workspace_sheet_show_group(struct hikari_workspace * workspace)922 hikari_workspace_sheet_show_group(struct hikari_workspace *workspace)
923 {
924   FOCUS_GUARD(workspace, focus_view);
925 
926   struct hikari_group *group = focus_view->group;
927   hikari_view_raise(focus_view);
928   hikari_workspace_clear(workspace);
929   hikari_sheet_show_group(workspace->sheet, group);
930   hikari_server_cursor_focus();
931 }
932 
933 void
hikari_workspace_show_group(struct hikari_workspace * workspace)934 hikari_workspace_show_group(struct hikari_workspace *workspace)
935 {
936   FOCUS_GUARD(workspace, focus_view);
937 
938   struct hikari_group *group = focus_view->group;
939   hikari_workspace_clear(workspace);
940   hikari_group_show(group);
941   hikari_server_cursor_focus();
942 }
943 
944 #define SHOW_VIEWS(cond)                                                       \
945   {                                                                            \
946     struct hikari_view *view, *view_temp, *top = NULL;                         \
947     wl_list_for_each_reverse_safe (                                            \
948         view, view_temp, &workspace->output->views, output_views) {            \
949       if (cond) {                                                              \
950         if (top == view) {                                                     \
951           break;                                                               \
952         }                                                                      \
953                                                                                \
954         if (top == NULL) {                                                     \
955           top = view;                                                          \
956         }                                                                      \
957                                                                                \
958         hikari_view_show(view);                                                \
959       }                                                                        \
960     }                                                                          \
961   }
962 
963 void
hikari_workspace_show_all(struct hikari_workspace * workspace)964 hikari_workspace_show_all(struct hikari_workspace *workspace)
965 {
966   hikari_workspace_clear(workspace);
967   SHOW_VIEWS(true);
968   hikari_server_cursor_focus();
969 }
970 
971 void
hikari_workspace_show_invisible(struct hikari_workspace * workspace)972 hikari_workspace_show_invisible(struct hikari_workspace *workspace)
973 {
974   hikari_workspace_clear(workspace);
975   SHOW_VIEWS(hikari_view_is_invisible(view));
976   hikari_server_cursor_focus();
977 }
978 #undef SHOW_VIEWS
979 
980 void
hikari_workspace_hide_group(struct hikari_workspace * workspace)981 hikari_workspace_hide_group(struct hikari_workspace *workspace)
982 {
983   FOCUS_GUARD(workspace, focus_view);
984 
985   struct hikari_group *group = focus_view->group;
986   hikari_group_hide(group);
987   hikari_server_cursor_focus();
988 }
989 
990 void
hikari_workspace_show_all_sheet_views(struct hikari_workspace * workspace)991 hikari_workspace_show_all_sheet_views(struct hikari_workspace *workspace)
992 {
993   struct hikari_sheet *sheet = workspace->sheet;
994 
995   hikari_workspace_clear(workspace);
996   hikari_sheet_show_all(sheet);
997   hikari_server_cursor_focus();
998 }
999 
1000 void
hikari_workspace_apply_split(struct hikari_workspace * workspace,struct hikari_split * split)1001 hikari_workspace_apply_split(
1002     struct hikari_workspace *workspace, struct hikari_split *split)
1003 {
1004   assert(workspace != NULL);
1005   assert(split != NULL);
1006 
1007   struct hikari_view *focus_view = workspace->focus_view;
1008   struct hikari_sheet *sheet = workspace->sheet;
1009 
1010   if (focus_view != NULL && focus_view->sheet == sheet &&
1011       hikari_view_is_tileable(focus_view)) {
1012     hikari_view_raise(focus_view);
1013 
1014     assert(focus_view == hikari_sheet_first_tileable_view(sheet));
1015   }
1016 
1017   hikari_workspace_display_sheet_current(workspace);
1018   hikari_sheet_apply_split(sheet, split);
1019 }
1020 
1021 void
hikari_workspace_center_cursor(struct hikari_workspace * workspace)1022 hikari_workspace_center_cursor(struct hikari_workspace *workspace)
1023 {
1024   struct hikari_output *output = workspace->output;
1025   hikari_cursor_center(&hikari_server.cursor, output, &output->usable_area);
1026 }
1027 
1028 struct hikari_view *
hikari_workspace_first_view(struct hikari_workspace * workspace)1029 hikari_workspace_first_view(struct hikari_workspace *workspace)
1030 {
1031   assert(workspace != NULL);
1032 
1033   struct hikari_view *view;
1034   if (workspace->focus_view != NULL) {
1035     view = workspace->focus_view;
1036   } else {
1037     view = hikari_sheet_first_view(workspace->sheet);
1038   }
1039 
1040   return view;
1041 }
1042 #undef FOCUS_GUARD
1043