1 #define G_LOG_DOMAIN "phoc-view"
2 
3 #include "config.h"
4 
5 #ifndef _POSIX_C_SOURCE
6 #define _POSIX_C_SOURCE 200809L
7 #endif
8 #include <assert.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <wlr/types/wlr_output_layout.h>
12 #include "desktop.h"
13 #include "input.h"
14 #include "seat.h"
15 #include "server.h"
16 #include "view.h"
17 
view_init(struct roots_view * view,const struct roots_view_interface * impl,enum roots_view_type type,PhocDesktop * desktop)18 void view_init(struct roots_view *view, const struct roots_view_interface *impl,
19 		enum roots_view_type type, PhocDesktop *desktop) {
20 	assert(impl->destroy);
21 	view->impl = impl;
22 	view->type = type;
23 	view->desktop = desktop;
24 	view->alpha = 1.0f;
25 	view->scale = 1.0f;
26 	view->title = NULL;
27 	view->app_id = NULL;
28 	wl_signal_init(&view->events.unmap);
29 	wl_signal_init(&view->events.destroy);
30 	wl_list_init(&view->child_surfaces);
31 	wl_list_init(&view->stack);
32 }
33 
view_destroy(struct roots_view * view)34 void view_destroy(struct roots_view *view) {
35 	if (view == NULL) {
36 		return;
37 	}
38 
39 	if (view->parent) {
40 		wl_list_remove(&view->parent_link);
41 		wl_list_init(&view->parent_link);
42 	}
43 	struct roots_view *child, *tmp;
44 	wl_list_for_each_safe(child, tmp, &view->stack, parent_link) {
45 		wl_list_remove(&child->parent_link);
46 		wl_list_init(&child->parent_link);
47 		child->parent = view->parent;
48 		if (child->parent) {
49 			wl_list_insert(&child->parent->stack, &child->parent_link);
50 		}
51 	}
52 
53 	wl_signal_emit(&view->events.destroy, view);
54 
55 	if (view->wlr_surface != NULL) {
56 		view_unmap(view);
57 	}
58 
59 	// Can happen if fullscreened while unmapped, and hasn't been mapped
60 	if (view->fullscreen_output != NULL) {
61 		view->fullscreen_output->fullscreen_view = NULL;
62 	}
63 
64 	view->impl->destroy(view);
65 }
66 
view_is_maximized(const struct roots_view * view)67 gboolean view_is_maximized(const struct roots_view *view)
68 {
69 	return view->state == PHOC_VIEW_STATE_MAXIMIZED;
70 }
71 
view_is_tiled(const struct roots_view * view)72 gboolean view_is_tiled(const struct roots_view *view)
73 {
74 	return view->state == PHOC_VIEW_STATE_TILED;
75 }
76 
view_get_box(const struct roots_view * view,struct wlr_box * box)77 void view_get_box(const struct roots_view *view, struct wlr_box *box) {
78 	box->x = view->box.x;
79 	box->y = view->box.y;
80 	box->width = view->box.width * view->scale;
81 	box->height = view->box.height * view->scale;
82 }
83 
84 
85 void
view_get_geometry(struct roots_view * view,struct wlr_box * geom)86 view_get_geometry (struct roots_view *view, struct wlr_box *geom)
87 {
88   if (view->impl->get_geometry) {
89     view->impl->get_geometry (view, geom);
90   } else {
91     geom->x = 0;
92     geom->y = 0;
93     geom->width = view->box.width * view->scale;
94     geom->height = view->box.height * view->scale;
95   }
96 }
97 
98 
view_get_deco_box(const struct roots_view * view,struct wlr_box * box)99 void view_get_deco_box(const struct roots_view *view, struct wlr_box *box) {
100 	view_get_box(view, box);
101 	if (!view->decorated) {
102 		return;
103 	}
104 
105 	box->x -= view->border_width;
106 	box->y -= (view->border_width + view->titlebar_height);
107 	box->width += view->border_width * 2;
108 	box->height += (view->border_width * 2 + view->titlebar_height);
109 }
110 
view_get_deco_part(struct roots_view * view,double sx,double sy)111 enum roots_deco_part view_get_deco_part(struct roots_view *view, double sx,
112 		double sy) {
113 	if (!view->decorated) {
114 		return ROOTS_DECO_PART_NONE;
115 	}
116 
117 	int sw = view->wlr_surface->current.width;
118 	int sh = view->wlr_surface->current.height;
119 	int bw = view->border_width;
120 	int titlebar_h = view->titlebar_height;
121 
122 	if (sx > 0 && sx < sw && sy < 0 && sy > -view->titlebar_height) {
123 		return ROOTS_DECO_PART_TITLEBAR;
124 	}
125 
126 	enum roots_deco_part parts = 0;
127 	if (sy >= -(titlebar_h + bw) &&
128 			sy <= sh + bw) {
129 		if (sx < 0 && sx > -bw) {
130 			parts |= ROOTS_DECO_PART_LEFT_BORDER;
131 		} else if (sx > sw && sx < sw + bw) {
132 			parts |= ROOTS_DECO_PART_RIGHT_BORDER;
133 		}
134 	}
135 
136 	if (sx >= -bw && sx <= sw + bw) {
137 		if (sy > sh && sy <= sh + bw) {
138 			parts |= ROOTS_DECO_PART_BOTTOM_BORDER;
139 		} else if (sy >= -(titlebar_h + bw) && sy < 0) {
140 			parts |= ROOTS_DECO_PART_TOP_BORDER;
141 		}
142 	}
143 
144 	// TODO corners
145 
146 	return parts;
147 }
148 
surface_send_enter_iterator(struct wlr_surface * surface,int x,int y,void * data)149 static void surface_send_enter_iterator(struct wlr_surface *surface,
150 		int x, int y, void *data) {
151 	struct wlr_output *wlr_output = data;
152 	wlr_surface_send_enter(surface, wlr_output);
153 }
154 
surface_send_leave_iterator(struct wlr_surface * surface,int x,int y,void * data)155 static void surface_send_leave_iterator(struct wlr_surface *surface,
156 		int x, int y, void *data) {
157 	struct wlr_output *wlr_output = data;
158 	wlr_surface_send_leave(surface, wlr_output);
159 }
160 
view_update_output(struct roots_view * view,const struct wlr_box * before)161 static void view_update_output(struct roots_view *view,
162 		const struct wlr_box *before) {
163 	PhocDesktop *desktop = view->desktop;
164 
165 	if (view->wlr_surface == NULL) {
166 		return;
167 	}
168 
169 	struct wlr_box box;
170 	view_get_box(view, &box);
171 
172 	PhocOutput *output;
173 	wl_list_for_each(output, &desktop->outputs, link) {
174 		bool intersected = before != NULL && wlr_output_layout_intersects(
175 			desktop->layout, output->wlr_output, before);
176 		bool intersects = wlr_output_layout_intersects(desktop->layout,
177 			output->wlr_output, &box);
178 		if (intersected && !intersects) {
179 			view_for_each_surface(view, surface_send_leave_iterator, output->wlr_output);
180 			if (view->toplevel_handle) {
181 				wlr_foreign_toplevel_handle_v1_output_leave(
182 					view->toplevel_handle, output->wlr_output);
183 			}
184 		}
185 		if (!intersected && intersects) {
186 			view_for_each_surface(view, surface_send_enter_iterator, output->wlr_output);
187 			if (view->toplevel_handle) {
188 				wlr_foreign_toplevel_handle_v1_output_enter(
189 					view->toplevel_handle, output->wlr_output);
190 			}
191 		}
192 	}
193 }
194 
195 static void
view_save(struct roots_view * view)196 view_save(struct roots_view *view)
197 {
198   if (view_is_maximized (view) || view_is_tiled (view))
199     return;
200 
201   /* backup window state */
202   struct wlr_box geom;
203   view_get_geometry(view, &geom);
204   view->saved.x = view->box.x + geom.x * view->scale;
205   view->saved.y = view->box.y + geom.y * view->scale;
206   view->saved.width = view->box.width;
207   view->saved.height = view->box.height;
208 }
209 
view_move(struct roots_view * view,double x,double y)210 void view_move(struct roots_view *view, double x, double y) {
211 	if (view->box.x == x && view->box.y == y) {
212 		return;
213 	}
214 
215 	view->pending_move_resize.update_x = false;
216 	view->pending_move_resize.update_y = false;
217 
218 	struct wlr_box before;
219 	view_get_box(view, &before);
220 	if (view->impl->move) {
221 		view->impl->move(view, x, y);
222 	} else {
223 		view_update_position(view, x, y);
224 	}
225 	view_update_output(view, &before);
226 }
227 
228 void
view_appear_activated(struct roots_view * view,bool activated)229 view_appear_activated (struct roots_view *view, bool activated)
230 {
231   if (view->impl->activate)
232     view->impl->activate (view, activated);
233 }
234 
view_activate(struct roots_view * view,bool activate)235 void view_activate(struct roots_view *view, bool activate) {
236 	if (!view->desktop->maximize) {
237 		view_appear_activated(view, activate);
238 	}
239 
240 	if (view->toplevel_handle) {
241 		wlr_foreign_toplevel_handle_v1_set_activated(view->toplevel_handle,
242 			activate);
243 	}
244 
245 	if (activate && view->fullscreen_output && view->fullscreen_output->force_shell_reveal) {
246 		view->fullscreen_output->force_shell_reveal = false;
247 		phoc_output_damage_whole(view->fullscreen_output);
248 	}
249 }
250 
view_resize(struct roots_view * view,uint32_t width,uint32_t height)251 void view_resize(struct roots_view *view, uint32_t width, uint32_t height) {
252 	struct wlr_box before;
253 	view_get_box(view, &before);
254 
255 	if (view->impl->resize) {
256 		view->impl->resize(view, width, height);
257 	}
258 	view_update_output(view, &before);
259 }
260 
view_move_resize(struct roots_view * view,double x,double y,uint32_t width,uint32_t height)261 void view_move_resize(struct roots_view *view, double x, double y,
262 		uint32_t width, uint32_t height) {
263 	bool update_x = x != view->box.x;
264 	bool update_y = y != view->box.y;
265 	if (!update_x && !update_y) {
266 		view_resize(view, width, height);
267 		return;
268 	}
269 
270 	if (view->impl->move_resize) {
271 		view->impl->move_resize(view, x, y, width, height);
272 		return;
273 	}
274 
275 	view->pending_move_resize.update_x = update_x;
276 	view->pending_move_resize.update_y = update_y;
277 	view->pending_move_resize.x = x;
278 	view->pending_move_resize.y = y;
279 	view->pending_move_resize.width = width;
280 	view->pending_move_resize.height = height;
281 
282 	view_resize(view, width, height);
283 }
284 
view_get_output(struct roots_view * view)285 static struct wlr_output *view_get_output(struct roots_view *view) {
286 	struct wlr_box view_box;
287 	view_get_box(view, &view_box);
288 
289 	double output_x, output_y;
290 	wlr_output_layout_closest_point(view->desktop->layout, NULL,
291 		view->box.x + (double)view_box.width/2,
292 		view->box.y + (double)view_box.height/2,
293 		&output_x, &output_y);
294 	return wlr_output_layout_output_at(view->desktop->layout, output_x,
295 		output_y);
296 }
297 
view_arrange_maximized(struct roots_view * view,struct wlr_output * output)298 void view_arrange_maximized(struct roots_view *view, struct wlr_output *output) {
299 	if (view->fullscreen_output)
300 		return;
301 
302 	if (!output)
303 		output = view_get_output(view);
304 
305 	if (!output)
306 		return;
307 
308 	PhocOutput *phoc_output = output->data;
309 	struct wlr_box *output_box =
310 		wlr_output_layout_get_box(view->desktop->layout, output);
311 	struct wlr_box usable_area = phoc_output->usable_area;
312 	usable_area.x += output_box->x;
313 	usable_area.y += output_box->y;
314 
315 	view_move_resize(view, usable_area.x / view->scale, usable_area.y / view->scale,
316 			usable_area.width / view->scale, usable_area.height / view->scale);
317 }
318 
319 void
view_arrange_tiled(struct roots_view * view,struct wlr_output * output)320 view_arrange_tiled (struct roots_view *view, struct wlr_output *output)
321 {
322   if (view->fullscreen_output)
323     return;
324 
325   if (!output)
326     output = view_get_output(view);
327 
328   if (!output)
329     return;
330 
331   PhocOutput *phoc_output = output->data;
332   struct wlr_box *output_box = wlr_output_layout_get_box (view->desktop->layout, output);
333   struct wlr_box usable_area = phoc_output->usable_area;
334   int x;
335 
336   usable_area.x += output_box->x;
337   usable_area.y += output_box->y;
338 
339   switch (view->tile_direction) {
340   case PHOC_VIEW_TILE_LEFT:
341     x = usable_area.x;
342     break;
343   case PHOC_VIEW_TILE_RIGHT:
344     x = usable_area.x + (0.5 * usable_area.width);
345     break;
346   default:
347     g_error ("Invalid tiling direction %d", view->tile_direction);
348   }
349 
350   /*
351    * No need to take geometry into account since maximized surfaces
352    * usually don't have drop shadows. It wouldn't be up to date here
353    * yet anyway since a client's configure is not yet processed.
354    */
355   view_move_resize (view, x, usable_area.y,
356                     usable_area.width / 2, usable_area.height);
357 }
358 
359 /*
360  * Check if a view needs to be maximized
361  */
362 static bool
want_auto_maximize(struct roots_view * view)363 want_auto_maximize(struct roots_view *view) {
364   if (!view->desktop->maximize)
365     return false;
366 
367   if (view->impl->want_auto_maximize)
368     return view->impl->want_auto_maximize(view);
369 
370   return false;
371 }
372 
view_maximize(struct roots_view * view,struct wlr_output * output)373 void view_maximize(struct roots_view *view, struct wlr_output *output) {
374 	if (view_is_maximized (view) && view_get_output(view) == output) {
375 		return;
376 	}
377 
378 	if (view->fullscreen_output != NULL) {
379 		return;
380 	}
381 
382 	if (view->impl->maximize) {
383 		view->impl->maximize(view, true);
384 	}
385 
386 	if (view->toplevel_handle) {
387 		wlr_foreign_toplevel_handle_v1_set_maximized(view->toplevel_handle, true);
388 	}
389 
390 	view_save (view);
391 
392 	view->state = PHOC_VIEW_STATE_MAXIMIZED;
393 	view_arrange_maximized(view, output);
394 }
395 
396 /*
397  * Maximize view if in auto-maximize mode otherwise do nothing.
398  */
399 void
view_auto_maximize(struct roots_view * view)400 view_auto_maximize(struct roots_view *view)
401 {
402   if (want_auto_maximize (view))
403     view_maximize (view, NULL);
404 }
405 
406 void
view_restore(struct roots_view * view)407 view_restore(struct roots_view *view)
408 {
409   if (!view_is_maximized (view) && !view_is_tiled (view))
410     return;
411 
412   if (want_auto_maximize (view))
413     return;
414 
415   struct wlr_box geom;
416   view_get_geometry(view, &geom);
417 
418   view->state = PHOC_VIEW_STATE_NORMAL;
419   view_move_resize (view, view->saved.x - geom.x * view->scale, view->saved.y - geom.y * view->scale,
420                     view->saved.width, view->saved.height);
421 
422   if (view->toplevel_handle)
423     wlr_foreign_toplevel_handle_v1_set_maximized (view->toplevel_handle, false);
424 
425   if (view->impl->maximize)
426     view->impl->maximize (view, false);
427 }
428 
view_set_fullscreen(struct roots_view * view,bool fullscreen,struct wlr_output * output)429 void view_set_fullscreen(struct roots_view *view, bool fullscreen,
430 		struct wlr_output *output) {
431 	bool was_fullscreen = view->fullscreen_output != NULL;
432 
433 	// TODO: check if client is focused?
434 
435 	if (!was_fullscreen) {
436 		if (view->impl->set_fullscreen) {
437 			view->impl->set_fullscreen(view, fullscreen);
438 		}
439 
440 		if (view->toplevel_handle) {
441 			wlr_foreign_toplevel_handle_v1_set_fullscreen(view->toplevel_handle,
442 					fullscreen);
443 		}
444 	}
445 
446 	struct wlr_box view_geom;
447 	view_get_geometry(view, &view_geom);
448 
449 	if (fullscreen) {
450 		if (output == NULL) {
451 			output = view_get_output(view);
452 		}
453 		PhocOutput *phoc_output = output->data;
454 		if (phoc_output == NULL) {
455 			return;
456 		}
457 
458 		if (was_fullscreen) {
459 			view->fullscreen_output->fullscreen_view = NULL;
460 		}
461 
462 		struct wlr_box view_box;
463 		view_get_box(view, &view_box);
464 
465 		view_save (view);
466 
467 		struct wlr_box *output_box =
468 			wlr_output_layout_get_box(view->desktop->layout, output);
469 		view_move_resize(view, output_box->x, output_box->y, output_box->width,
470 			output_box->height);
471 
472 		phoc_output->fullscreen_view = view;
473 		phoc_output->force_shell_reveal = false;
474 		view->fullscreen_output = phoc_output;
475 		phoc_output_damage_whole(phoc_output);
476 	}
477 
478 	if (was_fullscreen && !fullscreen) {
479 		PhocOutput *phoc_output = view->fullscreen_output;
480 		view->fullscreen_output->fullscreen_view = NULL;
481 		view->fullscreen_output = NULL;
482 
483 		phoc_output_damage_whole(phoc_output);
484 
485 		if (view->state == PHOC_VIEW_STATE_MAXIMIZED) {
486 			view_arrange_maximized (view, phoc_output->wlr_output);
487 		} else if (view->state == PHOC_VIEW_STATE_TILED) {
488 			view_arrange_tiled (view, phoc_output->wlr_output);
489 		} else {
490 			view_move_resize(view, view->saved.x - view_geom.x * view->scale, view->saved.y - view_geom.y * view->scale,
491 			                 view->saved.width, view->saved.height);
492 		}
493 
494 		view_auto_maximize(view);
495 	}
496 }
497 
view_close(struct roots_view * view)498 void view_close(struct roots_view *view) {
499 	if (view->impl->close) {
500 		view->impl->close(view);
501 	}
502 }
503 
504 bool
view_move_to_next_output(struct roots_view * view,enum wlr_direction direction)505 view_move_to_next_output (struct roots_view *view, enum wlr_direction direction)
506 {
507   PhocDesktop *desktop = view->desktop;
508   struct wlr_output_layout *layout = view->desktop->layout;
509   const struct wlr_output_layout_output *l_output;
510   PhocOutput *phoc_output;
511   struct wlr_output *output, *new_output;
512   struct wlr_box usable_area;
513   double x, y;
514 
515   if (view->fullscreen_output)
516     return false;
517 
518   output = view_get_output(view);
519   if (!output)
520     return false;
521 
522   /* use current view's x,y as ref_lx, ref_ly */
523   new_output = wlr_output_layout_adjacent_output (layout, direction, output,
524 						  view->box.x, view->box.y);
525   if (!new_output)
526     return false;
527 
528   phoc_output = new_output->data;
529   usable_area = phoc_output->usable_area;
530   l_output = wlr_output_layout_get(desktop->layout, new_output);
531 
532   /* use a proper position on the new output */
533   x = usable_area.x + l_output->x;
534   y = usable_area.y + l_output->y;
535   g_debug("moving view to %f %f", x, y);
536 
537   view->saved.x = x;
538   view->saved.y = y;
539 
540   if (view_is_maximized (view)) {
541     view_arrange_maximized (view, new_output);
542   } else if (view_is_tiled (view)) {
543     view_arrange_tiled (view, new_output);
544   } else {
545     view_move(view, x, y);
546   }
547 
548   return true;
549 }
550 
551 void
view_tile(struct roots_view * view,PhocViewTileDirection direction,struct wlr_output * output)552 view_tile(struct roots_view *view, PhocViewTileDirection direction, struct wlr_output *output)
553 {
554   if (view->fullscreen_output)
555     return;
556 
557   /* Set the maximized flag on the toplevel so it remove it's drop shadows */
558   if (view->impl->maximize)
559     view->impl->maximize(view, true);
560 
561   view_save (view);
562 
563   view->state = PHOC_VIEW_STATE_TILED;
564   view->tile_direction = direction;
565   view_arrange_tiled (view, output);
566 }
567 
view_center(struct roots_view * view)568 bool view_center(struct roots_view *view) {
569         PhocServer *server = phoc_server_get_default ();
570 	struct wlr_box box, geom;
571 	view_get_box(view, &box);
572 	view_get_geometry (view, &geom);
573 
574 	PhocDesktop *desktop = view->desktop;
575 	PhocInput *input = server->input;
576 	struct roots_seat *seat = input_last_active_seat(input);
577 	struct roots_cursor *cursor;
578 
579 	if (!seat) {
580 		return false;
581 	}
582 	cursor = roots_seat_get_cursor (seat);
583 
584 	struct wlr_output *output = wlr_output_layout_output_at(desktop->layout,
585 		cursor->cursor->x, cursor->cursor->y);
586 	if (!output) {
587 		// empty layout
588 		return false;
589 	}
590 
591 	const struct wlr_output_layout_output *l_output =
592 		wlr_output_layout_get(desktop->layout, output);
593 
594 	PhocOutput *phoc_output = output->data;
595 	struct wlr_box usable_area = phoc_output->usable_area;
596 
597 	double view_x = (double)(usable_area.width - box.width) / 2 +
598 	  usable_area.x + l_output->x - geom.x * view->scale;
599 	double view_y = (double)(usable_area.height - box.height) / 2 +
600 	  usable_area.y + l_output->y - geom.y * view->scale;
601 
602 	g_debug ("moving view to %f %f", view_x, view_y);
603 	view_move(view, view_x / view->scale, view_y / view->scale);
604 
605 	if (!desktop->maximize) {
606 		// TODO: fitting floating oversized windows requires more work (!228)
607 		return true;
608 	}
609 
610 	if (view->box.width > phoc_output->usable_area.width || view->box.height > phoc_output->usable_area.height) {
611 		view_resize (view,
612 		             (view->box.width > phoc_output->usable_area.width) ? phoc_output->usable_area.width : view->box.width,
613 		             (view->box.height > phoc_output->usable_area.height) ? phoc_output->usable_area.height : view->box.height);
614 	}
615 
616 	return true;
617 }
618 
view_child_destroy(struct roots_view_child * child)619 void view_child_destroy(struct roots_view_child *child) {
620 	if (child == NULL) {
621 		return;
622 	}
623 	view_damage_whole(child->view);
624 	wl_list_remove(&child->link);
625 	wl_list_remove(&child->commit.link);
626 	wl_list_remove(&child->new_subsurface.link);
627 	child->impl->destroy(child);
628 }
629 
view_child_handle_commit(struct wl_listener * listener,void * data)630 static void view_child_handle_commit(struct wl_listener *listener,
631 		void *data) {
632 	struct roots_view_child *child = wl_container_of(listener, child, commit);
633 	view_apply_damage(child->view);
634 }
635 
view_child_handle_new_subsurface(struct wl_listener * listener,void * data)636 static void view_child_handle_new_subsurface(struct wl_listener *listener,
637 		void *data) {
638 	struct roots_view_child *child =
639 		wl_container_of(listener, child, new_subsurface);
640 	struct wlr_subsurface *wlr_subsurface = data;
641 	subsurface_create(child->view, wlr_subsurface);
642 }
643 
view_child_init(struct roots_view_child * child,const struct roots_view_child_interface * impl,struct roots_view * view,struct wlr_surface * wlr_surface)644 void view_child_init(struct roots_view_child *child,
645 		const struct roots_view_child_interface *impl, struct roots_view *view,
646 		struct wlr_surface *wlr_surface) {
647 	assert(impl->destroy);
648 	child->impl = impl;
649 	child->view = view;
650 	child->wlr_surface = wlr_surface;
651 	child->commit.notify = view_child_handle_commit;
652 	wl_signal_add(&wlr_surface->events.commit, &child->commit);
653 	child->new_subsurface.notify = view_child_handle_new_subsurface;
654 	wl_signal_add(&wlr_surface->events.new_subsurface, &child->new_subsurface);
655 	wl_list_insert(&view->child_surfaces, &child->link);
656 }
657 
658 static const struct roots_view_child_interface subsurface_impl;
659 
subsurface_destroy(struct roots_view_child * child)660 static void subsurface_destroy(struct roots_view_child *child) {
661 	assert(child->impl == &subsurface_impl);
662 	struct roots_subsurface *subsurface = (struct roots_subsurface *)child;
663 	wl_list_remove(&subsurface->destroy.link);
664 	wl_list_remove(&subsurface->map.link);
665 	wl_list_remove(&subsurface->unmap.link);
666 	free(subsurface);
667 }
668 
669 static const struct roots_view_child_interface subsurface_impl = {
670 	.destroy = subsurface_destroy,
671 };
672 
subsurface_handle_destroy(struct wl_listener * listener,void * data)673 static void subsurface_handle_destroy(struct wl_listener *listener,
674 		void *data) {
675 	struct roots_subsurface *subsurface =
676 		wl_container_of(listener, subsurface, destroy);
677 	view_child_destroy(&subsurface->view_child);
678 }
679 
subsurface_handle_map(struct wl_listener * listener,void * data)680 static void subsurface_handle_map(struct wl_listener *listener,
681 		void *data) {
682 	PhocServer *server = phoc_server_get_default ();
683 	struct roots_subsurface *subsurface =
684 		wl_container_of(listener, subsurface, map);
685 	struct roots_view *view = subsurface->view_child.view;
686 	view_damage_whole(view);
687 	phoc_input_update_cursor_focus(server->input);
688 
689 	struct wlr_box box;
690 	view_get_box(view, &box);
691 	PhocOutput *output;
692 	wl_list_for_each(output, &view->desktop->outputs, link) {
693 		bool intersects = wlr_output_layout_intersects(view->desktop->layout,
694 			output->wlr_output, &box);
695 		if (intersects) {
696 			wlr_surface_send_enter (subsurface->wlr_subsurface->surface, output->wlr_output);
697 		}
698 	}
699 }
700 
subsurface_handle_unmap(struct wl_listener * listener,void * data)701 static void subsurface_handle_unmap(struct wl_listener *listener,
702 		void *data) {
703 	PhocServer *server = phoc_server_get_default ();
704 	struct roots_subsurface *subsurface =
705 		wl_container_of(listener, subsurface, unmap);
706 	struct roots_view *view = subsurface->view_child.view;
707 	view_damage_whole(view);
708 	phoc_input_update_cursor_focus(server->input);
709 }
710 
subsurface_create(struct roots_view * view,struct wlr_subsurface * wlr_subsurface)711 struct roots_subsurface *subsurface_create(struct roots_view *view,
712 		struct wlr_subsurface *wlr_subsurface) {
713 	struct roots_subsurface *subsurface =
714 		calloc(1, sizeof(struct roots_subsurface));
715 	if (subsurface == NULL) {
716 		return NULL;
717 	}
718 	subsurface->wlr_subsurface = wlr_subsurface;
719 	view_child_init(&subsurface->view_child, &subsurface_impl,
720 		view, wlr_subsurface->surface);
721 	subsurface->destroy.notify = subsurface_handle_destroy;
722 	wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy);
723 	subsurface->map.notify = subsurface_handle_map;
724 	wl_signal_add(&wlr_subsurface->events.map, &subsurface->map);
725 	subsurface->unmap.notify = subsurface_handle_unmap;
726 	wl_signal_add(&wlr_subsurface->events.unmap, &subsurface->unmap);
727 	return subsurface;
728 }
729 
view_handle_new_subsurface(struct wl_listener * listener,void * data)730 static void view_handle_new_subsurface(struct wl_listener *listener,
731 		void *data) {
732 	struct roots_view *view = wl_container_of(listener, view, new_subsurface);
733 	struct wlr_subsurface *wlr_subsurface = data;
734 	subsurface_create(view, wlr_subsurface);
735 }
736 
737 static gchar *
munge_app_id(const gchar * app_id)738 munge_app_id (const gchar *app_id)
739 {
740   gchar *id = g_strdup (app_id);
741   gint i;
742 
743   g_strcanon (id,
744               "0123456789"
745               "abcdefghijklmnopqrstuvwxyz"
746               "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
747               "-",
748               '-');
749   for (i = 0; id[i] != '\0'; i++)
750     id[i] = g_ascii_tolower (id[i]);
751 
752   return id;
753 }
754 
view_update_scale(struct roots_view * view)755 static void view_update_scale(struct roots_view *view) {
756 	PhocServer *server = phoc_server_get_default ();
757 
758 	if (!view->impl->want_scaling(view)) {
759 		return;
760 	}
761 
762 	bool scaling_enabled = false;
763 
764 	if (view->app_id) {
765 		g_autofree gchar *munged_app_id = munge_app_id (view->app_id);
766 		g_autofree gchar *path = g_strconcat ("/sm/puri/phoc/application/", munged_app_id, "/", NULL);
767 		g_autoptr (GSettings) setting =  g_settings_new_with_path ("sm.puri.phoc.application", path);
768 		scaling_enabled = g_settings_get_boolean (setting, "scale-to-fit");
769 	}
770 
771 	if (!scaling_enabled && !phoc_desktop_get_scale_to_fit (server->desktop)) {
772 		return;
773 	}
774 
775 	struct wlr_output *output = view_get_output(view);
776 	if (!output) {
777 		return;
778 	}
779 
780 	PhocOutput *phoc_output = output->data;
781 
782 	float scalex = 1.0f, scaley = 1.0f, oldscale = view->scale;
783 	scalex = phoc_output->usable_area.width / (float)view->box.width;
784 	scaley = phoc_output->usable_area.height / (float)view->box.height;
785 	if (scaley < scalex) {
786 		view->scale = scaley;
787 	} else {
788 		view->scale = scalex;
789 	}
790 	if (view->scale < 0.5f) {
791 		view->scale = 0.5f;
792 	}
793 	if (view->scale > 1.0f || view->fullscreen_output) {
794 		view->scale = 1.0f;
795 	}
796 	if (view->scale != oldscale) {
797 		if (view_is_maximized(view)) {
798 			view_arrange_maximized(view, NULL);
799 		} else if (view_is_tiled(view)) {
800 			view_arrange_tiled(view, NULL);
801 		} else {
802 			view_center(view);
803 		}
804 	}
805 }
806 
view_map(struct roots_view * view,struct wlr_surface * surface)807 void view_map(struct roots_view *view, struct wlr_surface *surface) {
808 	PhocServer *server = phoc_server_get_default ();
809 	assert(view->wlr_surface == NULL);
810 
811 	view->wlr_surface = surface;
812 
813 	struct wlr_subsurface *subsurface;
814 	wl_list_for_each(subsurface, &view->wlr_surface->subsurfaces,
815 			parent_link) {
816 		subsurface_create(view, subsurface);
817 	}
818 
819 	view->new_subsurface.notify = view_handle_new_subsurface;
820 	wl_signal_add(&view->wlr_surface->events.new_subsurface,
821 		&view->new_subsurface);
822 
823 	if (view->desktop->maximize) {
824 		view_appear_activated(view, true);
825 
826 		if (!wl_list_empty(&view->desktop->views)) {
827 			// mapping a new stack may make the old stack disappear, so damage its area
828 			struct roots_view *top_view = wl_container_of(view->desktop->views.next, view, link);
829 			while (top_view) {
830 				view_damage_whole(top_view);
831 				top_view = top_view->parent;
832 			}
833 		}
834 	}
835 
836 	wl_list_insert(&view->desktop->views, &view->link);
837 	view_damage_whole(view);
838 	phoc_input_update_cursor_focus(server->input);
839 }
840 
view_unmap(struct roots_view * view)841 void view_unmap(struct roots_view *view) {
842 	assert(view->wlr_surface != NULL);
843 
844 	bool was_visible = phoc_desktop_view_is_visible(view->desktop, view);
845 
846 	wl_signal_emit(&view->events.unmap, view);
847 
848 	view_damage_whole(view);
849 
850 	wl_list_remove(&view->new_subsurface.link);
851 
852 	struct roots_view_child *child, *tmp;
853 	wl_list_for_each_safe(child, tmp, &view->child_surfaces, link) {
854 		view_child_destroy(child);
855 	}
856 
857 	if (view->fullscreen_output != NULL) {
858 		phoc_output_damage_whole(view->fullscreen_output);
859 		view->fullscreen_output->fullscreen_view = NULL;
860 		view->fullscreen_output = NULL;
861 	}
862 
863 	wl_list_remove(&view->link);
864 
865 	if (was_visible && view->desktop->maximize && !wl_list_empty(&view->desktop->views)) {
866 		// damage the newly activated stack as well since it may have just become visible
867 		struct roots_view *top_view = wl_container_of(view->desktop->views.next, view, link);
868 		while (top_view) {
869 			view_damage_whole(top_view);
870 			top_view = top_view->parent;
871 		}
872 	}
873 
874 	view->wlr_surface = NULL;
875 	view->box.width = view->box.height = 0;
876 
877 	if (view->toplevel_handle) {
878 		view->toplevel_handle->data = NULL;
879 		wlr_foreign_toplevel_handle_v1_destroy(view->toplevel_handle);
880 		view->toplevel_handle = NULL;
881 	}
882 }
883 
view_initial_focus(struct roots_view * view)884 void view_initial_focus(struct roots_view *view) {
885 	PhocServer *server = phoc_server_get_default ();
886 	PhocInput *input = server->input;
887 	// TODO what seat gets focus? the one with the last input event?
888 	struct roots_seat *seat;
889 	wl_list_for_each(seat, &input->seats, link) {
890 		roots_seat_set_focus(seat, view);
891 	}
892 }
893 
894 
view_setup(struct roots_view * view)895 void view_setup(struct roots_view *view) {
896 	view_create_foreign_toplevel_handle(view);
897 	view_initial_focus(view);
898 
899 	if (view->fullscreen_output == NULL && !view_is_maximized(view)) {
900 		view_center(view);
901 	}
902 	view_update_scale(view);
903 
904 	view_update_output(view, NULL);
905 
906 	wlr_foreign_toplevel_handle_v1_set_fullscreen(view->toplevel_handle,
907 	                                              view->fullscreen_output != NULL);
908 	wlr_foreign_toplevel_handle_v1_set_maximized(view->toplevel_handle,
909 	                                             view_is_maximized(view));
910 	wlr_foreign_toplevel_handle_v1_set_title(view->toplevel_handle,
911 	                                         view->title ?: "");
912 	wlr_foreign_toplevel_handle_v1_set_app_id(view->toplevel_handle,
913 	                                          view->app_id ?: "");
914 }
915 
view_apply_damage(struct roots_view * view)916 void view_apply_damage(struct roots_view *view) {
917 	PhocOutput *output;
918 	wl_list_for_each(output, &view->desktop->outputs, link) {
919 		phoc_output_damage_from_view(output, view);
920 	}
921 }
922 
view_damage_whole(struct roots_view * view)923 void view_damage_whole(struct roots_view *view) {
924 	PhocOutput *output;
925 	wl_list_for_each(output, &view->desktop->outputs, link) {
926 		phoc_output_damage_whole_view(output, view);
927 	}
928 }
929 
view_for_each_surface(struct roots_view * view,wlr_surface_iterator_func_t iterator,void * user_data)930 void view_for_each_surface(struct roots_view *view,
931 		wlr_surface_iterator_func_t iterator, void *user_data) {
932 	if (view->impl->for_each_surface) {
933 		view->impl->for_each_surface(view, iterator, user_data);
934 	} else if (view->wlr_surface) {
935 		wlr_surface_for_each_surface(view->wlr_surface, iterator, user_data);
936 	}
937 }
938 
view_update_position(struct roots_view * view,int x,int y)939 void view_update_position(struct roots_view *view, int x, int y) {
940 	if (view->box.x == x && view->box.y == y) {
941 		return;
942 	}
943 
944 	view_damage_whole(view);
945 	view->box.x = x;
946 	view->box.y = y;
947 	view_damage_whole(view);
948 }
949 
view_update_size(struct roots_view * view,int width,int height)950 void view_update_size(struct roots_view *view, int width, int height) {
951 
952 	if (view->box.width == width && view->box.height == height) {
953 		return;
954 	}
955 
956 	view_damage_whole(view);
957 	view->box.width = width;
958 	view->box.height = height;
959 	view_update_scale(view);
960 	view_damage_whole(view);
961 }
962 
view_update_decorated(struct roots_view * view,bool decorated)963 void view_update_decorated(struct roots_view *view, bool decorated) {
964 	if (view->decorated == decorated) {
965 		return;
966 	}
967 
968 	view_damage_whole(view);
969 	view->decorated = decorated;
970 	if (decorated) {
971 		view->border_width = 4;
972 		view->titlebar_height = 12;
973 	} else {
974 		view->border_width = 0;
975 		view->titlebar_height = 0;
976 	}
977 	view_damage_whole(view);
978 }
979 
view_set_title(struct roots_view * view,const char * title)980 void view_set_title(struct roots_view *view, const char *title) {
981 	free(view->title);
982 	view->title = title ? strdup(title) : NULL;
983 
984 	if (view->toplevel_handle) {
985 		wlr_foreign_toplevel_handle_v1_set_title(view->toplevel_handle, title ?: "");
986 	}
987 }
988 
view_set_parent(struct roots_view * view,struct roots_view * parent)989 void view_set_parent(struct roots_view *view, struct roots_view *parent) {
990 	// setting a new parent may cause a cycle
991 	struct roots_view *node = parent;
992 	while (node) {
993 		g_return_if_fail(node != view);
994 		node = node->parent;
995 	}
996 
997 	if (view->parent) {
998 		wl_list_remove(&view->parent_link);
999 		wl_list_init(&view->parent_link);
1000 	}
1001 
1002 	view->parent = parent;
1003 	if (parent) {
1004 		wl_list_insert(&parent->stack, &view->parent_link);
1005 	}
1006 }
1007 
view_set_app_id(struct roots_view * view,const char * app_id)1008 void view_set_app_id(struct roots_view *view, const char *app_id) {
1009 	free(view->app_id);
1010 	view->app_id = app_id ? strdup(app_id) : NULL;
1011 
1012 	view_update_scale(view);
1013 
1014 	if (view->toplevel_handle) {
1015 		wlr_foreign_toplevel_handle_v1_set_app_id(view->toplevel_handle, app_id ?: "");
1016 	}
1017 }
1018 
handle_toplevel_handle_request_maximize(struct wl_listener * listener,void * data)1019 static void handle_toplevel_handle_request_maximize(struct wl_listener *listener,
1020 		void *data) {
1021 	struct roots_view *view = wl_container_of(listener, view,
1022 			toplevel_handle_request_maximize);
1023 	struct wlr_foreign_toplevel_handle_v1_maximized_event *event = data;
1024 	if (event->maximized) {
1025 		view_maximize(view, NULL);
1026 	} else {
1027 		view_restore(view);
1028 	}
1029 }
1030 
handle_toplevel_handle_request_activate(struct wl_listener * listener,void * data)1031 static void handle_toplevel_handle_request_activate(struct wl_listener *listener,
1032 		void *data) {
1033 	PhocServer *server = phoc_server_get_default ();
1034 	struct roots_view *view =
1035 		wl_container_of(listener, view, toplevel_handle_request_activate);
1036 	struct wlr_foreign_toplevel_handle_v1_activated_event *event = data;
1037 
1038 	struct roots_seat *seat;
1039 	wl_list_for_each(seat, &server->input->seats, link) {
1040 		if (event->seat == seat->seat) {
1041 			roots_seat_set_focus(seat, view);
1042 		}
1043 	}
1044 }
1045 
handle_toplevel_handle_request_fullscreen(struct wl_listener * listener,void * data)1046 static void handle_toplevel_handle_request_fullscreen(struct wl_listener *listener,
1047 		void *data)  {
1048 	struct roots_view *view =
1049 		wl_container_of(listener, view, toplevel_handle_request_fullscreen);
1050 	struct wlr_foreign_toplevel_handle_v1_fullscreen_event *event = data;
1051 	view_set_fullscreen(view, event->fullscreen, event->output);
1052 }
1053 
handle_toplevel_handle_request_close(struct wl_listener * listener,void * data)1054 static void handle_toplevel_handle_request_close(struct wl_listener *listener,
1055 		void *data) {
1056 	struct roots_view *view =
1057 		wl_container_of(listener, view, toplevel_handle_request_close);
1058 	view_close(view);
1059 }
1060 
view_create_foreign_toplevel_handle(struct roots_view * view)1061 void view_create_foreign_toplevel_handle(struct roots_view *view) {
1062 	view->toplevel_handle =
1063 		wlr_foreign_toplevel_handle_v1_create(
1064 			view->desktop->foreign_toplevel_manager_v1);
1065 
1066 	view->toplevel_handle_request_maximize.notify =
1067 		handle_toplevel_handle_request_maximize;
1068 	wl_signal_add(&view->toplevel_handle->events.request_maximize,
1069 			&view->toplevel_handle_request_maximize);
1070 	view->toplevel_handle_request_activate.notify =
1071 		handle_toplevel_handle_request_activate;
1072 	wl_signal_add(&view->toplevel_handle->events.request_activate,
1073 			&view->toplevel_handle_request_activate);
1074 	view->toplevel_handle_request_fullscreen.notify =
1075 		handle_toplevel_handle_request_fullscreen;
1076 	wl_signal_add(&view->toplevel_handle->events.request_fullscreen,
1077 			&view->toplevel_handle_request_fullscreen);
1078 	view->toplevel_handle_request_close.notify =
1079 		handle_toplevel_handle_request_close;
1080 	wl_signal_add(&view->toplevel_handle->events.request_close,
1081 			&view->toplevel_handle_request_close);
1082 
1083 	view->toplevel_handle->data = view;
1084 }
1085 
1086 /*
1087  * roots_view_from_wlr_surface::
1088  *
1089  * Given a #wlr_surface return the corresponding view
1090  */
1091 struct roots_view *
roots_view_from_wlr_surface(struct wlr_surface * wlr_surface)1092 roots_view_from_wlr_surface (struct wlr_surface *wlr_surface)
1093 {
1094   PhocServer *server = phoc_server_get_default ();
1095   PhocDesktop *desktop = server->desktop;
1096   struct roots_view *view;
1097 
1098   wl_list_for_each(view, &desktop->views, link) {
1099     if (view->wlr_surface == wlr_surface) {
1100       return view;
1101     }
1102   }
1103 
1104   return NULL;
1105 }
1106