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