1 #define G_LOG_DOMAIN "phoc-output"
2 
3 #include "config.h"
4 
5 #define _POSIX_C_SOURCE 200809L
6 #include <assert.h>
7 #include <stdbool.h>
8 #include <stdlib.h>
9 #include <time.h>
10 #include <wlr/backend/drm.h>
11 #include <wlr/config.h>
12 #include <wlr/types/wlr_compositor.h>
13 #include <wlr/types/wlr_output_layout.h>
14 #include <wlr/types/wlr_output_power_management_v1.h>
15 #include <wlr/types/wlr_xdg_shell.h>
16 #include <wlr/util/log.h>
17 #include <wlr/util/region.h>
18 #include "settings.h"
19 #include "layers.h"
20 #include "output.h"
21 #include "render.h"
22 #include "server.h"
23 #include "utils.h"
24 
25 
26 G_DEFINE_TYPE (PhocOutput, phoc_output, G_TYPE_OBJECT);
27 
28 enum {
29   PROP_0,
30   PROP_DESKTOP,
31   PROP_WLR_OUTPUT,
32   PROP_LAST_PROP
33 };
34 static GParamSpec *props[PROP_LAST_PROP];
35 
36 enum {
37   OUTPUT_DESTROY,
38   N_SIGNALS
39 };
40 static guint signals[N_SIGNALS] = { 0 };
41 
42 struct surface_iterator_data {
43   roots_surface_iterator_func_t user_iterator;
44   void                         *user_data;
45 
46   PhocOutput                   *output;
47   double                        ox, oy;
48   int                           width, height;
49   float                         rotation, scale;
50 };
51 
52 static bool
get_surface_box(struct surface_iterator_data * data,struct wlr_surface * surface,int sx,int sy,struct wlr_box * surface_box)53 get_surface_box (struct surface_iterator_data *data,
54                  struct wlr_surface *surface, int sx, int sy,
55                  struct wlr_box *surface_box)
56 {
57   PhocOutput *self = data->output;
58 
59   if (!wlr_surface_has_buffer (surface)) {
60     return false;
61   }
62 
63   int sw = surface->current.width;
64   int sh = surface->current.height;
65 
66   double _sx = sx + surface->sx;
67   double _sy = sy + surface->sy;
68 
69   phoc_utils_rotate_child_position (&_sx, &_sy, sw, sh, data->width,
70                                     data->height, data->rotation);
71 
72   struct wlr_box box = {
73     .x = data->ox + _sx,
74     .y = data->oy + _sy,
75     .width = sw,
76     .height = sh,
77   };
78 
79   if (surface_box != NULL) {
80     *surface_box = box;
81   }
82 
83   struct wlr_box rotated_box;
84 
85   wlr_box_rotated_bounds (&rotated_box, &box, data->rotation);
86 
87   struct wlr_box output_box = {0};
88 
89   wlr_output_effective_resolution (self->wlr_output,
90                                    &output_box.width, &output_box.height);
91   phoc_output_scale_box (self, &output_box, 1 / data->scale);
92 
93   struct wlr_box intersection;
94 
95   return wlr_box_intersection (&intersection, &output_box, &rotated_box);
96 }
97 
98 
99 static void
phoc_output_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)100 phoc_output_set_property (GObject      *object,
101                           guint         property_id,
102                           const GValue *value,
103                           GParamSpec   *pspec)
104 {
105   PhocOutput *self = PHOC_OUTPUT (object);
106 
107   switch (property_id) {
108   case PROP_DESKTOP:
109     self->desktop = g_value_get_pointer (value);
110     g_object_notify_by_pspec (G_OBJECT (self), props[PROP_DESKTOP]);
111     break;
112   case PROP_WLR_OUTPUT:
113     self->wlr_output = g_value_get_pointer (value);
114     g_object_notify_by_pspec (G_OBJECT (self), props[PROP_WLR_OUTPUT]);
115     break;
116   default:
117     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
118     break;
119   }
120 }
121 
122 static void
phoc_output_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)123 phoc_output_get_property (GObject    *object,
124                           guint       property_id,
125                           GValue     *value,
126                           GParamSpec *pspec)
127 {
128   PhocOutput *self = PHOC_OUTPUT (object);
129 
130   switch (property_id) {
131   case PROP_DESKTOP:
132     g_value_set_pointer (value, self->desktop);
133     break;
134   case PROP_WLR_OUTPUT:
135     g_value_set_pointer (value, self->wlr_output);
136     break;
137   default:
138     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
139     break;
140   }
141 }
142 
143 static void
phoc_output_init(PhocOutput * self)144 phoc_output_init (PhocOutput *self)
145 {
146 }
147 
148 PhocOutput *
phoc_output_new(PhocDesktop * desktop,struct wlr_output * wlr_output)149 phoc_output_new (PhocDesktop *desktop, struct wlr_output *wlr_output)
150 {
151   return g_object_new (PHOC_TYPE_OUTPUT,
152                        "desktop", desktop,
153                        "wlr-output", wlr_output,
154                        NULL);
155 }
156 
157 static void
update_output_manager_config(PhocDesktop * desktop)158 update_output_manager_config (PhocDesktop *desktop)
159 {
160   struct wlr_output_configuration_v1 *config =
161     wlr_output_configuration_v1_create ();
162 
163   PhocOutput *output;
164 
165   wl_list_for_each (output, &desktop->outputs, link) {
166     struct wlr_output_configuration_head_v1 *config_head =
167       wlr_output_configuration_head_v1_create (config, output->wlr_output);
168     struct wlr_box *output_box = wlr_output_layout_get_box (
169       output->desktop->layout, output->wlr_output);
170     if (output_box) {
171       config_head->state.x = output_box->x;
172       config_head->state.y = output_box->y;
173     }
174   }
175 
176   wlr_output_manager_v1_set_configuration (desktop->output_manager_v1, config);
177 }
178 
179 static void
phoc_output_handle_destroy(struct wl_listener * listener,void * data)180 phoc_output_handle_destroy (struct wl_listener *listener, void *data)
181 {
182   PhocOutput *self = wl_container_of (listener, self, output_destroy);
183 
184   update_output_manager_config (self->desktop);
185 
186   g_signal_emit (self, signals[OUTPUT_DESTROY], 0);
187 }
188 
189 static void
phoc_output_handle_enable(struct wl_listener * listener,void * data)190 phoc_output_handle_enable (struct wl_listener *listener, void *data)
191 {
192   PhocOutput *self = wl_container_of (listener, self, enable);
193 
194   update_output_manager_config (self->desktop);
195 }
196 
197 static void
phoc_output_damage_handle_frame(struct wl_listener * listener,void * data)198 phoc_output_damage_handle_frame (struct wl_listener *listener,
199                                  void               *data)
200 {
201   PhocOutput *self = wl_container_of (listener, self, damage_frame);
202 
203   output_render (self);
204 }
205 
206 static void
phoc_output_damage_handle_destroy(struct wl_listener * listener,void * data)207 phoc_output_damage_handle_destroy (struct wl_listener *listener,
208                                    void               *data)
209 {
210   PhocOutput *self = wl_container_of (listener, self, damage_destroy);
211 
212   g_signal_emit (self, signals[OUTPUT_DESTROY], 0);
213 }
214 
215 static void
phoc_output_handle_mode(struct wl_listener * listener,void * data)216 phoc_output_handle_mode (struct wl_listener *listener, void *data)
217 {
218   PhocOutput *self = wl_container_of (listener, self, mode);
219 
220   arrange_layers (self);
221   update_output_manager_config (self->desktop);
222 }
223 
224 static void
phoc_output_handle_transform(struct wl_listener * listener,void * data)225 phoc_output_handle_transform (struct wl_listener *listener, void *data)
226 {
227   PhocOutput *self = wl_container_of (listener, self, transform);
228 
229   arrange_layers (self);
230 }
231 
232 static void
phoc_output_set_mode(struct wlr_output * output,struct roots_output_config * oc)233 phoc_output_set_mode (struct wlr_output *output, struct roots_output_config *oc)
234 {
235   int mhz = (int)(oc->mode.refresh_rate * 1000);
236 
237   if (wl_list_empty (&output->modes)) {
238     // Output has no mode, try setting a custom one
239     wlr_output_set_custom_mode (output, oc->mode.width,
240                                 oc->mode.height, mhz);
241     return;
242   }
243 
244   struct wlr_output_mode *mode, *best = NULL;
245 
246   wl_list_for_each (mode, &output->modes, link) {
247     if (mode->width == oc->mode.width && mode->height == oc->mode.height) {
248       if (mode->refresh == mhz) {
249         best = mode;
250         break;
251       }
252       best = mode;
253     }
254   }
255   if (!best) {
256     wlr_log (WLR_ERROR, "Configured mode for %s not available",
257              output->name);
258   } else {
259     wlr_log (WLR_DEBUG, "Assigning configured mode to %s",
260              output->name);
261     wlr_output_set_mode (output, best);
262   }
263 }
264 
265 static void
phoc_output_constructed(GObject * object)266 phoc_output_constructed (GObject *object)
267 {
268   PhocOutput *self = PHOC_OUTPUT (object);
269   PhocServer *server = phoc_server_get_default ();
270   PhocInput *input = server->input;
271 
272   g_debug ("Initializing roots output");
273   assert (server->desktop);
274 
275   struct roots_config *config = self->desktop->config;
276 
277   wlr_log (WLR_DEBUG, "Output '%s' added", self->wlr_output->name);
278   wlr_log (WLR_DEBUG, "'%s %s %s' %" PRId32 "mm x %" PRId32 "mm",
279            self->wlr_output->make, self->wlr_output->model,
280            self->wlr_output->serial, self->wlr_output->phys_width,
281            self->wlr_output->phys_height);
282 
283   clock_gettime (CLOCK_MONOTONIC, &self->last_frame);
284   self->wlr_output->data = self;
285   wl_list_insert (&self->desktop->outputs, &self->link);
286 
287   self->damage = wlr_output_damage_create (self->wlr_output);
288 
289   self->debug_touch_points = NULL;
290 
291   self->output_destroy.notify = phoc_output_handle_destroy;
292   wl_signal_add (&self->wlr_output->events.destroy, &self->output_destroy);
293   self->enable.notify = phoc_output_handle_enable;
294   wl_signal_add (&self->wlr_output->events.enable, &self->enable);
295   self->mode.notify = phoc_output_handle_mode;
296   wl_signal_add (&self->wlr_output->events.mode, &self->mode);
297   self->transform.notify = phoc_output_handle_transform;
298   wl_signal_add (&self->wlr_output->events.transform, &self->transform);
299 
300   self->damage_frame.notify = phoc_output_damage_handle_frame;
301   wl_signal_add (&self->damage->events.frame, &self->damage_frame);
302   self->damage_destroy.notify = phoc_output_damage_handle_destroy;
303   wl_signal_add (&self->damage->events.destroy, &self->damage_destroy);
304 
305   size_t len = sizeof(self->layers) / sizeof(self->layers[0]);
306 
307   for (size_t i = 0; i < len; ++i) {
308     wl_list_init (&self->layers[i]);
309   }
310 
311   struct roots_output_config *output_config =
312     roots_config_get_output (config, self->wlr_output);
313 
314   struct wlr_output_mode *preferred_mode =
315     wlr_output_preferred_mode (self->wlr_output);
316 
317   if (output_config) {
318     if (output_config->enable) {
319       if (wlr_output_is_drm (self->wlr_output)) {
320         struct roots_output_mode_config *mode_config;
321         wl_list_for_each (mode_config, &output_config->modes, link) {
322           wlr_drm_connector_add_mode (self->wlr_output, &mode_config->info);
323         }
324       } else if (!wl_list_empty (&output_config->modes)) {
325         wlr_log (WLR_ERROR, "Can only add modes for DRM backend");
326       }
327 
328       if (output_config->mode.width) {
329         phoc_output_set_mode (self->wlr_output, output_config);
330       } else if (preferred_mode != NULL) {
331         wlr_output_set_mode (self->wlr_output, preferred_mode);
332       }
333 
334       wlr_output_set_scale (self->wlr_output, output_config->scale);
335       wlr_output_set_transform (self->wlr_output, output_config->transform);
336       wlr_output_layout_add (self->desktop->layout, self->wlr_output,
337                              output_config->x, output_config->y);
338     } else {
339       wlr_output_enable (self->wlr_output, false);
340     }
341   } else {
342     if (preferred_mode != NULL) {
343       wlr_output_set_mode (self->wlr_output, preferred_mode);
344     }
345     wlr_output_enable (self->wlr_output, true);
346     wlr_output_layout_add_auto (self->desktop->layout, self->wlr_output);
347   }
348   wlr_output_commit (self->wlr_output);
349 
350   struct roots_seat *seat;
351 
352   wl_list_for_each (seat, &input->seats, link) {
353     roots_seat_configure_cursor (seat);
354     roots_seat_configure_xcursor (seat);
355   }
356 
357   arrange_layers (self);
358   phoc_output_damage_whole (self);
359 
360   update_output_manager_config (self->desktop);
361 
362   G_OBJECT_CLASS (phoc_output_parent_class)->constructed (object);
363 
364 }
365 
366 static void
phoc_output_finalize(GObject * object)367 phoc_output_finalize (GObject *object)
368 {
369   PhocOutput *self = PHOC_OUTPUT (object);
370 
371   wl_list_remove (&self->link);
372   wl_list_remove (&self->output_destroy.link);
373   wl_list_remove (&self->enable.link);
374   wl_list_remove (&self->mode.link);
375   wl_list_remove (&self->transform.link);
376   wl_list_remove (&self->damage_frame.link);
377   wl_list_remove (&self->damage_destroy.link);
378   g_list_free_full (self->debug_touch_points, g_free);
379 
380   size_t len = sizeof (self->layers) / sizeof (self->layers[0]);
381   for (size_t i = 0; i < len; ++i) {
382     wl_list_remove (&self->layers[i]);
383   }
384 
385   G_OBJECT_CLASS (phoc_output_parent_class)->finalize (object);
386 }
387 
388 static void
phoc_output_dispose(GObject * object)389 phoc_output_dispose (GObject *object)
390 {
391   G_OBJECT_CLASS (phoc_output_parent_class)->dispose (object);
392 }
393 
394 static void
phoc_output_class_init(PhocOutputClass * klass)395 phoc_output_class_init (PhocOutputClass *klass)
396 {
397   GObjectClass *object_class = (GObjectClass *)klass;
398 
399   object_class->set_property = phoc_output_set_property;
400   object_class->get_property = phoc_output_get_property;
401 
402   object_class->constructed = phoc_output_constructed;
403   object_class->dispose = phoc_output_dispose;
404   object_class->finalize = phoc_output_finalize;
405 
406   props[PROP_DESKTOP] =
407     g_param_spec_pointer (
408       "desktop",
409       "Desktop",
410       "The desktop object",
411       G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
412   props[PROP_WLR_OUTPUT] =
413     g_param_spec_pointer (
414       "wlr-output",
415       "wlr-output",
416       "The wlroots output object",
417       G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
418   g_object_class_install_properties (object_class, PROP_LAST_PROP, props);
419 
420   signals[OUTPUT_DESTROY] = g_signal_new ("output-destroyed",
421                                           G_TYPE_OBJECT,
422                                           G_SIGNAL_RUN_LAST,
423                                           0, NULL, NULL, NULL, G_TYPE_NONE, 0);
424 
425 }
426 
427 static void
phoc_output_for_each_surface_iterator(struct wlr_surface * surface,int sx,int sy,void * _data)428 phoc_output_for_each_surface_iterator (struct wlr_surface *surface,
429                                        int sx, int sy, void *_data)
430 {
431   struct surface_iterator_data *data = _data;
432 
433   struct wlr_box box;
434   bool intersects = get_surface_box (data, surface, sx, sy, &box);
435 
436   if (!intersects) {
437     return;
438   }
439 
440   data->user_iterator (data->output, surface, &box, data->rotation,
441                        data->scale, data->user_data);
442 }
443 
444 void
phoc_output_surface_for_each_surface(PhocOutput * self,struct wlr_surface * surface,double ox,double oy,roots_surface_iterator_func_t iterator,void * user_data)445 phoc_output_surface_for_each_surface (PhocOutput *self, struct wlr_surface
446                                       *surface, double ox, double oy,
447                                       roots_surface_iterator_func_t iterator,
448                                       void *user_data)
449 {
450   struct surface_iterator_data data = {
451     .user_iterator = iterator,
452     .user_data = user_data,
453     .output = self,
454     .ox = ox,
455     .oy = oy,
456     .width = surface->current.width,
457     .height = surface->current.height,
458     .rotation = 0,
459     .scale = 1.0
460   };
461 
462   wlr_surface_for_each_surface (surface,
463                                 phoc_output_for_each_surface_iterator, &data);
464 }
465 
466 void
phoc_output_xdg_surface_for_each_surface(PhocOutput * self,struct wlr_xdg_surface * xdg_surface,double ox,double oy,roots_surface_iterator_func_t iterator,void * user_data)467 phoc_output_xdg_surface_for_each_surface (PhocOutput *self, struct
468                                           wlr_xdg_surface *xdg_surface, double
469                                           ox, double oy,
470                                           roots_surface_iterator_func_t
471                                           iterator, void *user_data)
472 {
473   struct surface_iterator_data data = {
474     .user_iterator = iterator,
475     .user_data = user_data,
476     .output = self,
477     .ox = ox,
478     .oy = oy,
479     .width = xdg_surface->surface->current.width,
480     .height = xdg_surface->surface->current.height,
481     .rotation = 0,
482     .scale = 1.0
483   };
484 
485   wlr_xdg_surface_for_each_surface (xdg_surface,
486                                     phoc_output_for_each_surface_iterator, &data);
487 }
488 
489 void
phoc_output_view_for_each_surface(PhocOutput * self,struct roots_view * view,roots_surface_iterator_func_t iterator,void * user_data)490 phoc_output_view_for_each_surface (PhocOutput *self, struct roots_view *view,
491                                    roots_surface_iterator_func_t iterator, void
492                                    *user_data)
493 {
494   struct wlr_box *output_box =
495     wlr_output_layout_get_box (self->desktop->layout, self->wlr_output);
496 
497   if (!output_box) {
498     return;
499   }
500 
501   struct surface_iterator_data data = {
502     .user_iterator = iterator,
503     .user_data = user_data,
504     .output = self,
505     .ox = view->box.x - output_box->x,
506     .oy = view->box.y - output_box->y,
507     .width = view->box.width,
508     .height = view->box.height,
509     .rotation = 0,
510     .scale = view->scale
511   };
512 
513   view_for_each_surface (view, phoc_output_for_each_surface_iterator, &data);
514 }
515 
516 #ifdef PHOC_XWAYLAND
517 void
phoc_output_xwayland_children_for_each_surface(PhocOutput * self,struct wlr_xwayland_surface * surface,roots_surface_iterator_func_t iterator,void * user_data)518 phoc_output_xwayland_children_for_each_surface (PhocOutput *self, struct
519                                                 wlr_xwayland_surface *surface,
520                                                 roots_surface_iterator_func_t
521                                                 iterator, void *user_data)
522 {
523   struct wlr_box *output_box =
524     wlr_output_layout_get_box (self->desktop->layout, self->wlr_output);
525 
526   if (!output_box) {
527     return;
528   }
529 
530   struct wlr_xwayland_surface *child;
531 
532   wl_list_for_each (child, &surface->children, parent_link) {
533     if (child->mapped) {
534       double ox = child->x - output_box->x;
535       double oy = child->y - output_box->y;
536       phoc_output_surface_for_each_surface (self, child->surface, ox, oy, iterator,
537                                             user_data);
538     }
539     phoc_output_xwayland_children_for_each_surface (self, child,
540                                                     iterator, user_data);
541   }
542 }
543 #endif
544 
545 static void
phoc_output_layer_handle_surface(PhocOutput * self,struct roots_layer_surface * layer_surface,roots_surface_iterator_func_t iterator,void * user_data)546 phoc_output_layer_handle_surface (PhocOutput *self, struct roots_layer_surface *layer_surface,
547                                   roots_surface_iterator_func_t iterator, void
548                                   *user_data)
549 {
550   struct wlr_layer_surface_v1 *wlr_layer_surface_v1 =
551     layer_surface->layer_surface;
552 
553   phoc_output_surface_for_each_surface (self, wlr_layer_surface_v1->surface,
554                                         layer_surface->geo.x,
555                                         layer_surface->geo.y, iterator,
556                                         user_data);
557 
558   struct wlr_xdg_popup *state;
559 
560   wl_list_for_each (state, &wlr_layer_surface_v1->popups, link) {
561     struct wlr_xdg_surface *popup = state->base;
562     if (!popup->configured) {
563       continue;
564     }
565 
566     double popup_sx, popup_sy;
567     popup_sx = layer_surface->geo.x;
568     popup_sx += popup->popup->geometry.x - popup->geometry.x;
569     popup_sy = layer_surface->geo.y;
570     popup_sy += popup->popup->geometry.y - popup->geometry.y;
571 
572     phoc_output_xdg_surface_for_each_surface (self, popup,
573                                               popup_sx, popup_sy, iterator, user_data);
574   }
575 }
576 
577 void
phoc_output_layer_for_each_surface(PhocOutput * self,struct wl_list * layer_surfaces,roots_surface_iterator_func_t iterator,void * user_data)578 phoc_output_layer_for_each_surface (PhocOutput                   *self,
579                                     struct wl_list               *layer_surfaces,
580                                     roots_surface_iterator_func_t iterator,
581                                     void                         *user_data)
582 {
583   struct roots_layer_surface *layer_surface;
584 
585   wl_list_for_each_reverse (layer_surface, layer_surfaces, link)
586   {
587     if (layer_surface->layer_surface->current.exclusive_zone <= 0) {
588       phoc_output_layer_handle_surface (self, layer_surface, iterator, user_data);
589     }
590   }
591   wl_list_for_each (layer_surface, layer_surfaces, link) {
592     if (layer_surface->layer_surface->current.exclusive_zone > 0) {
593       phoc_output_layer_handle_surface (self, layer_surface, iterator, user_data);
594     }
595   }
596 }
597 
598 void
phoc_output_drag_icons_for_each_surface(PhocOutput * self,PhocInput * input,roots_surface_iterator_func_t iterator,void * user_data)599 phoc_output_drag_icons_for_each_surface (PhocOutput *self, PhocInput *input,
600                                          roots_surface_iterator_func_t
601                                          iterator, void *user_data)
602 {
603   struct wlr_box *output_box =
604     wlr_output_layout_get_box (self->desktop->layout, self->wlr_output);
605 
606   if (!output_box) {
607     return;
608   }
609 
610   struct roots_seat *seat;
611 
612   wl_list_for_each (seat, &input->seats, link) {
613     struct roots_drag_icon *drag_icon = seat->drag_icon;
614     if (!drag_icon || !drag_icon->wlr_drag_icon->mapped) {
615       continue;
616     }
617 
618     double ox = drag_icon->x - output_box->x;
619     double oy = drag_icon->y - output_box->y;
620     phoc_output_surface_for_each_surface (self, drag_icon->wlr_drag_icon->surface,
621                                           ox, oy, iterator, user_data);
622   }
623 }
624 
625 void
phoc_output_for_each_surface(PhocOutput * self,roots_surface_iterator_func_t iterator,void * user_data,gboolean visible_only)626 phoc_output_for_each_surface (PhocOutput *self, roots_surface_iterator_func_t iterator, void
627                               *user_data, gboolean visible_only)
628 {
629   PhocDesktop *desktop = self->desktop;
630   PhocServer *server = phoc_server_get_default ();
631 
632   if (self->fullscreen_view != NULL) {
633     struct roots_view *view = self->fullscreen_view;
634 
635     phoc_output_view_for_each_surface (self, view, iterator, user_data);
636 
637 #ifdef PHOC_XWAYLAND
638     if (view->type == ROOTS_XWAYLAND_VIEW) {
639       struct roots_xwayland_surface *xwayland_surface =
640         roots_xwayland_surface_from_view (view);
641       phoc_output_xwayland_children_for_each_surface (self,
642                                                       xwayland_surface->xwayland_surface,
643                                                       iterator, user_data);
644     }
645 #endif
646   } else {
647     struct roots_view *view;
648     wl_list_for_each_reverse (view, &desktop->views, link)
649     {
650       if (!visible_only || phoc_desktop_view_is_visible (desktop, view)) {
651         phoc_output_view_for_each_surface (self, view, iterator, user_data);
652       }
653     }
654   }
655 
656   phoc_output_drag_icons_for_each_surface (self, server->input,
657                                            iterator, user_data);
658 
659   size_t len = sizeof(self->layers) / sizeof(self->layers[0]);
660   for (size_t i = 0; i < len; ++i) {
661     phoc_output_layer_for_each_surface (self, &self->layers[i],
662                                         iterator, user_data);
663   }
664 }
665 
666 static int
scale_length(int length,int offset,float scale)667 scale_length (int length, int offset, float scale)
668 {
669   return round ((offset + length) * scale) - round (offset * scale);
670 }
671 
672 void
phoc_output_scale_box(PhocOutput * self,struct wlr_box * box,float scale)673 phoc_output_scale_box (PhocOutput *self, struct wlr_box *box, float scale)
674 {
675   box->width = scale_length (box->width, box->x, scale);
676   box->height = scale_length (box->height, box->y, scale);
677   box->x = round (box->x * scale);
678   box->y = round (box->y * scale);
679 }
680 
681 void
phoc_output_get_decoration_box(PhocOutput * self,struct roots_view * view,struct wlr_box * box)682 phoc_output_get_decoration_box (PhocOutput *self, struct roots_view *view,
683                                 struct wlr_box *box)
684 {
685   struct wlr_box deco_box;
686 
687   view_get_deco_box (view, &deco_box);
688 
689   double x = deco_box.x;
690   double y = deco_box.y;
691 
692   wlr_output_layout_output_coords (self->desktop->layout,
693                                    self->wlr_output, &x, &y);
694 
695   box->x = x * self->wlr_output->scale;
696   box->y = y * self->wlr_output->scale;
697   box->width = deco_box.width * self->wlr_output->scale;
698   box->height = deco_box.height * self->wlr_output->scale;
699 }
700 
701 void
phoc_output_damage_whole(PhocOutput * self)702 phoc_output_damage_whole (PhocOutput *self)
703 {
704   wlr_output_damage_add_whole (self->damage);
705 }
706 
707 static bool
phoc_view_accept_damage(PhocOutput * self,struct roots_view * view)708 phoc_view_accept_damage (PhocOutput *self, struct roots_view   *view)
709 {
710   PhocServer *server = phoc_server_get_default ();
711   if (view->wlr_surface == NULL) {
712     return false;
713   }
714   if (!phoc_desktop_view_is_visible (server->desktop, view)) {
715     return false;
716   }
717   if (self->fullscreen_view == NULL) {
718     return true;
719   }
720   if (self->fullscreen_view == view) {
721     return true;
722   }
723 #ifdef PHOC_XWAYLAND
724   if (self->fullscreen_view->type == ROOTS_XWAYLAND_VIEW &&
725       view->type == ROOTS_XWAYLAND_VIEW) {
726     // Special case: accept damage from children
727     struct wlr_xwayland_surface *xsurface =
728       roots_xwayland_surface_from_view (view)->xwayland_surface;
729     struct wlr_xwayland_surface *fullscreen_xsurface =
730       roots_xwayland_surface_from_view (self->fullscreen_view)->xwayland_surface;
731     while (xsurface != NULL) {
732       if (fullscreen_xsurface == xsurface) {
733         return true;
734       }
735       xsurface = xsurface->parent;
736     }
737   }
738 #endif
739   return false;
740 }
741 
742 static void
damage_surface_iterator(PhocOutput * self,struct wlr_surface * surface,struct wlr_box * _box,float rotation,float scale,void * data)743 damage_surface_iterator (PhocOutput *self, struct wlr_surface *surface, struct
744                          wlr_box *_box, float rotation, float scale, void *data)
745 {
746   bool *whole = data;
747 
748   struct wlr_box box = *_box;
749 
750   phoc_output_scale_box (self, &box, scale);
751   phoc_output_scale_box (self, &box, self->wlr_output->scale);
752 
753   int center_x = box.x + box.width/2;
754   int center_y = box.y + box.height/2;
755 
756   if (pixman_region32_not_empty (&surface->buffer_damage)) {
757     pixman_region32_t damage;
758     pixman_region32_init (&damage);
759     wlr_surface_get_effective_damage (surface, &damage);
760     wlr_region_scale (&damage, &damage, scale);
761     wlr_region_scale (&damage, &damage, self->wlr_output->scale);
762     if (ceil (self->wlr_output->scale) > surface->current.scale) {
763       // When scaling up a surface, it'll become blurry so we need to
764       // expand the damage region
765       wlr_region_expand (&damage, &damage,
766                          ceil (self->wlr_output->scale) - surface->current.scale);
767     }
768     pixman_region32_translate (&damage, box.x, box.y);
769     wlr_region_rotated_bounds (&damage, &damage, rotation,
770                                center_x, center_y);
771     wlr_output_damage_add (self->damage, &damage);
772     pixman_region32_fini (&damage);
773   }
774 
775   if (*whole) {
776     wlr_box_rotated_bounds (&box, &box, rotation);
777     wlr_output_damage_add_box (self->damage, &box);
778   }
779 
780   wlr_output_schedule_frame (self->wlr_output);
781 }
782 
783 void
phoc_output_damage_whole_local_surface(PhocOutput * self,struct wlr_surface * surface,double ox,double oy)784 phoc_output_damage_whole_local_surface (PhocOutput *self, struct wlr_surface *surface, double ox,
785                                         double oy)
786 {
787   bool whole = true;
788 
789   phoc_output_surface_for_each_surface (self, surface, ox, oy,
790                                         damage_surface_iterator, &whole);
791 }
792 
793 static void
damage_whole_decoration(PhocOutput * self,struct roots_view * view)794 damage_whole_decoration (PhocOutput *self, struct roots_view   *view)
795 {
796   if (!view->decorated || view->wlr_surface == NULL) {
797     return;
798   }
799 
800   struct wlr_box box;
801 
802   phoc_output_get_decoration_box (self, view, &box);
803 
804   wlr_output_damage_add_box (self->damage, &box);
805 }
806 
807 void
phoc_output_damage_whole_view(PhocOutput * self,struct roots_view * view)808 phoc_output_damage_whole_view (PhocOutput *self, struct roots_view   *view)
809 {
810   if (!phoc_view_accept_damage (self, view)) {
811     return;
812   }
813 
814   damage_whole_decoration (self, view);
815 
816   bool whole = true;
817 
818   phoc_output_view_for_each_surface (self, view, damage_surface_iterator, &whole);
819 }
820 
821 
822 void
phoc_output_damage_from_view(PhocOutput * self,struct roots_view * view)823 phoc_output_damage_from_view (PhocOutput *self, struct roots_view *view)
824 {
825   if (!phoc_view_accept_damage (self, view)) {
826     return;
827   }
828 
829   bool whole = false;
830 
831   phoc_output_view_for_each_surface (self, view, damage_surface_iterator, &whole);
832 
833 }
834 
835 void
phoc_output_damage_whole_drag_icon(PhocOutput * self,struct roots_drag_icon * icon)836 phoc_output_damage_whole_drag_icon (PhocOutput *self, struct roots_drag_icon
837                                     *icon)
838 {
839   bool whole = true;
840 
841   phoc_output_surface_for_each_surface (self, icon->wlr_drag_icon->surface,
842                                         icon->x, icon->y,
843                                         damage_surface_iterator, &whole);
844 }
845 
846 void
phoc_output_damage_from_local_surface(PhocOutput * self,struct wlr_surface * surface,double ox,double oy)847 phoc_output_damage_from_local_surface (PhocOutput *self, struct wlr_surface
848                                        *surface, double ox, double oy)
849 {
850   bool whole = false;
851 
852   phoc_output_surface_for_each_surface (self, surface, ox, oy,
853                                         damage_surface_iterator, &whole);
854 }
855 
856 void
handle_output_manager_apply(struct wl_listener * listener,void * data)857 handle_output_manager_apply (struct wl_listener *listener, void *data)
858 {
859   PhocDesktop *desktop =
860     wl_container_of (listener, desktop, output_manager_apply);
861   struct wlr_output_configuration_v1 *config = data;
862 
863   bool ok = true;
864   struct wlr_output_configuration_head_v1 *config_head;
865 
866   // First disable outputs we need to disable
867   wl_list_for_each (config_head, &config->heads, link) {
868     struct wlr_output *wlr_output = config_head->state.output;
869 
870     if (config_head->state.enabled)
871       continue;
872 
873     if (!wlr_output->enabled)
874       continue;
875 
876     wlr_output_enable (wlr_output, false);
877     wlr_output_layout_remove (desktop->layout, wlr_output);
878     ok &= wlr_output_commit (wlr_output);
879   }
880 
881   // Then enable outputs that need to
882   wl_list_for_each (config_head, &config->heads, link) {
883     struct wlr_output *wlr_output = config_head->state.output;
884     PhocOutput *output = wlr_output->data;
885 
886     if (!config_head->state.enabled)
887       continue;
888 
889     wlr_output_enable (wlr_output, true);
890     if (config_head->state.mode != NULL) {
891       wlr_output_set_mode (wlr_output, config_head->state.mode);
892     } else {
893       wlr_output_set_custom_mode (wlr_output,
894                                   config_head->state.custom_mode.width,
895                                   config_head->state.custom_mode.height,
896                                   config_head->state.custom_mode.refresh);
897     }
898     wlr_output_layout_add (desktop->layout, wlr_output,
899                            config_head->state.x, config_head->state.y);
900     wlr_output_set_transform (wlr_output, config_head->state.transform);
901     wlr_output_set_scale (wlr_output, config_head->state.scale);
902     ok &= wlr_output_commit (wlr_output);
903     if (output->fullscreen_view) {
904       view_set_fullscreen (output->fullscreen_view, true, wlr_output);
905     }
906   }
907 
908   if (ok) {
909     wlr_output_configuration_v1_send_succeeded (config);
910   } else {
911     wlr_output_configuration_v1_send_failed (config);
912   }
913   wlr_output_configuration_v1_destroy (config);
914 
915   update_output_manager_config (desktop);
916 }
917 
918 void
handle_output_manager_test(struct wl_listener * listener,void * data)919 handle_output_manager_test (struct wl_listener *listener, void *data)
920 {
921   PhocDesktop *desktop =
922     wl_container_of (listener, desktop, output_manager_test);
923   struct wlr_output_configuration_v1 *config = data;
924 
925   // TODO: implement test-only mode
926   wlr_output_configuration_v1_send_succeeded (config);
927   wlr_output_configuration_v1_destroy (config);
928 }
929 
930 void
phoc_output_handle_output_power_manager_set_mode(struct wl_listener * listener,void * data)931 phoc_output_handle_output_power_manager_set_mode (struct wl_listener *listener, void *data)
932 {
933   struct wlr_output_power_v1_set_mode_event *event = data;
934   PhocOutput *self;
935   bool enable = true;
936 
937   g_return_if_fail (event && event->output && event->output->data);
938   self = event->output->data;
939   g_debug ("Request to set output power mode of %p to %d",
940            self, event->mode);
941   switch (event->mode) {
942   case ZWLR_OUTPUT_POWER_V1_MODE_OFF:
943     enable = false;
944     break;
945   case ZWLR_OUTPUT_POWER_V1_MODE_ON:
946     enable = true;
947     break;
948   default:
949     g_warning ("Unhandled power state %d for %p", event->mode, self);
950     return;
951   }
952 
953   if (enable == self->wlr_output->enabled)
954     return;
955 
956   wlr_output_enable (self->wlr_output, enable);
957   if (!wlr_output_commit (self->wlr_output)) {
958     g_warning ("Failed to commit power mode change to %d for %p", enable, self);
959     return;
960   }
961 
962   if (enable)
963     phoc_output_damage_whole (self);
964 }
965 
966 /**
967  * phoc_output_is_builtin:
968  *
969  * Returns: %TRUE if the output a built in panel (e.g. laptop panel or
970  * phone LCD), %FALSE otherwise.
971  */
972 gboolean
phoc_output_is_builtin(PhocOutput * output)973 phoc_output_is_builtin (PhocOutput *output)
974 {
975   g_return_val_if_fail (output, FALSE);
976   g_return_val_if_fail (output->wlr_output, FALSE);
977   g_return_val_if_fail (output->wlr_output->name, FALSE);
978 
979   if (g_str_has_prefix (output->wlr_output->name, "LVDS-"))
980     return TRUE;
981   else if (g_str_has_prefix (output->wlr_output->name, "eDP-"))
982     return TRUE;
983   else if (g_str_has_prefix (output->wlr_output->name, "DSI-"))
984     return TRUE;
985 
986   return FALSE;
987 }
988