1 #define G_LOG_DOMAIN "phoc-desktop"
2 
3 #include "config.h"
4 
5 #define _POSIX_C_SOURCE 200112L
6 #include <assert.h>
7 #include <math.h>
8 #include <stdlib.h>
9 #include <time.h>
10 #include <wlr/config.h>
11 #include <wlr/types/wlr_box.h>
12 #include <wlr/types/wlr_compositor.h>
13 #include <wlr/types/wlr_cursor.h>
14 #include <wlr/types/wlr_data_control_v1.h>
15 #include <wlr/types/wlr_export_dmabuf_v1.h>
16 #include <wlr/types/wlr_gamma_control_v1.h>
17 #include <wlr/types/wlr_gtk_primary_selection.h>
18 #include <wlr/types/wlr_idle_inhibit_v1.h>
19 #include <wlr/types/wlr_idle.h>
20 #include <wlr/types/wlr_input_inhibitor.h>
21 #include <wlr/types/wlr_layer_shell_v1.h>
22 #include <wlr/types/wlr_output_layout.h>
23 #include <wlr/types/wlr_output_power_management_v1.h>
24 #include <wlr/types/wlr_pointer_constraints_v1.h>
25 #include <wlr/types/wlr_server_decoration.h>
26 #include <wlr/types/wlr_tablet_v2.h>
27 #include <wlr/types/wlr_xcursor_manager.h>
28 #include <wlr/types/wlr_xdg_output_v1.h>
29 #include <wlr/types/wlr_xdg_output_v1.h>
30 #include <wlr/types/wlr_xdg_shell.h>
31 #include <wlr/util/log.h>
32 #include <wlr/version.h>
33 #include "layers.h"
34 #include "output.h"
35 #include "seat.h"
36 #include "server.h"
37 #include "utils.h"
38 #include "view.h"
39 #include "virtual.h"
40 #include "xcursor.h"
41 #include "wlr-layer-shell-unstable-v1-protocol.h"
42 
43 enum {
44   PROP_0,
45   PROP_CONFIG,
46   PROP_LAST_PROP,
47 };
48 static GParamSpec *props[PROP_LAST_PROP];
49 
50 G_DEFINE_TYPE(PhocDesktop, phoc_desktop, G_TYPE_OBJECT);
51 
52 
53 static void
phoc_desktop_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)54 phoc_desktop_set_property (GObject     *object,
55                           guint         property_id,
56                           const GValue *value,
57                           GParamSpec   *pspec)
58 {
59   PhocDesktop *self = PHOC_DESKTOP (object);
60 
61   switch (property_id) {
62   case PROP_CONFIG:
63     self->config = g_value_get_pointer (value);
64     g_object_notify_by_pspec (G_OBJECT (self), props[PROP_CONFIG]);
65     break;
66   default:
67     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
68     break;
69   }
70 }
71 
72 
73 static void
phoc_desktop_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)74 phoc_desktop_get_property (GObject    *object,
75 			   guint       property_id,
76 			   GValue     *value,
77 			   GParamSpec *pspec)
78 {
79   PhocDesktop *self = PHOC_DESKTOP (object);
80 
81   switch (property_id) {
82   case PROP_CONFIG:
83     g_value_set_pointer (value, self->config);
84     break;
85   default:
86     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
87     break;
88   }
89 }
90 
91 
view_at(struct roots_view * view,double lx,double ly,struct wlr_surface ** surface,double * sx,double * sy)92 static bool view_at(struct roots_view *view, double lx, double ly,
93 		struct wlr_surface **surface, double *sx, double *sy) {
94 	if (view->wlr_surface == NULL) {
95 		return false;
96 	}
97 
98 	double view_sx = lx / view->scale - view->box.x;
99 	double view_sy = ly / view->scale - view->box.y;
100 
101 	double _sx, _sy;
102 	struct wlr_surface *_surface = NULL;
103 	switch (view->type) {
104 	case ROOTS_XDG_SHELL_VIEW:;
105 		struct roots_xdg_surface *xdg_surface =
106 			roots_xdg_surface_from_view(view);
107 		_surface = wlr_xdg_surface_surface_at(xdg_surface->xdg_surface,
108 			view_sx, view_sy, &_sx, &_sy);
109 		break;
110 #ifdef PHOC_XWAYLAND
111 	case ROOTS_XWAYLAND_VIEW:
112 		_surface = wlr_surface_surface_at(view->wlr_surface,
113 			view_sx, view_sy, &_sx, &_sy);
114 		break;
115 #endif
116 	default:
117 		g_error("Invalid view type %d", view->type);
118 	}
119 	if (_surface != NULL) {
120 		*sx = _sx;
121 		*sy = _sy;
122 		*surface = _surface;
123 		return true;
124 	}
125 
126 	if (view_get_deco_part(view, view_sx, view_sy)) {
127 		*sx = view_sx;
128 		*sy = view_sy;
129 		*surface = NULL;
130 		return true;
131 	}
132 
133 	return false;
134 }
135 
desktop_view_at(PhocDesktop * desktop,double lx,double ly,struct wlr_surface ** surface,double * sx,double * sy)136 static struct roots_view *desktop_view_at(PhocDesktop *desktop,
137 		double lx, double ly, struct wlr_surface **surface,
138 		double *sx, double *sy) {
139 	struct roots_view *view;
140 	wl_list_for_each(view, &desktop->views, link) {
141 		if (phoc_desktop_view_is_visible(desktop, view) && view_at(view, lx, ly, surface, sx, sy)) {
142 			return view;
143 		}
144 	}
145 	return NULL;
146 }
147 
layer_surface_at(struct wl_list * layer,double ox,double oy,double * sx,double * sy)148 static struct wlr_surface *layer_surface_at(struct wl_list *layer, double ox,
149                                              double oy, double *sx, double *sy)
150 {
151 	struct roots_layer_surface *roots_surface;
152 
153 	wl_list_for_each_reverse(roots_surface, layer, link) {
154 		if (roots_surface->layer_surface->current.exclusive_zone <= 0) {
155 			continue;
156 		}
157 
158 		double _sx = ox - roots_surface->geo.x;
159 		double _sy = oy - roots_surface->geo.y;
160 
161 		struct wlr_surface *sub = wlr_layer_surface_v1_surface_at(
162 			roots_surface->layer_surface, _sx, _sy, sx, sy);
163 
164 		if (sub) {
165 			return sub;
166 		}
167 	}
168 
169 	wl_list_for_each(roots_surface, layer, link) {
170 		if (roots_surface->layer_surface->current.exclusive_zone > 0) {
171 			continue;
172 		}
173 
174 		double _sx = ox - roots_surface->geo.x;
175 		double _sy = oy - roots_surface->geo.y;
176 
177 		struct wlr_surface *sub = wlr_layer_surface_v1_surface_at(
178 			roots_surface->layer_surface, _sx, _sy, sx, sy);
179 
180 		if (sub) {
181 			return sub;
182 		}
183 	}
184 
185 	return NULL;
186 }
187 
phoc_desktop_surface_at(PhocDesktop * desktop,double lx,double ly,double * sx,double * sy,struct roots_view ** view)188 struct wlr_surface *phoc_desktop_surface_at(PhocDesktop *desktop,
189 		double lx, double ly, double *sx, double *sy,
190 		struct roots_view **view) {
191 	struct wlr_surface *surface = NULL;
192 	struct wlr_output *wlr_output =
193 		wlr_output_layout_output_at(desktop->layout, lx, ly);
194 	PhocOutput *phoc_output = NULL;
195 	double ox = lx, oy = ly;
196 	if (view) {
197 		*view = NULL;
198 	}
199 
200 	if (wlr_output) {
201 		phoc_output = wlr_output->data;
202 		wlr_output_layout_output_coords(desktop->layout, wlr_output, &ox, &oy);
203 
204 		if ((surface = layer_surface_at(&phoc_output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY],
205 						ox, oy, sx, sy))) {
206 			return surface;
207 		}
208 
209 		PhocOutput *output = wlr_output->data;
210 		if (output != NULL && output->fullscreen_view != NULL) {
211 
212 			if (output->force_shell_reveal) {
213 				if ((surface = layer_surface_at(&phoc_output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP],
214 								ox, oy, sx, sy))) {
215 					return surface;
216 				}
217 			}
218 
219 			if (view_at(output->fullscreen_view, lx, ly, &surface, sx, sy)) {
220 				if (view) {
221 					*view = output->fullscreen_view;
222 				}
223 				return surface;
224 			} else {
225 				return NULL;
226 			}
227 		}
228 
229 		if ((surface = layer_surface_at(&phoc_output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP],
230 						ox, oy, sx, sy))) {
231 			return surface;
232 		}
233 	}
234 
235 	struct roots_view *_view;
236 	if ((_view = desktop_view_at(desktop, lx, ly, &surface, sx, sy))) {
237 		if (view) {
238 			*view = _view;
239 		}
240 		return surface;
241 	}
242 
243 	if (wlr_output) {
244 		if ((surface = layer_surface_at(&phoc_output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM],
245 						ox, oy, sx, sy))) {
246 			return surface;
247 		}
248 		if ((surface = layer_surface_at(&phoc_output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND],
249 						ox, oy, sx, sy))) {
250 			return surface;
251 		}
252 	}
253 	return NULL;
254 }
255 
256 gboolean
phoc_desktop_view_is_visible(PhocDesktop * desktop,struct roots_view * view)257 phoc_desktop_view_is_visible (PhocDesktop *desktop, struct roots_view *view)
258 {
259   if (!view->wlr_surface) {
260     return false;
261   }
262 
263   g_assert_false (wl_list_empty (&desktop->views));
264 
265   if (wl_list_length (&desktop->outputs) != 1) {
266     // current heuristics work well only for single output
267     return true;
268   }
269 
270   if (!desktop->maximize) {
271     return true;
272   }
273 
274   struct roots_view *top_view = wl_container_of (desktop->views.next, view, link);
275 
276 #ifdef PHOC_XWAYLAND
277   // XWayland parent relations can be complicated and aren't described by roots_view
278   // relationships very well at the moment, so just make all XWayland windows visible
279   // when some XWayland window is active for now
280   if (view->type == ROOTS_XWAYLAND_VIEW && top_view->type == ROOTS_XWAYLAND_VIEW) {
281     return true;
282   }
283 #endif
284 
285   struct roots_view *v = top_view;
286   while (v) {
287     if (v == view) {
288       return true;
289     }
290     if (view_is_maximized (v)) {
291       return false;
292     }
293     v = v->parent;
294   }
295 
296   return false;
297 }
298 
299 static void
handle_layout_change(struct wl_listener * listener,void * data)300 handle_layout_change (struct wl_listener *listener, void *data)
301 {
302   PhocDesktop *self;
303   struct wlr_output *center_output;
304   struct wlr_box *center_output_box;
305   double center_x, center_y;
306   struct roots_view *view;
307   PhocOutput *output;
308 
309   self = wl_container_of (listener, self, layout_change);
310   center_output = wlr_output_layout_get_center_output (self->layout);
311   if (center_output == NULL)
312     return;
313 
314   center_output_box = wlr_output_layout_get_box (self->layout, center_output);
315   center_x = center_output_box->x + center_output_box->width / 2;
316   center_y = center_output_box->y + center_output_box->height / 2;
317 
318   /* Make sure all views are on an existing output */
319   wl_list_for_each (view, &self->views, link) {
320     struct wlr_box box;
321     view_get_box (view, &box);
322 
323     if (wlr_output_layout_intersects (self->layout, NULL, &box))
324       continue;
325     view_move (view, center_x - box.width / 2, center_y - box.height / 2);
326   }
327 
328   /* Damage all outputs since the move above damaged old layout space */
329   wl_list_for_each(output, &self->outputs, link)
330     phoc_output_damage_whole(output);
331 }
332 
input_inhibit_activate(struct wl_listener * listener,void * data)333 static void input_inhibit_activate(struct wl_listener *listener, void *data) {
334 	PhocDesktop *desktop = wl_container_of(
335 			listener, desktop, input_inhibit_activate);
336 	struct roots_seat *seat;
337 	PhocServer *server = phoc_server_get_default ();
338 
339 	wl_list_for_each(seat, &server->input->seats, link) {
340 		roots_seat_set_exclusive_client(seat,
341 				desktop->input_inhibit->active_client);
342 	}
343 }
344 
input_inhibit_deactivate(struct wl_listener * listener,void * data)345 static void input_inhibit_deactivate(struct wl_listener *listener, void *data) {
346 	PhocDesktop *desktop = wl_container_of(
347 			listener, desktop, input_inhibit_deactivate);
348 	struct roots_seat *seat;
349 	PhocServer *server = phoc_server_get_default ();
350 
351 	wl_list_for_each(seat, &server->input->seats, link) {
352 		roots_seat_set_exclusive_client(seat, NULL);
353 	}
354 }
355 
handle_constraint_destroy(struct wl_listener * listener,void * data)356 static void handle_constraint_destroy(struct wl_listener *listener,
357 		void *data) {
358 	struct roots_pointer_constraint *constraint =
359 		wl_container_of(listener, constraint, destroy);
360 	struct wlr_pointer_constraint_v1 *wlr_constraint = data;
361 	struct roots_seat *seat = wlr_constraint->seat->data;
362 	struct roots_cursor *cursor = roots_seat_get_cursor (seat);
363 
364 	wl_list_remove(&constraint->destroy.link);
365 
366 	if (cursor->active_constraint == wlr_constraint) {
367 		wl_list_remove(&cursor->constraint_commit.link);
368 		wl_list_init(&cursor->constraint_commit.link);
369 		cursor->active_constraint = NULL;
370 
371 		if (wlr_constraint->current.committed &
372 				WLR_POINTER_CONSTRAINT_V1_STATE_CURSOR_HINT &&
373 				cursor->pointer_view) {
374 			struct roots_view *view = cursor->pointer_view->view;
375 			double lx = view->box.x + wlr_constraint->current.cursor_hint.x;
376 			double ly = view->box.y + wlr_constraint->current.cursor_hint.y;
377 
378 			wlr_cursor_warp(cursor->cursor, NULL, lx, ly);
379 		}
380 	}
381 
382 	free(constraint);
383 }
384 
handle_pointer_constraint(struct wl_listener * listener,void * data)385 static void handle_pointer_constraint(struct wl_listener *listener,
386 		void *data) {
387 	PhocServer *server = phoc_server_get_default ();
388 	struct wlr_pointer_constraint_v1 *wlr_constraint = data;
389 	struct roots_seat *seat = wlr_constraint->seat->data;
390 	struct roots_cursor *cursor = roots_seat_get_cursor (seat);
391 
392 	struct roots_pointer_constraint *constraint =
393 		calloc(1, sizeof(struct roots_pointer_constraint));
394 	constraint->constraint = wlr_constraint;
395 
396 	constraint->destroy.notify = handle_constraint_destroy;
397 	wl_signal_add(&wlr_constraint->events.destroy, &constraint->destroy);
398 
399 	double sx, sy;
400 	struct wlr_surface *surface = phoc_desktop_surface_at(
401 		server->desktop,
402 		cursor->cursor->x, cursor->cursor->y, &sx, &sy, NULL);
403 
404 	if (surface == wlr_constraint->surface) {
405 		assert(!cursor->active_constraint);
406 		roots_cursor_constrain(cursor, wlr_constraint, sx, sy);
407 	}
408 }
409 
410 static void
auto_maximize_changed_cb(PhocDesktop * self,const gchar * key,GSettings * settings)411 auto_maximize_changed_cb (PhocDesktop *self,
412 			  const gchar *key,
413 			  GSettings   *settings)
414 {
415   gboolean max = g_settings_get_boolean (settings, key);
416 
417   g_return_if_fail (PHOC_IS_DESKTOP (self));
418   g_return_if_fail (G_IS_SETTINGS (settings));
419 
420   phoc_desktop_set_auto_maximize (self, max);
421 }
422 
423 static void
scale_to_fit_changed_cb(PhocDesktop * self,const gchar * key,GSettings * settings)424 scale_to_fit_changed_cb (PhocDesktop *self,
425 			  const gchar *key,
426 			  GSettings   *settings)
427 {
428     gboolean max = g_settings_get_boolean (settings, key);
429 
430     g_return_if_fail (PHOC_IS_DESKTOP (self));
431     g_return_if_fail (G_IS_SETTINGS (settings));
432 
433     phoc_desktop_set_scale_to_fit (self, max);
434 }
435 
436 #ifdef PHOC_XWAYLAND
437 static const char *atom_map[XWAYLAND_ATOM_LAST] = {
438 	"_NET_WM_WINDOW_TYPE_NORMAL",
439 	"_NET_WM_WINDOW_TYPE_DIALOG"
440 };
441 
442 static
handle_xwayland_ready(struct wl_listener * listener,void * data)443 void handle_xwayland_ready(struct wl_listener *listener, void *data) {
444   PhocDesktop *desktop = wl_container_of (
445         listener, desktop, xwayland_ready);
446   xcb_connection_t *xcb_conn = xcb_connect (NULL, NULL);
447 
448   int err = xcb_connection_has_error (xcb_conn);
449   if (err) {
450     g_warning ("XCB connect failed: %d", err);
451     return;
452   }
453 
454   xcb_intern_atom_cookie_t cookies[XWAYLAND_ATOM_LAST];
455 
456   for (size_t i = 0; i < XWAYLAND_ATOM_LAST; i++)
457     cookies[i] = xcb_intern_atom (xcb_conn, 0, strlen (atom_map[i]), atom_map[i]);
458 
459   for (size_t i = 0; i < XWAYLAND_ATOM_LAST; i++) {
460     xcb_generic_error_t *error = NULL;
461     xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply (xcb_conn, cookies[i], &error);
462 
463     if (error) {
464       g_warning ("could not resolve atom %s, X11 error code %d",
465         atom_map[i], error->error_code);
466       free (error);
467     }
468 
469     if (reply)
470       desktop->xwayland_atoms[i] = reply->atom;
471 
472     free (reply);
473   }
474 
475   xcb_disconnect (xcb_conn);
476 }
477 #endif
478 
479 static void
handle_output_destroy(PhocOutput * destroyed_output)480 handle_output_destroy (PhocOutput *destroyed_output)
481 {
482 	PhocDesktop *self = destroyed_output->desktop;
483 	PhocOutput *output;
484 	char *input_name;
485 	GHashTableIter iter;
486 	g_hash_table_iter_init (&iter, self->input_output_map);
487 	while (g_hash_table_iter_next (&iter, (gpointer) &input_name,
488 				       (gpointer) &output)){
489 		if (destroyed_output == output){
490 			g_debug ("Removing mapping for input device '%s' to output '%s'",
491 				 input_name, output->wlr_output->name);
492 			g_hash_table_remove (self->input_output_map, input_name);
493 			break;
494 		}
495 
496 	}
497 	g_object_unref (destroyed_output);
498 }
499 
500 static void
handle_new_output(struct wl_listener * listener,void * data)501 handle_new_output (struct wl_listener *listener, void *data)
502 {
503 	PhocDesktop *self = wl_container_of (listener, self, new_output);
504 	PhocOutput *output = phoc_output_new (self, (struct wlr_output *)data);
505 
506 	g_signal_connect (output, "output-destroyed",
507 			  G_CALLBACK (handle_output_destroy),
508 			  NULL);
509 }
510 
511 static void
phoc_desktop_constructed(GObject * object)512 phoc_desktop_constructed (GObject *object)
513 {
514   PhocDesktop *self = PHOC_DESKTOP (object);
515   PhocServer *server = phoc_server_get_default ();
516   struct roots_config *config = self->config;
517 
518   G_OBJECT_CLASS (phoc_desktop_parent_class)->constructed (object);
519 
520   wl_list_init(&self->views);
521   wl_list_init(&self->outputs);
522 
523   self->new_output.notify = handle_new_output;
524   wl_signal_add(&server->backend->events.new_output, &self->new_output);
525 
526   self->layout = wlr_output_layout_create();
527   wlr_xdg_output_manager_v1_create(server->wl_display, self->layout);
528   self->layout_change.notify = handle_layout_change;
529   wl_signal_add(&self->layout->events.change, &self->layout_change);
530 
531   self->compositor = wlr_compositor_create(server->wl_display,
532 					   server->renderer);
533 
534   self->xdg_shell = wlr_xdg_shell_create(server->wl_display);
535   wl_signal_add(&self->xdg_shell->events.new_surface,
536 		&self->xdg_shell_surface);
537   self->xdg_shell_surface.notify = handle_xdg_shell_surface;
538 
539   self->layer_shell = wlr_layer_shell_v1_create(server->wl_display);
540   wl_signal_add(&self->layer_shell->events.new_surface,
541 		&self->layer_shell_surface);
542   self->layer_shell_surface.notify = handle_layer_shell_surface;
543 
544   self->tablet_v2 = wlr_tablet_v2_create(server->wl_display);
545 
546   const char *cursor_theme = NULL;
547 #ifdef PHOC_XWAYLAND
548   const char *cursor_default = ROOTS_XCURSOR_DEFAULT;
549 #endif
550   struct roots_cursor_config *cc =
551     roots_config_get_cursor(config, ROOTS_CONFIG_DEFAULT_SEAT_NAME);
552   if (cc != NULL) {
553     cursor_theme = cc->theme;
554 #ifdef PHOC_XWAYLAND
555     if (cc->default_image != NULL) {
556       cursor_default = cc->default_image;
557     }
558 #endif
559   }
560 
561   char cursor_size_fmt[16];
562   snprintf(cursor_size_fmt, sizeof(cursor_size_fmt),
563 	   "%d", ROOTS_XCURSOR_SIZE);
564   setenv("XCURSOR_SIZE", cursor_size_fmt, 1);
565   if (cursor_theme != NULL) {
566     setenv("XCURSOR_THEME", cursor_theme, 1);
567   }
568 
569 #ifdef PHOC_XWAYLAND
570   self->xcursor_manager = wlr_xcursor_manager_create(cursor_theme,
571 						     ROOTS_XCURSOR_SIZE);
572   g_return_if_fail (self->xcursor_manager);
573 
574   if (config->xwayland) {
575     self->xwayland = wlr_xwayland_create(server->wl_display,
576 					 self->compositor, config->xwayland_lazy);
577     wl_signal_add(&self->xwayland->events.new_surface,
578 		  &self->xwayland_surface);
579     self->xwayland_surface.notify = handle_xwayland_surface;
580 
581     wl_signal_add(&self->xwayland->events.ready,
582 		  &self->xwayland_ready);
583     self->xwayland_ready.notify = handle_xwayland_ready;
584 
585     setenv("DISPLAY", self->xwayland->display_name, true);
586 
587 #if (WLR_VERSION_MAJOR > 0 || WLR_VERSION_MINOR > 10)
588     if (!wlr_xcursor_manager_load(self->xcursor_manager, 1)) {
589 #else
590     if (wlr_xcursor_manager_load(self->xcursor_manager, 1)) {
591 #endif
592       wlr_log(WLR_ERROR, "Cannot load XWayland XCursor theme");
593     }
594     struct wlr_xcursor *xcursor = wlr_xcursor_manager_get_xcursor(
595 								  self->xcursor_manager, cursor_default, 1);
596     if (xcursor != NULL) {
597       struct wlr_xcursor_image *image = xcursor->images[0];
598       wlr_xwayland_set_cursor(self->xwayland, image->buffer,
599 			      image->width * 4, image->width, image->height, image->hotspot_x,
600 			      image->hotspot_y);
601     }
602   }
603 #endif
604 
605   self->gamma_control_manager_v1 = wlr_gamma_control_manager_v1_create(server->wl_display);
606   self->export_dmabuf_manager_v1 =
607     wlr_export_dmabuf_manager_v1_create(server->wl_display);
608   self->server_decoration_manager =
609     wlr_server_decoration_manager_create(server->wl_display);
610   wlr_server_decoration_manager_set_default_mode(self->server_decoration_manager,
611 						 WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT);
612   self->idle = wlr_idle_create(server->wl_display);
613   self->idle_inhibit = wlr_idle_inhibit_v1_create(server->wl_display);
614   self->primary_selection_device_manager =
615     wlr_gtk_primary_selection_device_manager_create(server->wl_display);
616   self->input_inhibit =
617     wlr_input_inhibit_manager_create(server->wl_display);
618   self->input_inhibit_activate.notify = input_inhibit_activate;
619   wl_signal_add(&self->input_inhibit->events.activate,
620 		&self->input_inhibit_activate);
621   self->input_inhibit_deactivate.notify = input_inhibit_deactivate;
622   wl_signal_add(&self->input_inhibit->events.deactivate,
623 		&self->input_inhibit_deactivate);
624 
625   self->input_method =
626     wlr_input_method_manager_v2_create(server->wl_display);
627 
628   self->text_input = wlr_text_input_manager_v3_create(server->wl_display);
629 
630   self->gtk_shell = phoc_gtk_shell_create(self, server->wl_display);
631   self->phosh = phosh_create(self, server->wl_display);
632   self->virtual_keyboard = wlr_virtual_keyboard_manager_v1_create(
633 								  server->wl_display);
634   wl_signal_add(&self->virtual_keyboard->events.new_virtual_keyboard,
635 		&self->virtual_keyboard_new);
636   self->virtual_keyboard_new.notify = phoc_handle_virtual_keyboard;
637 
638   self->virtual_pointer = wlr_virtual_pointer_manager_v1_create(server->wl_display);
639   wl_signal_add(&self->virtual_pointer->events.new_virtual_pointer,
640 		&self->virtual_pointer_new);
641   self->virtual_pointer_new.notify = phoc_handle_virtual_pointer;
642 
643   self->screencopy = wlr_screencopy_manager_v1_create(server->wl_display);
644 
645   self->xdg_decoration_manager =
646     wlr_xdg_decoration_manager_v1_create(server->wl_display);
647   wl_signal_add(&self->xdg_decoration_manager->events.new_toplevel_decoration,
648 		&self->xdg_toplevel_decoration);
649   self->xdg_toplevel_decoration.notify = handle_xdg_toplevel_decoration;
650 
651   self->pointer_constraints =
652     wlr_pointer_constraints_v1_create(server->wl_display);
653   self->pointer_constraint.notify = handle_pointer_constraint;
654   wl_signal_add(&self->pointer_constraints->events.new_constraint,
655 		&self->pointer_constraint);
656 
657   self->presentation =
658     wlr_presentation_create(server->wl_display, server->backend);
659   self->foreign_toplevel_manager_v1 =
660     wlr_foreign_toplevel_manager_v1_create(server->wl_display);
661   self->relative_pointer_manager =
662     wlr_relative_pointer_manager_v1_create(server->wl_display);
663   self->pointer_gestures =
664     wlr_pointer_gestures_v1_create(server->wl_display);
665 
666   self->output_manager_v1 =
667     wlr_output_manager_v1_create(server->wl_display);
668   self->output_manager_apply.notify = handle_output_manager_apply;
669   wl_signal_add(&self->output_manager_v1->events.apply,
670 		&self->output_manager_apply);
671   self->output_manager_test.notify = handle_output_manager_test;
672   wl_signal_add(&self->output_manager_v1->events.test,
673 		&self->output_manager_test);
674 
675   self->output_power_manager_v1 =
676     wlr_output_power_manager_v1_create(server->wl_display);
677   self->output_power_manager_set_mode.notify =
678     phoc_output_handle_output_power_manager_set_mode;
679   wl_signal_add(&self->output_power_manager_v1->events.set_mode,
680 		&self->output_power_manager_set_mode);
681 
682   wlr_data_control_manager_v1_create(server->wl_display);
683 
684   self->settings = g_settings_new ("sm.puri.phoc");
685   g_signal_connect_swapped(self->settings, "changed::auto-maximize",
686 			   G_CALLBACK (auto_maximize_changed_cb), self);
687   auto_maximize_changed_cb(self, "auto-maximize", self->settings);
688   g_signal_connect_swapped(self->settings, "changed::scale-to-fit",
689 			   G_CALLBACK (scale_to_fit_changed_cb), self);
690   scale_to_fit_changed_cb(self, "scale-to-fit", self->settings);
691 }
692 
693 
694 static void
695 phoc_desktop_finalize (GObject *object)
696 {
697   PhocDesktop *self = PHOC_DESKTOP (object);
698 
699 #ifdef PHOC_XWAYLAND
700   // We need to shutdown Xwayland before disconnecting all clients, otherwise
701   // wlroots will restart it automatically.
702   g_clear_pointer (&self->xwayland, wlr_xwayland_destroy);
703 #endif
704 
705   g_clear_pointer (&self->phosh, phosh_destroy);
706   g_clear_pointer (&self->gtk_shell, phoc_gtk_shell_destroy);
707 
708   g_hash_table_remove_all (self->input_output_map);
709   g_hash_table_unref (self->input_output_map);
710 
711   G_OBJECT_CLASS (phoc_desktop_parent_class)->finalize (object);
712 }
713 
714 
715 static void
716 phoc_desktop_class_init (PhocDesktopClass *klass)
717 {
718   GObjectClass *object_class = (GObjectClass *)klass;
719 
720   object_class->set_property = phoc_desktop_set_property;
721   object_class->get_property = phoc_desktop_get_property;
722 
723   object_class->constructed = phoc_desktop_constructed;
724   object_class->finalize = phoc_desktop_finalize;
725 
726   props[PROP_CONFIG] =
727     g_param_spec_pointer (
728       "config",
729       "Config",
730       "The config object",
731       G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
732 
733   g_object_class_install_properties (object_class, PROP_LAST_PROP, props);
734 }
735 
736 
737 static void
738 phoc_desktop_init (PhocDesktop *self)
739 {
740   self->input_output_map = g_hash_table_new_full (g_str_hash,
741                                                   g_str_equal,
742                                                   g_free,
743                                                   NULL);
744 }
745 
746 
747 PhocDesktop *
748 phoc_desktop_new (struct roots_config *config)
749 {
750   return g_object_new (PHOC_TYPE_DESKTOP, "config", config, NULL);
751 }
752 
753 
754 /**
755  * phoc_desktop_toggle_output_blank:
756  *
757  * Blank or unblank all outputs depending on the current state
758  */
759 void
760 phoc_desktop_toggle_output_blank (PhocDesktop *self)
761 {
762   PhocOutput *output;
763 
764   wl_list_for_each(output, &self->outputs, link) {
765     gboolean enable = !output->wlr_output->enabled;
766 
767     wlr_output_enable (output->wlr_output, enable);
768     wlr_output_commit (output->wlr_output);
769     if (enable)
770       phoc_output_damage_whole(output);
771   }
772 }
773 
774 /**
775  * phoc_desktop_set_auto_maximize:
776  *
777  * Turn auto maximization of toplevels on (%TRUE) or off (%FALSE)
778  */
779 void
780 phoc_desktop_set_auto_maximize (PhocDesktop *self, gboolean enable)
781 {
782   struct roots_view *view;
783 
784   g_debug ("auto-maximize: %d", enable);
785   self->maximize = enable;
786 
787   /* Disabling auto-maximize leaves all views in their current position */
788   if (!enable) {
789     wl_list_for_each (view, &self->views, link)
790       view_appear_activated (view, phoc_input_view_has_focus (phoc_server_get_default()->input, view));
791     return;
792   }
793 
794   wl_list_for_each (view, &self->views, link) {
795     view_auto_maximize (view);
796     view_appear_activated (view, true);
797   }
798 }
799 
800 gboolean
801 phoc_desktop_get_auto_maximize (PhocDesktop *self)
802 {
803   return self->maximize;
804 }
805 
806 /**
807  * phoc_desktop_set_scale_to_fit:
808  *
809  * Turn auto scaling of all oversized toplevels on (%TRUE) or off (%FALSE)
810  */
811 void
812 phoc_desktop_set_scale_to_fit (PhocDesktop *self, gboolean enable)
813 {
814     g_debug ("scale to fit: %d", enable);
815     self->scale_to_fit = enable;
816 }
817 
818 gboolean
819 phoc_desktop_get_scale_to_fit (PhocDesktop *self)
820 {
821     return self->scale_to_fit;
822 }
823