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