1 #include <hikari/view.h>
2 
3 #include <assert.h>
4 #include <string.h>
5 
6 #include <wlr/types/wlr_cursor.h>
7 
8 #include <hikari/color.h>
9 #include <hikari/configuration.h>
10 #include <hikari/geometry.h>
11 #include <hikari/group.h>
12 #include <hikari/indicator.h>
13 #include <hikari/layout.h>
14 #include <hikari/mark.h>
15 #include <hikari/memory.h>
16 #include <hikari/operation.h>
17 #include <hikari/output.h>
18 #include <hikari/server.h>
19 #include <hikari/sheet.h>
20 #include <hikari/tile.h>
21 #include <hikari/view_config.h>
22 #include <hikari/workspace.h>
23 #include <hikari/xdg_view.h>
24 #include <hikari/xwayland_view.h>
25 
26 #define VIEW(name, link)                                                       \
27   static struct hikari_view *name##_view(void)                                 \
28   {                                                                            \
29     struct wl_list *views = hikari_server.visible_views.link;                  \
30                                                                                \
31     if (!wl_list_empty(views)) {                                               \
32       struct hikari_view *view;                                                \
33       view = wl_container_of(views, view, visible_server_views);               \
34       return view;                                                             \
35     }                                                                          \
36                                                                                \
37     return NULL;                                                               \
38   }                                                                            \
39                                                                                \
40   static bool is_##name##_view(struct hikari_view *view)                       \
41   {                                                                            \
42     return view == name##_view();                                              \
43   }
44 
VIEW(first,next)45 VIEW(first, next)
46 VIEW(last, prev)
47 #undef VIEW
48 
49 static void
50 move_to_top(struct hikari_view *view)
51 {
52   assert(view != NULL);
53   assert(hikari_view_is_mapped(view));
54 
55   wl_list_remove(&view->sheet_views);
56   wl_list_insert(&view->sheet->views, &view->sheet_views);
57 
58   wl_list_remove(&view->group_views);
59   wl_list_insert(&view->group->views, &view->group_views);
60 
61   wl_list_remove(&view->output_views);
62   wl_list_insert(&view->output->views, &view->output_views);
63 }
64 
65 static void
place_visibly_above(struct hikari_view * view,struct hikari_workspace * workspace)66 place_visibly_above(
67     struct hikari_view *view, struct hikari_workspace *workspace)
68 {
69   assert(hikari_view_is_forced(view) ? hikari_view_is_hidden(view)
70                                      : !hikari_view_is_hidden(view));
71 
72   wl_list_remove(&view->visible_server_views);
73   wl_list_insert(&hikari_server.visible_views, &view->visible_server_views);
74 
75   wl_list_remove(&view->visible_group_views);
76   wl_list_insert(&view->group->visible_views, &view->visible_group_views);
77 
78   wl_list_remove(&view->group->visible_server_groups);
79   wl_list_insert(
80       &hikari_server.visible_groups, &view->group->visible_server_groups);
81 
82   wl_list_remove(&view->workspace_views);
83   wl_list_insert(&workspace->views, &view->workspace_views);
84 }
85 
86 static void
raise_view(struct hikari_view * view)87 raise_view(struct hikari_view *view)
88 {
89   assert(view != NULL);
90 
91   move_to_top(view);
92   place_visibly_above(view, view->sheet->workspace);
93 }
94 
95 static void
refresh_border_geometry(struct hikari_view * view)96 refresh_border_geometry(struct hikari_view *view)
97 {
98   assert(view != NULL);
99   hikari_border_refresh_geometry(&view->border, view->current_geometry);
100   hikari_indicator_frame_refresh_geometry(&view->indicator_frame, view);
101 }
102 
103 static inline void
move_view_constrained(struct hikari_view * view,struct wlr_box * geometry,int x,int y)104 move_view_constrained(
105     struct hikari_view *view, struct wlr_box *geometry, int x, int y)
106 {
107   if (!hikari_view_is_hidden(view)) {
108     hikari_view_damage_whole(view);
109     hikari_indicator_damage(&hikari_server.indicator, view);
110   }
111 
112   hikari_geometry_constrain_relative(
113       geometry, &view->output->usable_area, x, y);
114 }
115 
116 static void
move_view(struct hikari_view * view,struct wlr_box * geometry,int x,int y)117 move_view(struct hikari_view *view, struct wlr_box *geometry, int x, int y)
118 {
119   if (view->maximized_state != NULL) {
120     struct wlr_box *usable_area;
121 
122     switch (view->maximized_state->maximization) {
123       case HIKARI_MAXIMIZATION_FULLY_MAXIMIZED:
124         return;
125 
126       case HIKARI_MAXIMIZATION_VERTICALLY_MAXIMIZED:
127         usable_area = &view->output->usable_area;
128 
129         if (y != usable_area->y) {
130           return;
131         }
132 
133         move_view_constrained(view, geometry, x, y);
134         view->geometry.x = x;
135         break;
136 
137       case HIKARI_MAXIMIZATION_HORIZONTALLY_MAXIMIZED:
138         usable_area = &view->output->usable_area;
139 
140         if (x != usable_area->x) {
141           return;
142         }
143 
144         move_view_constrained(view, geometry, x, y);
145         view->geometry.y = y;
146         break;
147     }
148   } else {
149     move_view_constrained(view, geometry, x, y);
150   }
151 
152 #ifdef HAVE_XWAYLAND
153   if (view->move != NULL) {
154     view->move(view, geometry->x, geometry->y);
155   }
156 #endif
157 
158   refresh_border_geometry(view);
159 
160   if (!hikari_view_is_hidden(view)) {
161     hikari_view_damage_whole(view);
162     hikari_indicator_damage(&hikari_server.indicator, view);
163   }
164 }
165 
166 static void
increase_group_visiblity(struct hikari_view * view)167 increase_group_visiblity(struct hikari_view *view)
168 {
169   assert(hikari_view_is_forced(view) ? hikari_view_is_hidden(view)
170                                      : !hikari_view_is_hidden(view));
171 
172   struct hikari_group *group = view->group;
173 
174   if (wl_list_empty(&group->visible_views)) {
175     wl_list_insert(
176         &hikari_server.visible_groups, &group->visible_server_groups);
177   }
178 
179   wl_list_init(&view->visible_group_views);
180 }
181 
182 static void
decrease_group_visibility(struct hikari_view * view)183 decrease_group_visibility(struct hikari_view *view)
184 {
185   struct hikari_group *group = view->group;
186 
187   wl_list_remove(&view->visible_group_views);
188 
189   if (wl_list_empty(&group->visible_views)) {
190     wl_list_remove(&group->visible_server_groups);
191   }
192 }
193 
194 static void
hide(struct hikari_view * view)195 hide(struct hikari_view *view)
196 {
197   decrease_group_visibility(view);
198 
199   wl_list_remove(&view->workspace_views);
200   wl_list_init(&view->workspace_views);
201 
202   wl_list_remove(&view->visible_server_views);
203   wl_list_init(&view->visible_server_views);
204 
205   hikari_view_set_hidden(view);
206 }
207 
208 static void
detach_from_group(struct hikari_view * view)209 detach_from_group(struct hikari_view *view)
210 {
211   struct hikari_group *group = view->group;
212 
213   wl_list_remove(&view->group_views);
214   wl_list_init(&view->group_views);
215 
216   if (wl_list_empty(&group->views)) {
217     hikari_group_fini(group);
218     hikari_free(group);
219   }
220 }
221 
222 static void
remove_from_group(struct hikari_view * view)223 remove_from_group(struct hikari_view *view)
224 {
225   assert(!hikari_view_is_hidden(view));
226 
227   if (!hikari_view_is_hidden(view)) {
228     decrease_group_visibility(view);
229   }
230 
231   detach_from_group(view);
232 }
233 
234 static void
cancel_tile(struct hikari_view * view)235 cancel_tile(struct hikari_view *view)
236 {
237   if (hikari_view_is_tiling(view)) {
238     struct hikari_tile *tile = view->pending_operation.tile;
239 
240     hikari_tile_detach(tile);
241     hikari_free(tile);
242     view->pending_operation.tile = NULL;
243   }
244 }
245 
246 static inline void
guarded_resize(struct hikari_view * view,struct hikari_operation * op,void (* f)(struct hikari_view *,struct hikari_operation *))247 guarded_resize(struct hikari_view *view,
248     struct hikari_operation *op,
249     void (*f)(struct hikari_view *, struct hikari_operation *))
250 {
251   op->serial = view->resize(view, op->geometry.width, op->geometry.height);
252 
253   if (op->serial == 0) {
254     f(view, op);
255   } else {
256     hikari_view_set_dirty(view);
257   }
258 }
259 
260 static inline void
resize(struct hikari_view * view,struct hikari_operation * op,void (* f)(struct hikari_view *,struct hikari_operation *))261 resize(struct hikari_view *view,
262     struct hikari_operation *op,
263     void (*f)(struct hikari_view *, struct hikari_operation *))
264 {
265 #ifdef HAVE_XWAYLAND
266   if (view->move_resize != NULL) {
267     op->serial = 0;
268     view->move_resize(view,
269         op->geometry.x,
270         op->geometry.y,
271         op->geometry.width,
272         op->geometry.height);
273 
274     hikari_view_set_dirty(view);
275   } else {
276     guarded_resize(view, op, f);
277   }
278 #else
279   guarded_resize(view, op, f);
280 #endif
281 }
282 
283 static void
commit_pending_geometry(struct hikari_view * view,struct wlr_box * pending_geometry)284 commit_pending_geometry(
285     struct hikari_view *view, struct wlr_box *pending_geometry)
286 {
287   hikari_view_refresh_geometry(view, pending_geometry);
288 
289   hikari_indicator_damage(&hikari_server.indicator, view);
290   hikari_view_damage_whole(view);
291 }
292 
293 static void
commit_pending_operation(struct hikari_view * view,struct hikari_operation * operation)294 commit_pending_operation(
295     struct hikari_view *view, struct hikari_operation *operation)
296 {
297   if (!hikari_view_is_hidden(view)) {
298     if (!hikari_view_is_forced(view)) {
299       raise_view(view);
300     } else {
301       move_to_top(view);
302     }
303 
304     commit_pending_geometry(view, &operation->geometry);
305 
306     if (operation->center) {
307       hikari_view_center_cursor(view);
308       hikari_server_cursor_focus();
309     }
310   } else {
311     hikari_view_refresh_geometry(view, &operation->geometry);
312 
313     if (hikari_sheet_is_visible(view->sheet)) {
314       if (!hikari_view_is_forced(view)) {
315         hikari_view_show(view);
316       } else {
317         raise_view(view);
318       }
319 
320       if (operation->center) {
321         hikari_view_center_cursor(view);
322         hikari_server_cursor_focus();
323       }
324     } else {
325       if (!hikari_view_is_forced(view)) {
326         move_to_top(view);
327       } else {
328         raise_view(view);
329       }
330     }
331   }
332 }
333 
334 static void
commit_reset(struct hikari_view * view,struct hikari_operation * operation)335 commit_reset(struct hikari_view *view, struct hikari_operation *operation)
336 {
337   hikari_indicator_damage(&hikari_server.indicator, view);
338 
339   if (hikari_view_is_tiled(view)) {
340     assert(!hikari_tile_is_attached(view->tile));
341     hikari_free(view->tile);
342     view->tile = NULL;
343   }
344 
345   if (hikari_view_is_maximized(view)) {
346     hikari_maximized_state_destroy(view->maximized_state);
347     view->maximized_state = NULL;
348 
349     if (!view->use_csd) {
350       if (view == hikari_server.workspace->focus_view) {
351         view->border.state = HIKARI_BORDER_ACTIVE;
352       } else {
353         view->border.state = HIKARI_BORDER_INACTIVE;
354       }
355     }
356   }
357 
358   commit_pending_operation(view, operation);
359 }
360 
361 static void
queue_reset(struct hikari_view * view,bool center)362 queue_reset(struct hikari_view *view, bool center)
363 {
364   struct wlr_box *view_geometry = hikari_view_geometry(view);
365   struct hikari_operation *op = &view->pending_operation;
366   struct hikari_tile *tile = view->tile;
367   struct wlr_box *geometry = &view->geometry;
368 
369   cancel_tile(view);
370 
371   if (hikari_view_is_tiled(view) && hikari_tile_is_attached(tile)) {
372     hikari_tile_detach(tile);
373   }
374 
375   op->type = HIKARI_OPERATION_TYPE_RESET;
376   op->center = center;
377   memcpy(&op->geometry, geometry, sizeof(struct wlr_box));
378 
379   if (view_geometry->width == geometry->width &&
380       view_geometry->height == geometry->height) {
381     hikari_view_set_dirty(view);
382     hikari_view_commit_pending_operation(view, view_geometry);
383   } else {
384     resize(view, op, commit_reset);
385   }
386 
387   assert(
388       hikari_view_is_tiled(view) ? !hikari_tile_is_attached(view->tile) : true);
389 }
390 
391 static void
clear_focus(struct hikari_view * view)392 clear_focus(struct hikari_view *view)
393 {
394   if (hikari_view_is_focus_view(view)) {
395     if (hikari_view_has_focus(view)) {
396       assert(!hikari_server_in_lock_mode());
397 
398       if (!hikari_server_in_normal_mode()) {
399         hikari_server_enter_normal_mode(NULL);
400       }
401 
402       hikari_workspace_focus_view(hikari_server.workspace, NULL);
403     } else {
404       view->sheet->workspace->focus_view = NULL;
405     }
406   }
407 }
408 
409 void
hikari_view_init(struct hikari_view * view,bool child,struct hikari_workspace * workspace)410 hikari_view_init(
411     struct hikari_view *view, bool child, struct hikari_workspace *workspace)
412 {
413 #if !defined(NDEBUG)
414   printf("VIEW INIT %p\n", view);
415 #endif
416   view->flags = hikari_view_hidden_flag;
417   view->border.state = HIKARI_BORDER_INACTIVE;
418   view->sheet = NULL;
419   view->mark = NULL;
420   view->surface = NULL;
421   view->maximized_state = NULL;
422   view->output = NULL;
423   view->group = NULL;
424   view->title = NULL;
425   view->tile = NULL;
426   view->id = NULL;
427   view->use_csd = false;
428   view->child = child;
429   view->current_geometry = &view->geometry;
430   view->current_unmaximized_geometry = &view->geometry;
431 
432   hikari_view_unset_dirty(view);
433   view->pending_operation.tile = NULL;
434 
435   wl_list_init(&view->children);
436 }
437 
438 void
hikari_view_fini(struct hikari_view * view)439 hikari_view_fini(struct hikari_view *view)
440 {
441   assert(hikari_view_is_hidden(view));
442   assert(!hikari_view_is_mapped(view));
443   assert(!hikari_view_is_forced(view));
444 
445 #if !defined(NDEBUG)
446   printf("DESTROY VIEW %p\n", view);
447 #endif
448 
449   hikari_free(view->title);
450   hikari_free(view->id);
451 
452   if (view->group != NULL) {
453     detach_from_group(view);
454   }
455 
456   if (view->sheet != NULL) {
457     wl_list_remove(&view->sheet_views);
458     wl_list_remove(&view->output_views);
459   }
460 
461   if (view->maximized_state != NULL) {
462     hikari_maximized_state_destroy(view->maximized_state);
463   }
464 
465   if (view->mark != NULL) {
466     hikari_mark_clear(view->mark);
467   }
468 
469   if (hikari_view_is_tiled(view)) {
470     struct hikari_tile *tile = view->tile;
471     hikari_tile_detach(tile);
472     hikari_free(tile);
473     view->tile = NULL;
474   }
475 
476   cancel_tile(view);
477 }
478 
479 void
hikari_view_set_title(struct hikari_view * view,const char * title)480 hikari_view_set_title(struct hikari_view *view, const char *title)
481 {
482   hikari_free(view->title);
483 
484   if (title != NULL) {
485     view->title = hikari_malloc(strlen(title) + 1);
486     strcpy(view->title, title);
487 
488     if (hikari_server.workspace->focus_view == view) {
489       assert(!hikari_view_is_hidden(view));
490       struct hikari_output *output = view->output;
491       hikari_indicator_update_title(&hikari_server.indicator, output, title);
492     }
493   } else {
494     view->title = NULL;
495   }
496 }
497 
498 static void
set_app_id(struct hikari_view * view,const char * id)499 set_app_id(struct hikari_view *view, const char *id)
500 {
501   assert(view->id == NULL);
502   assert(id != NULL);
503 
504   view->id = hikari_malloc(strlen(id) + 1);
505 
506   strcpy(view->id, id);
507 }
508 
509 struct hikari_damage_data {
510   struct wlr_box *geometry;
511   struct wlr_surface *surface;
512 
513   struct hikari_view *view;
514   struct hikari_output *output;
515 
516   bool whole;
517 };
518 
519 static void
damage_whole_surface(struct wlr_surface * surface,int sx,int sy,void * data)520 damage_whole_surface(struct wlr_surface *surface, int sx, int sy, void *data)
521 {
522   struct hikari_damage_data *damage_data = data;
523   struct hikari_output *output = damage_data->output;
524   struct hikari_view *view = damage_data->view;
525 
526   if (view->surface == surface) {
527     hikari_view_damage_border(view);
528   } else {
529     struct wlr_box geometry;
530     memcpy(&geometry, damage_data->geometry, sizeof(struct wlr_box));
531 
532     geometry.x += sx;
533     geometry.y += sy;
534     geometry.width = surface->current.width;
535     geometry.height = surface->current.height;
536 
537     hikari_output_add_damage(output, &geometry);
538   }
539 }
540 
541 void
hikari_view_damage_whole(struct hikari_view * view)542 hikari_view_damage_whole(struct hikari_view *view)
543 {
544   assert(view != NULL);
545 
546   struct hikari_output *output = view->output;
547 
548   // TODO I know, this needs to be done A LOT better
549   if (view->use_csd) {
550     hikari_output_damage_whole(output);
551     return;
552   }
553 
554   struct hikari_damage_data damage_data;
555 
556   damage_data.geometry = hikari_view_geometry(view);
557   damage_data.output = output;
558   damage_data.view = view;
559 
560   hikari_node_for_each_surface(
561       (struct hikari_node *)view, damage_whole_surface, &damage_data);
562 }
563 
564 static struct wlr_box *
refresh_unmaximized_geometry(struct hikari_view * view)565 refresh_unmaximized_geometry(struct hikari_view *view)
566 {
567   assert(view != NULL);
568 
569   if (hikari_view_is_tiled(view)) {
570     return &view->tile->view_geometry;
571   } else {
572     return &view->geometry;
573   }
574 }
575 
576 static struct wlr_box *
refresh_geometry(struct hikari_view * view)577 refresh_geometry(struct hikari_view *view)
578 {
579   assert(view != NULL);
580 
581   if (view->maximized_state != NULL) {
582     return &view->maximized_state->geometry;
583   } else {
584     return refresh_unmaximized_geometry(view);
585   }
586 }
587 
588 static inline int
constrain_size(int min,int max,int value)589 constrain_size(int min, int max, int value)
590 {
591   if (value > max) {
592     return max;
593   } else if (value < min) {
594     return min;
595   } else {
596     return value;
597   }
598 }
599 
600 static void
commit_resize(struct hikari_view * view,struct hikari_operation * operation)601 commit_resize(struct hikari_view *view, struct hikari_operation *operation)
602 {
603   commit_pending_operation(view, operation);
604 }
605 
606 static void
queue_resize(struct hikari_view * view,struct wlr_box * geometry,int requested_x,int requested_y,int requested_width,int requested_height)607 queue_resize(struct hikari_view *view,
608     struct wlr_box *geometry,
609     int requested_x,
610     int requested_y,
611     int requested_width,
612     int requested_height)
613 {
614   struct hikari_operation *op = &view->pending_operation;
615 
616   int min_width;
617   int min_height;
618   int max_width;
619   int max_height;
620 
621   int new_width;
622   int new_height;
623 
624   if (view->maximized_state != NULL) {
625     switch (view->maximized_state->maximization) {
626       case HIKARI_MAXIMIZATION_FULLY_MAXIMIZED:
627         return;
628 
629       case HIKARI_MAXIMIZATION_VERTICALLY_MAXIMIZED:
630         view->constraints(
631             view, &min_width, &min_height, &max_width, &max_height);
632         new_width = constrain_size(min_width, max_width, requested_width);
633         new_height = geometry->height;
634         view->geometry.width = new_width;
635         break;
636 
637       case HIKARI_MAXIMIZATION_HORIZONTALLY_MAXIMIZED:
638         view->constraints(
639             view, &min_width, &min_height, &max_width, &max_height);
640         new_width = geometry->width;
641         new_height = constrain_size(min_height, max_height, requested_height);
642         view->geometry.height = new_height;
643         break;
644     }
645   } else {
646     view->constraints(view, &min_width, &min_height, &max_width, &max_height);
647     new_width = constrain_size(min_width, max_width, requested_width);
648     new_height = constrain_size(min_height, max_height, requested_height);
649   }
650 
651   struct hikari_output *output = view->output;
652 
653   op->type = HIKARI_OPERATION_TYPE_RESIZE;
654   op->geometry.width = new_width;
655   op->geometry.height = new_height;
656   op->center = false;
657 
658   hikari_geometry_constrain_relative(
659       &op->geometry, &output->usable_area, requested_x, requested_y);
660 
661   resize(view, op, commit_resize);
662 }
663 
664 void
hikari_view_resize(struct hikari_view * view,int dwidth,int dheight)665 hikari_view_resize(struct hikari_view *view, int dwidth, int dheight)
666 {
667   assert(view != NULL);
668   assert(view->resize != NULL);
669   assert(view->constraints != NULL);
670 
671   if (hikari_view_is_dirty(view)) {
672     return;
673   }
674 
675   struct wlr_box *geometry = hikari_view_geometry(view);
676 
677   int requested_width = geometry->width + dwidth;
678   int requested_height = geometry->height + dheight;
679 
680   queue_resize(view,
681       geometry,
682       geometry->x,
683       geometry->y,
684       requested_width,
685       requested_height);
686 }
687 
688 void
hikari_view_resize_absolute(struct hikari_view * view,int width,int height)689 hikari_view_resize_absolute(struct hikari_view *view, int width, int height)
690 {
691   assert(view != NULL);
692   assert(view->resize != NULL);
693   assert(view->constraints != NULL);
694 
695   if (hikari_view_is_dirty(view)) {
696     return;
697   }
698 
699   struct wlr_box *geometry = hikari_view_geometry(view);
700   queue_resize(view, geometry, geometry->x, geometry->y, width, height);
701 }
702 
703 void
hikari_view_move_resize(struct hikari_view * view,int x,int y,int width,int height)704 hikari_view_move_resize(
705     struct hikari_view *view, int x, int y, int width, int height)
706 {
707   assert(view != NULL);
708   assert(view->resize != NULL);
709   assert(view->constraints != NULL);
710 
711   if (hikari_view_is_dirty(view)) {
712     return;
713   }
714 
715   struct wlr_box *geometry = hikari_view_geometry(view);
716 
717   int requested_x = geometry->x + x;
718   int requested_y = geometry->y + y;
719   int requested_width = geometry->width + width;
720   int requested_height = geometry->height + height;
721 
722   queue_resize(view,
723       geometry,
724       requested_x,
725       requested_y,
726       requested_width,
727       requested_height);
728 }
729 
730 void
hikari_view_move(struct hikari_view * view,int x,int y)731 hikari_view_move(struct hikari_view *view, int x, int y)
732 {
733   assert(view != NULL);
734 
735   struct wlr_box *geometry = hikari_view_geometry(view);
736 
737   move_view(view, geometry, geometry->x + x, geometry->y + y);
738 }
739 
740 void
hikari_view_move_absolute(struct hikari_view * view,int x,int y)741 hikari_view_move_absolute(struct hikari_view *view, int x, int y)
742 {
743   assert(view != NULL);
744 
745   struct wlr_box *geometry = hikari_view_geometry(view);
746 
747   move_view(view, geometry, x, y);
748 }
749 
750 #define MOVE(pos)                                                              \
751   void hikari_view_move_##pos(struct hikari_view *view)                        \
752   {                                                                            \
753     assert(view != NULL);                                                      \
754                                                                                \
755     struct hikari_output *output = view->output;                               \
756     struct wlr_box *usable_area = &output->usable_area;                        \
757     struct wlr_box *border_geometry = hikari_view_border_geometry(view);       \
758     struct wlr_box *geometry = hikari_view_geometry(view);                     \
759                                                                                \
760     int x;                                                                     \
761     int y;                                                                     \
762     hikari_geometry_position_##pos(border_geometry, usable_area, &x, &y);      \
763                                                                                \
764     move_view(view, geometry, x, y);                                           \
765   }
766 
767 MOVE(bottom_left)
MOVE(bottom_middle)768 MOVE(bottom_middle)
769 MOVE(bottom_right)
770 MOVE(center_left)
771 MOVE(center)
772 MOVE(center_right)
773 MOVE(top_left)
774 MOVE(top_middle)
775 MOVE(top_right)
776 #undef MOVE
777 
778 static void
779 new_subsurface_handler(struct wl_listener *listener, void *data)
780 {
781   struct hikari_view *view = wl_container_of(listener, view, new_subsurface);
782 
783   struct wlr_subsurface *wlr_subsurface = data;
784 
785   struct hikari_view_subsurface *view_subsurface =
786       hikari_malloc(sizeof(struct hikari_view_subsurface));
787 
788   hikari_view_subsurface_init(view_subsurface, view, wlr_subsurface);
789 }
790 
791 void
hikari_view_map(struct hikari_view * view,struct wlr_surface * surface)792 hikari_view_map(struct hikari_view *view, struct wlr_surface *surface)
793 {
794   assert(hikari_view_is_hidden(view));
795   assert(!hikari_view_is_unmanaged(view));
796   assert(!hikari_view_is_mapped(view));
797 
798   struct hikari_sheet *sheet = view->sheet;
799   struct hikari_output *output = view->output;
800   struct hikari_group *group;
801   bool focus;
802 
803   struct hikari_view_config *view_config =
804       hikari_configuration_resolve_view_config(hikari_configuration, view->id);
805 
806   view->surface = surface;
807 
808   view->new_subsurface.notify = new_subsurface_handler;
809   wl_signal_add(&surface->events.new_subsurface, &view->new_subsurface);
810 
811   struct wlr_subsurface *wlr_subsurface;
812   wl_list_for_each (wlr_subsurface, &surface->subsurfaces, parent_link) {
813     struct hikari_view_subsurface *subsurface =
814         (struct hikari_view_subsurface *)malloc(
815             sizeof(struct hikari_view_subsurface));
816     hikari_view_subsurface_init(subsurface, view, wlr_subsurface);
817   }
818 
819   if (view_config != NULL) {
820     struct hikari_mark *mark;
821     struct hikari_view_properties *properties =
822         hikari_view_config_resolve_properties(view_config, view->child);
823 
824     assert(properties != NULL);
825 
826     group = hikari_view_properties_resolve_group(properties, view->id);
827     mark = properties->mark;
828 
829     if (mark != NULL && mark->view == NULL) {
830       hikari_mark_set(mark, view);
831     }
832 
833     focus = properties->focus;
834   } else {
835     group = hikari_server_find_or_create_group(view->id);
836     focus = false;
837   }
838 
839   view->group = group;
840 
841   wl_list_insert(&sheet->views, &view->sheet_views);
842   wl_list_insert(&group->views, &view->group_views);
843   wl_list_insert(&output->views, &view->output_views);
844 
845   if (!hikari_server_in_lock_mode() || hikari_view_is_public(view)) {
846     hikari_view_show(view);
847 
848     if (focus) {
849       hikari_view_center_cursor(view);
850     }
851 
852     hikari_server_cursor_focus();
853   } else {
854     hikari_view_set_forced(view);
855     increase_group_visiblity(view);
856     raise_view(view);
857   }
858 }
859 
860 void
hikari_view_unmap(struct hikari_view * view)861 hikari_view_unmap(struct hikari_view *view)
862 {
863   assert(!hikari_view_is_unmanaged(view));
864   assert(hikari_view_is_mapped(view));
865 
866   wl_list_remove(&view->new_subsurface.link);
867 
868   struct hikari_view_child *child, *child_temp;
869   wl_list_for_each_safe (child, child_temp, &view->children, link) {
870     struct hikari_view_subsurface *subsurface =
871         (struct hikari_view_subsurface *)child;
872     hikari_view_subsurface_fini(subsurface);
873     hikari_free(subsurface);
874   }
875 
876   if (hikari_view_is_forced(view)) {
877     if (hikari_view_is_hidden(view)) {
878       hide(view);
879     } else {
880       hikari_view_damage_whole(view);
881       hikari_view_set_hidden(view);
882     }
883 
884     hikari_view_unset_forced(view);
885   }
886 
887   if (!hikari_view_is_hidden(view)) {
888     hikari_view_hide(view);
889     hikari_server_cursor_focus();
890   }
891 
892   assert(hikari_view_is_hidden(view));
893   assert(!hikari_view_is_forced(view));
894 
895   view->surface = NULL;
896 
897   struct hikari_mark *mark = view->mark;
898   if (mark != NULL) {
899     hikari_mark_clear(mark);
900   }
901 
902   detach_from_group(view);
903   view->group = NULL;
904 
905   cancel_tile(view);
906 
907   if (hikari_view_is_tiled(view)) {
908     struct wlr_box geometry;
909     struct hikari_tile *tile = view->tile;
910 
911     memcpy(&geometry, hikari_view_geometry(view), sizeof(struct wlr_box));
912 
913     if (hikari_tile_is_attached(tile)) {
914       hikari_tile_detach(tile);
915     }
916 
917     hikari_free(tile);
918     view->tile = NULL;
919 
920     hikari_view_refresh_geometry(view, &geometry);
921   }
922 
923   wl_list_remove(&view->sheet_views);
924   wl_list_init(&view->sheet_views);
925 
926   wl_list_remove(&view->output_views);
927   wl_list_init(&view->output_views);
928 
929   hikari_view_unset_dirty(view);
930 
931   assert(!hikari_view_is_tiling(view));
932   assert(!hikari_view_is_tiled(view));
933 }
934 
935 void
hikari_view_show(struct hikari_view * view)936 hikari_view_show(struct hikari_view *view)
937 {
938   assert(view != NULL);
939   assert(hikari_view_is_hidden(view));
940   assert(!hikari_view_is_forced(view));
941 
942 #if !defined(NDEBUG)
943   printf("SHOW %p\n", view);
944 #endif
945   hikari_view_unset_hidden(view);
946 
947   increase_group_visiblity(view);
948 
949   raise_view(view);
950 
951   hikari_view_damage_whole(view);
952 
953   assert(is_first_view(view));
954 }
955 
956 void
hikari_view_hide(struct hikari_view * view)957 hikari_view_hide(struct hikari_view *view)
958 {
959   assert(view != NULL);
960   assert(!hikari_view_is_hidden(view));
961   assert(!hikari_view_is_forced(view));
962 
963 #if !defined(NDEBUG)
964   printf("HIDE %p\n", view);
965 #endif
966 
967   clear_focus(view);
968   hide(view);
969 
970   hikari_view_damage_whole(view);
971 }
972 
973 void
hikari_view_raise(struct hikari_view * view)974 hikari_view_raise(struct hikari_view *view)
975 {
976   assert(view != NULL);
977   assert(!hikari_view_is_hidden(view));
978 
979   if (is_first_view(view)) {
980     return;
981   }
982 
983   raise_view(view);
984   hikari_view_damage_whole(view);
985 }
986 
987 void
hikari_view_lower(struct hikari_view * view)988 hikari_view_lower(struct hikari_view *view)
989 {
990   assert(view != NULL);
991   assert(!hikari_view_is_hidden(view));
992 
993   if (is_last_view(view)) {
994     return;
995   }
996 
997   wl_list_remove(&view->sheet_views);
998   wl_list_insert(view->sheet->views.prev, &view->sheet_views);
999 
1000   wl_list_remove(&view->group_views);
1001   wl_list_insert(view->group->views.prev, &view->group_views);
1002 
1003   wl_list_remove(&view->output_views);
1004   wl_list_insert(view->output->views.prev, &view->output_views);
1005 
1006   wl_list_remove(&view->visible_group_views);
1007   wl_list_insert(view->group->visible_views.prev, &view->visible_group_views);
1008 
1009   wl_list_remove(&view->group->visible_server_groups);
1010   wl_list_insert(
1011       hikari_server.visible_groups.prev, &view->group->visible_server_groups);
1012 
1013   wl_list_remove(&view->workspace_views);
1014   wl_list_insert(view->sheet->workspace->views.prev, &view->workspace_views);
1015 
1016   wl_list_remove(&view->visible_server_views);
1017   wl_list_insert(hikari_server.visible_views.prev, &view->visible_server_views);
1018 
1019   hikari_view_damage_whole(view);
1020 }
1021 
1022 static void
commit_tile(struct hikari_view * view,struct hikari_operation * operation)1023 commit_tile(struct hikari_view *view, struct hikari_operation *operation)
1024 {
1025   if (view->maximized_state) {
1026     hikari_maximized_state_destroy(view->maximized_state);
1027     view->maximized_state = NULL;
1028     if (!view->use_csd) {
1029       view->border.state = HIKARI_BORDER_INACTIVE;
1030     }
1031   }
1032 
1033   assert(hikari_tile_is_attached(operation->tile));
1034 
1035   if (hikari_view_is_tiled(view)) {
1036     struct hikari_tile *tile = view->tile;
1037 
1038     assert(hikari_tile_is_attached(tile));
1039 
1040     wl_list_remove(&tile->layout_tiles);
1041     hikari_free(tile);
1042     view->tile = NULL;
1043   }
1044 
1045   assert(!hikari_view_is_tiled(view));
1046   assert(operation->tile != NULL);
1047   view->tile = operation->tile;
1048   operation->tile = NULL;
1049 
1050   if (!hikari_view_is_hidden(view)) {
1051     commit_pending_geometry(view, &operation->geometry);
1052     if (operation->center) {
1053       hikari_view_center_cursor(view);
1054     }
1055     hikari_server_cursor_focus();
1056   } else {
1057     hikari_view_refresh_geometry(view, &operation->geometry);
1058   }
1059 }
1060 
1061 static void
queue_tile(struct hikari_view * view,struct hikari_layout * layout,struct hikari_tile * tile,bool center)1062 queue_tile(struct hikari_view *view,
1063     struct hikari_layout *layout,
1064     struct hikari_tile *tile,
1065     bool center)
1066 {
1067   assert(!hikari_view_is_dirty(view));
1068 
1069   struct hikari_operation *op = &view->pending_operation;
1070 
1071   struct wlr_box *current_geometry = hikari_view_geometry(view);
1072 
1073   op->type = HIKARI_OPERATION_TYPE_TILE;
1074   op->tile = tile;
1075   op->geometry = tile->view_geometry;
1076   op->center = center;
1077 
1078   if (current_geometry->width == op->geometry.width &&
1079       current_geometry->height == op->geometry.height) {
1080     hikari_view_set_dirty(view);
1081 
1082     hikari_view_commit_pending_operation(view, current_geometry);
1083   } else {
1084     resize(view, op, commit_tile);
1085   }
1086 }
1087 
1088 void
hikari_view_tile(struct hikari_view * view,struct wlr_box * geometry,bool center)1089 hikari_view_tile(
1090     struct hikari_view *view, struct wlr_box *geometry, bool center)
1091 {
1092   assert(!hikari_view_is_dirty(view));
1093   assert(hikari_view_is_tileable(view));
1094 
1095   struct hikari_layout *layout = view->sheet->workspace->sheet->layout;
1096 
1097   struct hikari_tile *tile = hikari_malloc(sizeof(struct hikari_tile));
1098   hikari_tile_init(tile, view, layout, geometry, geometry);
1099 
1100   queue_tile(view, layout, tile, center);
1101 
1102   wl_list_insert(layout->tiles.prev, &tile->layout_tiles);
1103 }
1104 
1105 static void
commit_full_maximize(struct hikari_view * view,struct hikari_operation * operation)1106 commit_full_maximize(
1107     struct hikari_view *view, struct hikari_operation *operation)
1108 {
1109   if (!view->maximized_state) {
1110     view->maximized_state =
1111         hikari_malloc(sizeof(struct hikari_maximized_state));
1112   }
1113 
1114   view->maximized_state->maximization = HIKARI_MAXIMIZATION_FULLY_MAXIMIZED;
1115   view->maximized_state->geometry = operation->geometry;
1116 
1117   if (!view->use_csd) {
1118     view->border.state = HIKARI_BORDER_NONE;
1119   }
1120 
1121   commit_pending_operation(view, operation);
1122 }
1123 
1124 static void
queue_full_maximize(struct hikari_view * view)1125 queue_full_maximize(struct hikari_view *view)
1126 {
1127   assert(view != NULL);
1128   assert(!hikari_view_is_hidden(view));
1129 
1130   struct hikari_operation *op = &view->pending_operation;
1131   struct hikari_output *output = view->output;
1132 
1133   op->type = HIKARI_OPERATION_TYPE_FULL_MAXIMIZE;
1134   op->geometry = output->usable_area;
1135   op->center = true;
1136 
1137   resize(view, op, commit_full_maximize);
1138 }
1139 
1140 static void
commit_unmaximize(struct hikari_view * view,struct hikari_operation * operation)1141 commit_unmaximize(struct hikari_view *view, struct hikari_operation *operation)
1142 {
1143   hikari_view_damage_whole(view);
1144 
1145   hikari_free(view->maximized_state);
1146   view->maximized_state = NULL;
1147 
1148   if (!view->use_csd) {
1149     view->border.state = HIKARI_BORDER_ACTIVE;
1150   }
1151 
1152   commit_pending_operation(view, operation);
1153 }
1154 
1155 static void
queue_unmaximize(struct hikari_view * view)1156 queue_unmaximize(struct hikari_view *view)
1157 {
1158   assert(view != NULL);
1159   assert(!hikari_view_is_hidden(view));
1160 
1161   struct hikari_operation *op = &view->pending_operation;
1162 
1163   op->type = HIKARI_OPERATION_TYPE_UNMAXIMIZE;
1164   op->center = true;
1165 
1166   if (view->tile != NULL) {
1167     op->geometry = view->tile->view_geometry;
1168   } else {
1169     op->geometry = view->geometry;
1170   }
1171 
1172   resize(view, op, commit_unmaximize);
1173 }
1174 
1175 void
hikari_view_toggle_full_maximize(struct hikari_view * view)1176 hikari_view_toggle_full_maximize(struct hikari_view *view)
1177 {
1178   assert(view != NULL);
1179   assert(!hikari_view_is_hidden(view));
1180 
1181   if (hikari_view_is_dirty(view)) {
1182     return;
1183   }
1184 
1185   if (hikari_view_is_fully_maximized(view)) {
1186     queue_unmaximize(view);
1187   } else {
1188     queue_full_maximize(view);
1189   }
1190 }
1191 
1192 void
hikari_view_toggle_public(struct hikari_view * view)1193 hikari_view_toggle_public(struct hikari_view *view)
1194 {
1195   if (hikari_view_is_public(view)) {
1196     hikari_view_unset_public(view);
1197   } else {
1198     hikari_view_set_public(view);
1199   }
1200 }
1201 
1202 static void
commit_horizontal_maximize(struct hikari_view * view,struct hikari_operation * operation)1203 commit_horizontal_maximize(
1204     struct hikari_view *view, struct hikari_operation *operation)
1205 {
1206   if (!view->maximized_state) {
1207     view->maximized_state =
1208         hikari_malloc(sizeof(struct hikari_maximized_state));
1209   } else {
1210     switch (view->maximized_state->maximization) {
1211       case HIKARI_MAXIMIZATION_HORIZONTALLY_MAXIMIZED:
1212         commit_full_maximize(view, operation);
1213         return;
1214 
1215       case HIKARI_MAXIMIZATION_FULLY_MAXIMIZED:
1216         if (!view->use_csd) {
1217           view->border.state = HIKARI_BORDER_INACTIVE;
1218         }
1219         break;
1220 
1221       case HIKARI_MAXIMIZATION_VERTICALLY_MAXIMIZED:
1222         assert(false);
1223         break;
1224     }
1225   }
1226 
1227   view->maximized_state->maximization =
1228       HIKARI_MAXIMIZATION_HORIZONTALLY_MAXIMIZED;
1229   view->maximized_state->geometry = operation->geometry;
1230 
1231   commit_pending_operation(view, operation);
1232 }
1233 
1234 static void
queue_horizontal_maximize(struct hikari_view * view)1235 queue_horizontal_maximize(struct hikari_view *view)
1236 {
1237   assert(view != NULL);
1238   assert(!hikari_view_is_hidden(view));
1239 
1240   struct hikari_operation *op = &view->pending_operation;
1241   struct hikari_output *output = view->output;
1242 
1243   struct wlr_box *geometry = view->current_unmaximized_geometry;
1244 
1245   op->type = HIKARI_OPERATION_TYPE_HORIZONTAL_MAXIMIZE;
1246   op->geometry.x = output->usable_area.x;
1247   op->geometry.y = geometry->y;
1248   op->geometry.height = geometry->height;
1249   op->geometry.width = output->usable_area.width;
1250   op->center = true;
1251 
1252   resize(view, op, commit_horizontal_maximize);
1253 }
1254 
1255 static void
commit_vertical_maximize(struct hikari_view * view,struct hikari_operation * operation)1256 commit_vertical_maximize(
1257     struct hikari_view *view, struct hikari_operation *operation)
1258 {
1259   if (!view->maximized_state) {
1260     view->maximized_state =
1261         hikari_malloc(sizeof(struct hikari_maximized_state));
1262   } else {
1263     switch (view->maximized_state->maximization) {
1264       case HIKARI_MAXIMIZATION_HORIZONTALLY_MAXIMIZED:
1265         commit_full_maximize(view, operation);
1266         return;
1267 
1268       case HIKARI_MAXIMIZATION_FULLY_MAXIMIZED:
1269         if (!view->use_csd) {
1270           view->border.state = HIKARI_BORDER_INACTIVE;
1271         }
1272         break;
1273 
1274       case HIKARI_MAXIMIZATION_VERTICALLY_MAXIMIZED:
1275         assert(false);
1276         break;
1277     }
1278   }
1279 
1280   view->maximized_state->maximization =
1281       HIKARI_MAXIMIZATION_VERTICALLY_MAXIMIZED;
1282   view->maximized_state->geometry = operation->geometry;
1283 
1284   commit_pending_operation(view, operation);
1285 }
1286 
1287 static void
queue_vertical_maximize(struct hikari_view * view)1288 queue_vertical_maximize(struct hikari_view *view)
1289 {
1290   assert(view != NULL);
1291   assert(!hikari_view_is_hidden(view));
1292 
1293   struct hikari_operation *op = &view->pending_operation;
1294   struct hikari_output *output = view->output;
1295 
1296   struct wlr_box *geometry = view->current_unmaximized_geometry;
1297 
1298   op->type = HIKARI_OPERATION_TYPE_VERTICAL_MAXIMIZE;
1299   op->geometry.x = geometry->x;
1300   op->geometry.y = output->usable_area.y;
1301   op->geometry.height = output->usable_area.height;
1302   op->geometry.width = geometry->width;
1303   op->center = true;
1304 
1305   resize(view, op, commit_vertical_maximize);
1306 }
1307 
1308 void
hikari_view_toggle_vertical_maximize(struct hikari_view * view)1309 hikari_view_toggle_vertical_maximize(struct hikari_view *view)
1310 {
1311   assert(view != NULL);
1312   assert(!hikari_view_is_hidden(view));
1313 
1314   if (hikari_view_is_dirty(view)) {
1315     return;
1316   }
1317 
1318   if (view->maximized_state != NULL) {
1319     switch (view->maximized_state->maximization) {
1320       case HIKARI_MAXIMIZATION_FULLY_MAXIMIZED:
1321         queue_horizontal_maximize(view);
1322         break;
1323 
1324       case HIKARI_MAXIMIZATION_VERTICALLY_MAXIMIZED:
1325         queue_unmaximize(view);
1326         break;
1327 
1328       case HIKARI_MAXIMIZATION_HORIZONTALLY_MAXIMIZED:
1329         queue_full_maximize(view);
1330         break;
1331     }
1332   } else {
1333     queue_vertical_maximize(view);
1334   }
1335 }
1336 
1337 void
hikari_view_toggle_horizontal_maximize(struct hikari_view * view)1338 hikari_view_toggle_horizontal_maximize(struct hikari_view *view)
1339 {
1340   assert(view != NULL);
1341   assert(!hikari_view_is_hidden(view));
1342 
1343   if (view->maximized_state != NULL) {
1344     switch (view->maximized_state->maximization) {
1345       case HIKARI_MAXIMIZATION_FULLY_MAXIMIZED:
1346         queue_vertical_maximize(view);
1347         break;
1348 
1349       case HIKARI_MAXIMIZATION_VERTICALLY_MAXIMIZED:
1350         queue_full_maximize(view);
1351         break;
1352 
1353       case HIKARI_MAXIMIZATION_HORIZONTALLY_MAXIMIZED:
1354         queue_unmaximize(view);
1355         break;
1356     }
1357   } else {
1358     queue_horizontal_maximize(view);
1359   }
1360 }
1361 
1362 void
hikari_view_toggle_floating(struct hikari_view * view)1363 hikari_view_toggle_floating(struct hikari_view *view)
1364 {
1365   if (!hikari_view_is_floating(view)) {
1366     if (hikari_view_is_tiled(view)) {
1367       hikari_view_reset_geometry(view);
1368     }
1369     hikari_view_set_floating(view);
1370   } else {
1371     hikari_view_unset_floating(view);
1372   }
1373 }
1374 
1375 void
hikari_view_reset_geometry(struct hikari_view * view)1376 hikari_view_reset_geometry(struct hikari_view *view)
1377 {
1378   queue_reset(view, true);
1379 }
1380 
1381 void
hikari_view_evacuate(struct hikari_view * view,struct hikari_sheet * sheet)1382 hikari_view_evacuate(struct hikari_view *view, struct hikari_sheet *sheet)
1383 {
1384 #ifndef NDEBUG
1385   printf("EVACUATE VIEW %p\n", view);
1386 #endif
1387 
1388   clear_focus(view);
1389 
1390   view->output = sheet->workspace->output;
1391   view->sheet = sheet;
1392 
1393   if (!hikari_view_is_hidden(view)) {
1394     if (hikari_view_is_forced(view)) {
1395       move_to_top(view);
1396     } else {
1397       raise_view(view);
1398     }
1399 
1400     if (hikari_sheet_is_visible(sheet)) {
1401       hikari_view_damage_whole(view);
1402       hikari_indicator_damage(&hikari_server.indicator, view);
1403     } else {
1404       if (hikari_view_is_forced(view)) {
1405         move_to_top(view);
1406       } else {
1407         hikari_view_hide(view);
1408       }
1409     }
1410   } else {
1411     if (hikari_view_is_forced(view)) {
1412       raise_view(view);
1413     } else {
1414       move_to_top(view);
1415     }
1416   }
1417 
1418   if (hikari_view_is_tiled(view) || hikari_view_is_maximized(view)) {
1419     queue_reset(view, false);
1420   }
1421 }
1422 
1423 void
hikari_view_pin_to_sheet(struct hikari_view * view,struct hikari_sheet * sheet)1424 hikari_view_pin_to_sheet(struct hikari_view *view, struct hikari_sheet *sheet)
1425 {
1426   assert(view != NULL);
1427   assert(sheet != NULL);
1428   assert(sheet->workspace->output == view->output);
1429 
1430   if (view->sheet == sheet) {
1431     assert(!hikari_view_is_hidden(view));
1432 
1433     if (view->sheet->workspace->sheet != sheet && sheet->nr != 0) {
1434       hikari_view_hide(view);
1435       hikari_server_cursor_focus();
1436 
1437       move_to_top(view);
1438     } else {
1439       hikari_view_raise(view);
1440       hikari_indicator_damage(&hikari_server.indicator, view);
1441     }
1442   } else {
1443     if (hikari_sheet_is_visible(sheet)) {
1444       place_visibly_above(view, sheet->workspace);
1445 
1446       hikari_view_damage_whole(view);
1447       hikari_indicator_damage(&hikari_server.indicator, view);
1448     } else {
1449       hikari_view_hide(view);
1450       hikari_server_cursor_focus();
1451     }
1452 
1453     view->sheet = sheet;
1454 
1455     if (hikari_view_is_tiled(view)) {
1456       queue_reset(view, true);
1457     } else {
1458       move_to_top(view);
1459     }
1460   }
1461 }
1462 
1463 void
hikari_view_center_cursor(struct hikari_view * view)1464 hikari_view_center_cursor(struct hikari_view *view)
1465 {
1466   assert(view != NULL);
1467 
1468   struct hikari_output *output = view->output;
1469   struct wlr_box *view_geometry = hikari_view_geometry(view);
1470 
1471   struct wlr_box geometry;
1472   hikari_geometry_constrain_size(
1473       view_geometry, &output->usable_area, &geometry);
1474 
1475   hikari_cursor_center(&hikari_server.cursor, output, &geometry);
1476 }
1477 
1478 void
hikari_view_top_left_cursor(struct hikari_view * view)1479 hikari_view_top_left_cursor(struct hikari_view *view)
1480 {
1481   assert(view != NULL);
1482 
1483   struct wlr_box *geometry = hikari_view_geometry(view);
1484   struct hikari_output *output = view->output;
1485 
1486   int x = output->geometry.x + geometry->x;
1487   int y = output->geometry.y + geometry->y;
1488 
1489   hikari_cursor_warp(&hikari_server.cursor, x, y);
1490 }
1491 
1492 void
hikari_view_bottom_right_cursor(struct hikari_view * view)1493 hikari_view_bottom_right_cursor(struct hikari_view *view)
1494 {
1495   assert(view != NULL);
1496 
1497   struct wlr_box *geometry = hikari_view_geometry(view);
1498   struct hikari_output *output = view->output;
1499 
1500   int x = output->geometry.x + geometry->x + geometry->width;
1501   int y = output->geometry.y + geometry->y + geometry->height;
1502 
1503   hikari_cursor_warp(&hikari_server.cursor, x, y);
1504 }
1505 
1506 void
hikari_view_toggle_invisible(struct hikari_view * view)1507 hikari_view_toggle_invisible(struct hikari_view *view)
1508 {
1509   if (hikari_view_is_invisible(view)) {
1510     hikari_view_unset_invisible(view);
1511   } else {
1512     if (hikari_view_is_tiled(view)) {
1513       hikari_view_reset_geometry(view);
1514     }
1515     hikari_view_set_invisible(view);
1516   }
1517 }
1518 
1519 void
hikari_view_group(struct hikari_view * view,struct hikari_group * group)1520 hikari_view_group(struct hikari_view *view, struct hikari_group *group)
1521 {
1522   assert(view != NULL);
1523   assert(group != NULL);
1524   assert(view->group != NULL);
1525 
1526   if (view->group == group) {
1527     return;
1528   }
1529 
1530   remove_from_group(view);
1531   view->group = group;
1532 
1533   increase_group_visiblity(view);
1534 
1535   raise_view(view);
1536 
1537   hikari_view_damage_whole(view);
1538 }
1539 
1540 void
hikari_view_exchange(struct hikari_view * from,struct hikari_view * to)1541 hikari_view_exchange(struct hikari_view *from, struct hikari_view *to)
1542 {
1543   assert(from != NULL);
1544   assert(to != NULL);
1545 
1546   if (hikari_view_is_dirty(from) || hikari_view_is_dirty(to)) {
1547     return;
1548   }
1549 
1550   assert(from->tile != NULL);
1551   assert(to->tile != NULL);
1552   assert(to->tile->view->sheet == from->tile->view->sheet);
1553 
1554   struct hikari_layout *layout = from->sheet->workspace->sheet->layout;
1555 
1556   struct wlr_box *from_geometry = &from->tile->tile_geometry;
1557   struct wlr_box *to_geometry = &to->tile->tile_geometry;
1558 
1559   struct hikari_tile *from_tile = hikari_malloc(sizeof(struct hikari_tile));
1560   struct hikari_tile *to_tile = hikari_malloc(sizeof(struct hikari_tile));
1561 
1562   hikari_tile_init(from_tile, from, layout, to_geometry, to_geometry);
1563   hikari_tile_init(to_tile, to, layout, from_geometry, from_geometry);
1564 
1565   wl_list_insert(&from->tile->layout_tiles, &to_tile->layout_tiles);
1566   wl_list_insert(&to->tile->layout_tiles, &from_tile->layout_tiles);
1567 
1568   queue_tile(from, layout, from_tile, true);
1569   queue_tile(to, layout, to_tile, false);
1570 }
1571 
1572 static void
destroy_subsurface_handler(struct wl_listener * listener,void * data)1573 destroy_subsurface_handler(struct wl_listener *listener, void *data)
1574 {
1575   struct hikari_view_subsurface *view_subsurface =
1576       wl_container_of(listener, view_subsurface, destroy);
1577 
1578   hikari_view_subsurface_fini(view_subsurface);
1579 
1580   hikari_free(view_subsurface);
1581 }
1582 
1583 void
hikari_view_subsurface_init(struct hikari_view_subsurface * view_subsurface,struct hikari_view * parent,struct wlr_subsurface * subsurface)1584 hikari_view_subsurface_init(struct hikari_view_subsurface *view_subsurface,
1585     struct hikari_view *parent,
1586     struct wlr_subsurface *subsurface)
1587 {
1588   view_subsurface->subsurface = subsurface;
1589 
1590   view_subsurface->destroy.notify = destroy_subsurface_handler;
1591   wl_signal_add(
1592       &subsurface->surface->events.destroy, &view_subsurface->destroy);
1593 
1594   hikari_view_child_init(
1595       (struct hikari_view_child *)view_subsurface, parent, subsurface->surface);
1596 }
1597 
1598 void
hikari_view_child_fini(struct hikari_view_child * view_child)1599 hikari_view_child_fini(struct hikari_view_child *view_child)
1600 {
1601   wl_list_remove(&view_child->link);
1602   wl_list_remove(&view_child->commit.link);
1603   wl_list_remove(&view_child->new_subsurface.link);
1604 }
1605 
1606 void
hikari_view_subsurface_fini(struct hikari_view_subsurface * view_subsurface)1607 hikari_view_subsurface_fini(struct hikari_view_subsurface *view_subsurface)
1608 {
1609   hikari_view_child_fini(&view_subsurface->view_child);
1610   wl_list_remove(&view_subsurface->destroy.link);
1611 }
1612 
1613 static void
damage_surface(struct wlr_surface * surface,int sx,int sy,void * data)1614 damage_surface(struct wlr_surface *surface, int sx, int sy, void *data)
1615 {
1616   struct hikari_damage_data *damage_data = data;
1617   struct hikari_output *output = damage_data->output;
1618 
1619   if (damage_data->whole) {
1620     damage_whole_surface(surface, sx, sy, data);
1621   } else {
1622     struct wlr_box *geometry = damage_data->geometry;
1623 
1624     hikari_output_add_effective_surface_damage(
1625         output, surface, geometry->x + sx, geometry->y + sy);
1626   }
1627 }
1628 
1629 static void
damage_single_surface(struct wlr_surface * surface,int sx,int sy,void * data)1630 damage_single_surface(struct wlr_surface *surface, int sx, int sy, void *data)
1631 {
1632   struct hikari_damage_data *damage_data = data;
1633 
1634   if (damage_data->surface == surface) {
1635     damage_surface(surface, sx, sy, data);
1636   }
1637 }
1638 
1639 static void
commit_child_handler(struct wl_listener * listener,void * data)1640 commit_child_handler(struct wl_listener *listener, void *data)
1641 {
1642   struct hikari_view_child *view_child =
1643       wl_container_of(listener, view_child, commit);
1644 
1645   struct hikari_view *parent = view_child->parent;
1646   assert(!hikari_view_is_hidden(parent));
1647 
1648   struct wlr_surface *surface = view_child->surface;
1649 
1650   hikari_view_damage_surface(parent, surface, false);
1651 }
1652 
1653 static void
new_subsurface_child_handler(struct wl_listener * listener,void * data)1654 new_subsurface_child_handler(struct wl_listener *listener, void *data)
1655 {
1656   struct hikari_view_child *view_child =
1657       wl_container_of(listener, view_child, new_subsurface);
1658 
1659   struct wlr_subsurface *wlr_subsurface = data;
1660 
1661   struct hikari_view_subsurface *view_subsurface =
1662       hikari_malloc(sizeof(struct hikari_view_subsurface));
1663 
1664   hikari_view_subsurface_init(
1665       view_subsurface, view_child->parent, wlr_subsurface);
1666 }
1667 
1668 void
hikari_view_child_init(struct hikari_view_child * view_child,struct hikari_view * parent,struct wlr_surface * surface)1669 hikari_view_child_init(struct hikari_view_child *view_child,
1670     struct hikari_view *parent,
1671     struct wlr_surface *surface)
1672 {
1673   view_child->parent = parent;
1674   view_child->surface = surface;
1675 
1676   view_child->new_subsurface.notify = new_subsurface_child_handler;
1677   wl_signal_add(&surface->events.new_subsurface, &view_child->new_subsurface);
1678 
1679   view_child->commit.notify = commit_child_handler;
1680   wl_signal_add(&surface->events.commit, &view_child->commit);
1681 
1682   wl_list_insert(&parent->children, &view_child->link);
1683 }
1684 
1685 void
hikari_view_damage_surface(struct hikari_view * view,struct wlr_surface * surface,bool whole)1686 hikari_view_damage_surface(
1687     struct hikari_view *view, struct wlr_surface *surface, bool whole)
1688 {
1689   assert(view != NULL);
1690 
1691   // TODO I know, this needs to be done A LOT better
1692   if (view->use_csd) {
1693     hikari_output_damage_whole(view->output);
1694     return;
1695   }
1696 
1697   struct hikari_damage_data damage_data;
1698 
1699   damage_data.geometry = hikari_view_geometry(view);
1700   damage_data.output = view->output;
1701   damage_data.surface = surface;
1702   damage_data.whole = whole;
1703   damage_data.view = view;
1704 
1705   hikari_node_for_each_surface(
1706       (struct hikari_node *)view, damage_single_surface, &damage_data);
1707 }
1708 
1709 void
hikari_view_refresh_geometry(struct hikari_view * view,struct wlr_box * geometry)1710 hikari_view_refresh_geometry(struct hikari_view *view, struct wlr_box *geometry)
1711 {
1712   struct wlr_box *new_geometry = refresh_geometry(view);
1713 
1714   memcpy(new_geometry, geometry, sizeof(struct wlr_box));
1715 
1716   view->current_geometry = new_geometry;
1717   view->current_unmaximized_geometry = refresh_unmaximized_geometry(view);
1718 
1719   refresh_border_geometry(view);
1720 }
1721 
1722 static void
commit_operation(struct hikari_operation * operation,struct hikari_view * view)1723 commit_operation(struct hikari_operation *operation, struct hikari_view *view)
1724 {
1725   switch (operation->type) {
1726     case HIKARI_OPERATION_TYPE_RESIZE:
1727       commit_resize(view, operation);
1728       break;
1729 
1730     case HIKARI_OPERATION_TYPE_RESET:
1731       commit_reset(view, operation);
1732       break;
1733 
1734     case HIKARI_OPERATION_TYPE_UNMAXIMIZE:
1735       commit_unmaximize(view, operation);
1736       break;
1737 
1738     case HIKARI_OPERATION_TYPE_FULL_MAXIMIZE:
1739       commit_full_maximize(view, operation);
1740       break;
1741 
1742     case HIKARI_OPERATION_TYPE_VERTICAL_MAXIMIZE:
1743       commit_vertical_maximize(view, operation);
1744       break;
1745 
1746     case HIKARI_OPERATION_TYPE_HORIZONTAL_MAXIMIZE:
1747       commit_horizontal_maximize(view, operation);
1748       break;
1749 
1750     case HIKARI_OPERATION_TYPE_TILE:
1751       commit_tile(view, operation);
1752       break;
1753   }
1754 }
1755 
1756 void
hikari_view_commit_pending_operation(struct hikari_view * view,struct wlr_box * geometry)1757 hikari_view_commit_pending_operation(
1758     struct hikari_view *view, struct wlr_box *geometry)
1759 {
1760   assert(view != NULL);
1761   assert(hikari_view_is_dirty(view));
1762 
1763   view->pending_operation.geometry.width = geometry->width;
1764   view->pending_operation.geometry.height = geometry->height;
1765 
1766   hikari_indicator_damage(&hikari_server.indicator, view);
1767   hikari_view_damage_whole(view);
1768 
1769   commit_operation(&view->pending_operation, view);
1770   hikari_view_unset_dirty(view);
1771 }
1772 
1773 void
hikari_view_activate(struct hikari_view * view,bool active)1774 hikari_view_activate(struct hikari_view *view, bool active)
1775 {
1776   assert(view != NULL);
1777 
1778   if (view->activate) {
1779     if (view->border.state != HIKARI_BORDER_NONE) {
1780       view->border.state =
1781           active ? HIKARI_BORDER_ACTIVE : HIKARI_BORDER_INACTIVE;
1782     }
1783     view->activate(view, active);
1784   }
1785 }
1786 
1787 static void
migrate_view(struct hikari_view * view,struct hikari_sheet * sheet,bool center)1788 migrate_view(struct hikari_view *view, struct hikari_sheet *sheet, bool center)
1789 {
1790   assert(hikari_view_is_hidden(view));
1791 
1792   view->output = sheet->workspace->output;
1793   view->sheet = sheet;
1794 
1795   move_to_top(view);
1796 
1797   queue_reset(view, center);
1798 }
1799 
1800 void
hikari_view_migrate(struct hikari_view * view,struct hikari_sheet * sheet,int x,int y,bool center)1801 hikari_view_migrate(struct hikari_view *view,
1802     struct hikari_sheet *sheet,
1803     int x,
1804     int y,
1805     bool center)
1806 {
1807   struct hikari_output *output = sheet->workspace->output;
1808   struct wlr_box *view_geometry = hikari_view_geometry(view);
1809 
1810   hikari_indicator_damage(&hikari_server.indicator, view);
1811   hikari_view_damage_whole(view);
1812 
1813   // only remove view from lists and do not make it lose focus by calling
1814   // `hikari_view_hide`.
1815   hide(view);
1816 
1817   hikari_geometry_constrain_relative(
1818       &view->geometry, &output->usable_area, x, y);
1819   hikari_geometry_constrain_relative(view_geometry, &output->usable_area, x, y);
1820 
1821   migrate_view(view, sheet, center);
1822 
1823 #ifdef HAVE_XWAYLAND
1824   if (view->move != NULL) {
1825     view->move(view, view_geometry->x, view_geometry->y);
1826   }
1827 #endif
1828 
1829   if (hikari_view_is_hidden(view)) {
1830     hikari_view_show(view);
1831   }
1832 }
1833 
1834 void
hikari_view_configure(struct hikari_view * view,const char * app_id,struct hikari_view_config * view_config)1835 hikari_view_configure(struct hikari_view *view,
1836     const char *app_id,
1837     struct hikari_view_config *view_config)
1838 {
1839   assert(view->id == NULL);
1840 
1841   struct hikari_sheet *sheet;
1842   struct hikari_output *output;
1843   struct wlr_box *geometry = &view->geometry;
1844   int x, y;
1845   bool invisible, floating, publicview;
1846 
1847   set_app_id(view, app_id);
1848 
1849   if (view_config != NULL) {
1850     struct hikari_view_properties *properties =
1851         hikari_view_config_resolve_properties(view_config, view->child);
1852 
1853     sheet = hikari_view_properties_resolve_sheet(properties);
1854     output = sheet->workspace->output;
1855 
1856     invisible = properties->invisible;
1857     floating = properties->floating;
1858     publicview = properties->publicview;
1859 
1860     hikari_view_properties_resolve_position(properties, view, &x, &y);
1861   } else {
1862     sheet = hikari_server.workspace->sheet;
1863     output = sheet->workspace->output;
1864 
1865     invisible = false;
1866     floating = false;
1867     publicview = false;
1868 
1869     x = hikari_server.cursor.wlr_cursor->x - output->geometry.x;
1870     y = hikari_server.cursor.wlr_cursor->y - output->geometry.y;
1871   }
1872 
1873   view->sheet = sheet;
1874   view->output = output;
1875 
1876   wl_list_init(&view->workspace_views);
1877   wl_list_init(&view->visible_server_views);
1878 
1879   if (invisible) {
1880     hikari_view_set_invisible(view);
1881   }
1882 
1883   if (floating) {
1884     hikari_view_set_floating(view);
1885   }
1886 
1887   if (publicview) {
1888     hikari_view_set_public(view);
1889   }
1890 
1891   hikari_geometry_constrain_absolute(geometry, &output->usable_area, x, y);
1892   hikari_view_refresh_geometry(view, geometry);
1893 }
1894