blob: 17b25a3ce559ff41e62dfb0f0e9c388a026236b8 [file] [log] [blame]
Jason Ekstrand946a9482014-04-02 19:53:47 -05001/*
2 * Copyright © 2013 Jason Ekstrand
3 *
Bryce Harringtonaf637c22015-06-11 12:55:55 -07004 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files (the
6 * "Software"), to deal in the Software without restriction, including
7 * without limitation the rights to use, copy, modify, merge, publish,
8 * distribute, sublicense, and/or sell copies of the Software, and to
9 * permit persons to whom the Software is furnished to do so, subject to
10 * the following conditions:
Jason Ekstrand946a9482014-04-02 19:53:47 -050011 *
Bryce Harringtonaf637c22015-06-11 12:55:55 -070012 * The above copyright notice and this permission notice (including the
13 * next paragraph) shall be included in all copies or substantial
14 * portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 * SOFTWARE.
Jason Ekstrand946a9482014-04-02 19:53:47 -050024 */
25
26#include "config.h"
27
28#include <sys/wait.h>
29#include <unistd.h>
30#include <stdlib.h>
Jussi Kukkonen649bbce2016-07-19 14:16:27 +030031#include <stdint.h>
Jason Ekstrand946a9482014-04-02 19:53:47 -050032#include <stdio.h>
33#include <string.h>
34#include <assert.h>
35
Pekka Paalanen3d5d9472019-03-28 16:28:47 +020036#include <libweston/libweston.h>
Quentin Glidic3d7ca3b2016-12-02 14:20:35 +010037#include "compositor/weston.h"
Jonas Ådahl496adb32015-11-17 16:00:27 +080038#include "fullscreen-shell-unstable-v1-server-protocol.h"
Jon Cruz867d50e2015-06-15 15:37:10 -070039#include "shared/helpers.h"
Jason Ekstrand946a9482014-04-02 19:53:47 -050040
41struct fullscreen_shell {
42 struct wl_client *client;
43 struct wl_listener client_destroyed;
44 struct weston_compositor *compositor;
Pekka Paalanen6ffbba32019-11-06 12:59:32 +020045 /* XXX: missing compositor destroy listener
46 * https://gitlab.freedesktop.org/wayland/weston/issues/299
47 */
Jason Ekstrand946a9482014-04-02 19:53:47 -050048
49 struct weston_layer layer;
50 struct wl_list output_list;
51 struct wl_listener output_created_listener;
52
53 struct wl_listener seat_created_listener;
Armin Krezovićccfd0292016-08-11 15:49:59 +020054
55 /* List of one surface per client, presented for the NULL output
56 *
57 * This is implemented as a list in case someone fixes the shell
58 * implementation to support more than one client.
59 */
60 struct wl_list default_surface_list; /* struct fs_client_surface::link */
Jason Ekstrand946a9482014-04-02 19:53:47 -050061};
62
63struct fs_output {
64 struct fullscreen_shell *shell;
65 struct wl_list link;
66
67 struct weston_output *output;
68 struct wl_listener output_destroyed;
69
70 struct {
71 struct weston_surface *surface;
72 struct wl_listener surface_destroyed;
73 struct wl_resource *mode_feedback;
74
75 int presented_for_mode;
Jonas Ådahl496adb32015-11-17 16:00:27 +080076 enum zwp_fullscreen_shell_v1_present_method method;
Jason Ekstrand946a9482014-04-02 19:53:47 -050077 int32_t framerate;
78 } pending;
79
80 struct weston_surface *surface;
81 struct wl_listener surface_destroyed;
82 struct weston_view *view;
83 struct weston_view *black_view;
84 struct weston_transform transform; /* matrix from x, y */
85
86 int presented_for_mode;
Jonas Ådahl496adb32015-11-17 16:00:27 +080087 enum zwp_fullscreen_shell_v1_present_method method;
Jason Ekstrand946a9482014-04-02 19:53:47 -050088 uint32_t framerate;
89};
90
91struct pointer_focus_listener {
92 struct fullscreen_shell *shell;
93 struct wl_listener pointer_focus;
94 struct wl_listener seat_caps;
95 struct wl_listener seat_destroyed;
96};
97
Armin Krezovićccfd0292016-08-11 15:49:59 +020098struct fs_client_surface {
99 struct weston_surface *surface;
100 enum zwp_fullscreen_shell_v1_present_method method;
101 struct wl_list link; /* struct fullscreen_shell::default_surface_list */
102 struct wl_listener surface_destroyed;
103};
104
105static void
106remove_default_surface(struct fs_client_surface *surf)
107{
108 wl_list_remove(&surf->surface_destroyed.link);
109 wl_list_remove(&surf->link);
110 free(surf);
111}
112
113static void
114default_surface_destroy_listener(struct wl_listener *listener, void *data)
115{
116 struct fs_client_surface *surf;
117
118 surf = container_of(listener, struct fs_client_surface, surface_destroyed);
119
120 remove_default_surface(surf);
121}
122
123static void
124replace_default_surface(struct fullscreen_shell *shell, struct weston_surface *surface,
125 enum zwp_fullscreen_shell_v1_present_method method)
126{
127 struct fs_client_surface *surf, *prev = NULL;
128
129 if (!wl_list_empty(&shell->default_surface_list))
130 prev = container_of(shell->default_surface_list.prev,
131 struct fs_client_surface, link);
132
133 surf = zalloc(sizeof *surf);
134 if (!surf)
135 return;
136
137 surf->surface = surface;
138 surf->method = method;
139
140 if (prev)
141 remove_default_surface(prev);
142
143 wl_list_insert(shell->default_surface_list.prev, &surf->link);
144
145 surf->surface_destroyed.notify = default_surface_destroy_listener;
146 wl_signal_add(&surface->destroy_signal, &surf->surface_destroyed);
147}
148
Jason Ekstrand946a9482014-04-02 19:53:47 -0500149static void
150pointer_focus_changed(struct wl_listener *listener, void *data)
151{
152 struct weston_pointer *pointer = data;
153
154 if (pointer->focus && pointer->focus->surface->resource)
Bryce Harrington260c2ff2016-06-29 19:04:06 -0700155 weston_seat_set_keyboard_focus(pointer->seat, pointer->focus->surface);
Jason Ekstrand946a9482014-04-02 19:53:47 -0500156}
157
158static void
159seat_caps_changed(struct wl_listener *l, void *data)
160{
161 struct weston_seat *seat = data;
Derek Foreman1281a362015-07-31 16:55:32 -0500162 struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat);
163 struct weston_pointer *pointer = weston_seat_get_pointer(seat);
Jason Ekstrand946a9482014-04-02 19:53:47 -0500164 struct pointer_focus_listener *listener;
165 struct fs_output *fsout;
166
167 listener = container_of(l, struct pointer_focus_listener, seat_caps);
168
169 /* no pointer */
Derek Foreman1281a362015-07-31 16:55:32 -0500170 if (pointer) {
Jason Ekstrand946a9482014-04-02 19:53:47 -0500171 if (!listener->pointer_focus.link.prev) {
Derek Foreman1281a362015-07-31 16:55:32 -0500172 wl_signal_add(&pointer->focus_signal,
Jason Ekstrand946a9482014-04-02 19:53:47 -0500173 &listener->pointer_focus);
174 }
175 } else {
176 if (listener->pointer_focus.link.prev) {
177 wl_list_remove(&listener->pointer_focus.link);
178 }
179 }
180
Derek Foreman1281a362015-07-31 16:55:32 -0500181 if (keyboard && keyboard->focus != NULL) {
Jason Ekstrand946a9482014-04-02 19:53:47 -0500182 wl_list_for_each(fsout, &listener->shell->output_list, link) {
183 if (fsout->surface) {
Bryce Harrington260c2ff2016-06-29 19:04:06 -0700184 weston_seat_set_keyboard_focus(seat, fsout->surface);
Jason Ekstrand946a9482014-04-02 19:53:47 -0500185 return;
186 }
187 }
188 }
189}
190
191static void
192seat_destroyed(struct wl_listener *l, void *data)
193{
194 struct pointer_focus_listener *listener;
195
196 listener = container_of(l, struct pointer_focus_listener,
197 seat_destroyed);
198
199 free(listener);
200}
201
202static void
203seat_created(struct wl_listener *l, void *data)
204{
205 struct weston_seat *seat = data;
206 struct pointer_focus_listener *listener;
207
Ryo Munakata5e653ca2015-08-07 20:20:46 +0900208 listener = zalloc(sizeof *listener);
Jason Ekstrand946a9482014-04-02 19:53:47 -0500209 if (!listener)
210 return;
Jason Ekstrand946a9482014-04-02 19:53:47 -0500211
212 listener->shell = container_of(l, struct fullscreen_shell,
213 seat_created_listener);
214 listener->pointer_focus.notify = pointer_focus_changed;
215 listener->seat_caps.notify = seat_caps_changed;
216 listener->seat_destroyed.notify = seat_destroyed;
217
218 wl_signal_add(&seat->destroy_signal, &listener->seat_destroyed);
219 wl_signal_add(&seat->updated_caps_signal, &listener->seat_caps);
220
221 seat_caps_changed(&listener->seat_caps, seat);
222}
223
224static void
Quentin Glidic2edc3d52016-08-12 10:41:33 +0200225black_surface_committed(struct weston_surface *es, int32_t sx, int32_t sy)
Jason Ekstrand946a9482014-04-02 19:53:47 -0500226{
227}
228
229static struct weston_view *
230create_black_surface(struct weston_compositor *ec, struct fs_output *fsout,
231 float x, float y, int w, int h)
232{
233 struct weston_surface *surface = NULL;
234 struct weston_view *view;
235
236 surface = weston_surface_create(ec);
237 if (surface == NULL) {
238 weston_log("no memory\n");
239 return NULL;
240 }
241 view = weston_view_create(surface);
242 if (!view) {
243 weston_surface_destroy(surface);
244 weston_log("no memory\n");
245 return NULL;
246 }
247
Quentin Glidic2edc3d52016-08-12 10:41:33 +0200248 surface->committed = black_surface_committed;
249 surface->committed_private = fsout;
Jason Ekstrand946a9482014-04-02 19:53:47 -0500250 weston_surface_set_color(surface, 0.0f, 0.0f, 0.0f, 1.0f);
251 pixman_region32_fini(&surface->opaque);
252 pixman_region32_init_rect(&surface->opaque, 0, 0, w, h);
253 pixman_region32_fini(&surface->input);
254 pixman_region32_init_rect(&surface->input, 0, 0, w, h);
255
256 weston_surface_set_size(surface, w, h);
257 weston_view_set_position(view, x, y);
258
259 return view;
260}
261
262static void
263fs_output_set_surface(struct fs_output *fsout, struct weston_surface *surface,
Jonas Ådahl496adb32015-11-17 16:00:27 +0800264 enum zwp_fullscreen_shell_v1_present_method method,
Jason Ekstrand946a9482014-04-02 19:53:47 -0500265 int32_t framerate, int presented_for_mode);
266static void
267fs_output_apply_pending(struct fs_output *fsout);
268static void
269fs_output_clear_pending(struct fs_output *fsout);
270
271static void
272fs_output_destroy(struct fs_output *fsout)
273{
274 fs_output_set_surface(fsout, NULL, 0, 0, 0);
275 fs_output_clear_pending(fsout);
276
277 wl_list_remove(&fsout->link);
278
279 if (fsout->output)
280 wl_list_remove(&fsout->output_destroyed.link);
281}
282
283static void
284output_destroyed(struct wl_listener *listener, void *data)
285{
286 struct fs_output *output = container_of(listener,
287 struct fs_output,
288 output_destroyed);
289 fs_output_destroy(output);
290}
291
292static void
293surface_destroyed(struct wl_listener *listener, void *data)
294{
295 struct fs_output *fsout = container_of(listener,
296 struct fs_output,
297 surface_destroyed);
298 fsout->surface = NULL;
299 fsout->view = NULL;
Arnaud Vrace91b6e92016-06-08 17:55:08 +0200300 wl_list_remove(&fsout->transform.link);
301 wl_list_init(&fsout->transform.link);
Jason Ekstrand946a9482014-04-02 19:53:47 -0500302}
303
304static void
305pending_surface_destroyed(struct wl_listener *listener, void *data)
306{
307 struct fs_output *fsout = container_of(listener,
308 struct fs_output,
309 pending.surface_destroyed);
310 fsout->pending.surface = NULL;
311}
312
Armin Krezovićccfd0292016-08-11 15:49:59 +0200313static void
314configure_presented_surface(struct weston_surface *surface, int32_t sx,
315 int32_t sy);
316
Jason Ekstrand946a9482014-04-02 19:53:47 -0500317static struct fs_output *
318fs_output_create(struct fullscreen_shell *shell, struct weston_output *output)
319{
320 struct fs_output *fsout;
Armin Krezovićccfd0292016-08-11 15:49:59 +0200321 struct fs_client_surface *surf;
Jason Ekstrand946a9482014-04-02 19:53:47 -0500322
Ryo Munakata5e653ca2015-08-07 20:20:46 +0900323 fsout = zalloc(sizeof *fsout);
Jason Ekstrand946a9482014-04-02 19:53:47 -0500324 if (!fsout)
325 return NULL;
Jason Ekstrand946a9482014-04-02 19:53:47 -0500326
327 fsout->shell = shell;
328 wl_list_insert(&shell->output_list, &fsout->link);
329
330 fsout->output = output;
331 fsout->output_destroyed.notify = output_destroyed;
332 wl_signal_add(&output->destroy_signal, &fsout->output_destroyed);
333
334 fsout->surface_destroyed.notify = surface_destroyed;
335 fsout->pending.surface_destroyed.notify = pending_surface_destroyed;
336 fsout->black_view = create_black_surface(shell->compositor, fsout,
337 output->x, output->y,
338 output->width, output->height);
Armin Krezovićb1a48e62016-06-30 06:04:30 +0200339 fsout->black_view->surface->is_mapped = true;
340 fsout->black_view->is_mapped = true;
Giulio Camuffod1ceecf2014-07-29 21:57:24 +0300341 weston_layer_entry_insert(&shell->layer.view_list,
Jason Ekstrand946a9482014-04-02 19:53:47 -0500342 &fsout->black_view->layer_link);
343 wl_list_init(&fsout->transform.link);
Armin Krezovićccfd0292016-08-11 15:49:59 +0200344
345 if (!wl_list_empty(&shell->default_surface_list)) {
346 surf = container_of(shell->default_surface_list.prev,
347 struct fs_client_surface, link);
348
349 fs_output_set_surface(fsout, surf->surface, surf->method, 0, 0);
350 configure_presented_surface(surf->surface, 0, 0);
351 }
352
Jason Ekstrand946a9482014-04-02 19:53:47 -0500353 return fsout;
354}
355
356static struct fs_output *
357fs_output_for_output(struct weston_output *output)
358{
359 struct wl_listener *listener;
360
361 if (!output)
362 return NULL;
363
364 listener = wl_signal_get(&output->destroy_signal, output_destroyed);
365
366 return container_of(listener, struct fs_output, output_destroyed);
367}
368
369static void
370restore_output_mode(struct weston_output *output)
371{
Armin Krezović70487cd2016-06-23 11:59:33 +0200372 if (output && output->original_mode)
Derek Foreman6ae7bc92014-11-04 10:47:33 -0600373 weston_output_mode_switch_to_native(output);
Jason Ekstrand946a9482014-04-02 19:53:47 -0500374}
375
376/*
377 * Returns the bounding box of a surface and all its sub-surfaces,
Yong Bakos3b227612016-04-28 11:59:07 -0500378 * in surface-local coordinates. */
Jason Ekstrand946a9482014-04-02 19:53:47 -0500379static void
380surface_subsurfaces_boundingbox(struct weston_surface *surface, int32_t *x,
381 int32_t *y, int32_t *w, int32_t *h) {
382 pixman_region32_t region;
383 pixman_box32_t *box;
384 struct weston_subsurface *subsurface;
385
386 pixman_region32_init_rect(&region, 0, 0,
387 surface->width,
388 surface->height);
389
390 wl_list_for_each(subsurface, &surface->subsurface_list, parent_link) {
391 pixman_region32_union_rect(&region, &region,
392 subsurface->position.x,
393 subsurface->position.y,
394 subsurface->surface->width,
395 subsurface->surface->height);
396 }
397
398 box = pixman_region32_extents(&region);
399 if (x)
400 *x = box->x1;
401 if (y)
402 *y = box->y1;
403 if (w)
404 *w = box->x2 - box->x1;
405 if (h)
406 *h = box->y2 - box->y1;
407
408 pixman_region32_fini(&region);
409}
410
411static void
412fs_output_center_view(struct fs_output *fsout)
413{
414 int32_t surf_x, surf_y, surf_width, surf_height;
415 float x, y;
416 struct weston_output *output = fsout->output;
417
418 surface_subsurfaces_boundingbox(fsout->view->surface, &surf_x, &surf_y,
419 &surf_width, &surf_height);
420
421 x = output->x + (output->width - surf_width) / 2 - surf_x / 2;
422 y = output->y + (output->height - surf_height) / 2 - surf_y / 2;
423
424 weston_view_set_position(fsout->view, x, y);
425}
426
427static void
428fs_output_scale_view(struct fs_output *fsout, float width, float height)
429{
430 float x, y;
431 int32_t surf_x, surf_y, surf_width, surf_height;
432 struct weston_matrix *matrix;
433 struct weston_view *view = fsout->view;
434 struct weston_output *output = fsout->output;
435
436 surface_subsurfaces_boundingbox(view->surface, &surf_x, &surf_y,
437 &surf_width, &surf_height);
438
439 if (output->width == surf_width && output->height == surf_height) {
440 weston_view_set_position(view,
441 fsout->output->x - surf_x,
442 fsout->output->y - surf_y);
443 } else {
444 matrix = &fsout->transform.matrix;
445 weston_matrix_init(matrix);
446
447 weston_matrix_scale(matrix, width / surf_width,
448 height / surf_height, 1);
449 wl_list_remove(&fsout->transform.link);
450 wl_list_insert(&fsout->view->geometry.transformation_list,
451 &fsout->transform.link);
452
453 x = output->x + (output->width - width) / 2 - surf_x;
454 y = output->y + (output->height - height) / 2 - surf_y;
455
456 weston_view_set_position(view, x, y);
457 }
458}
459
460static void
461fs_output_configure(struct fs_output *fsout, struct weston_surface *surface);
462
463static void
464fs_output_configure_simple(struct fs_output *fsout,
465 struct weston_surface *configured_surface)
466{
467 struct weston_output *output = fsout->output;
468 float output_aspect, surface_aspect;
469 int32_t surf_x, surf_y, surf_width, surf_height;
470
471 if (fsout->pending.surface == configured_surface)
472 fs_output_apply_pending(fsout);
473
474 assert(fsout->view);
475
476 restore_output_mode(fsout->output);
477
478 wl_list_remove(&fsout->transform.link);
479 wl_list_init(&fsout->transform.link);
480
481 surface_subsurfaces_boundingbox(fsout->view->surface,
482 &surf_x, &surf_y,
483 &surf_width, &surf_height);
484
485 output_aspect = (float) output->width / (float) output->height;
486 surface_aspect = (float) surf_width / (float) surf_height;
487
488 switch (fsout->method) {
Jonas Ådahl496adb32015-11-17 16:00:27 +0800489 case ZWP_FULLSCREEN_SHELL_V1_PRESENT_METHOD_DEFAULT:
490 case ZWP_FULLSCREEN_SHELL_V1_PRESENT_METHOD_CENTER:
Jason Ekstrand946a9482014-04-02 19:53:47 -0500491 fs_output_center_view(fsout);
492 break;
493
Jonas Ådahl496adb32015-11-17 16:00:27 +0800494 case ZWP_FULLSCREEN_SHELL_V1_PRESENT_METHOD_ZOOM:
Jason Ekstrand946a9482014-04-02 19:53:47 -0500495 if (output_aspect < surface_aspect)
496 fs_output_scale_view(fsout,
497 output->width,
498 output->width / surface_aspect);
499 else
500 fs_output_scale_view(fsout,
501 output->height * surface_aspect,
502 output->height);
503 break;
504
Jonas Ådahl496adb32015-11-17 16:00:27 +0800505 case ZWP_FULLSCREEN_SHELL_V1_PRESENT_METHOD_ZOOM_CROP:
Jason Ekstrand946a9482014-04-02 19:53:47 -0500506 if (output_aspect < surface_aspect)
507 fs_output_scale_view(fsout,
508 output->height * surface_aspect,
509 output->height);
510 else
511 fs_output_scale_view(fsout,
512 output->width,
513 output->width / surface_aspect);
514 break;
515
Jonas Ådahl496adb32015-11-17 16:00:27 +0800516 case ZWP_FULLSCREEN_SHELL_V1_PRESENT_METHOD_STRETCH:
Jason Ekstrand946a9482014-04-02 19:53:47 -0500517 fs_output_scale_view(fsout, output->width, output->height);
518 break;
519 default:
520 break;
521 }
522
523 weston_view_set_position(fsout->black_view,
524 fsout->output->x - surf_x,
525 fsout->output->y - surf_y);
526 weston_surface_set_size(fsout->black_view->surface,
527 fsout->output->width,
528 fsout->output->height);
529}
530
531static void
532fs_output_configure_for_mode(struct fs_output *fsout,
533 struct weston_surface *configured_surface)
534{
535 int32_t surf_x, surf_y, surf_width, surf_height;
536 struct weston_mode mode;
537 int ret;
538
539 if (fsout->pending.surface != configured_surface) {
540 /* Nothing to really reconfigure. We'll just recenter the
541 * view in case they played with subsurfaces */
542 fs_output_center_view(fsout);
543 return;
544 }
545
546 /* We have a pending surface */
547 surface_subsurfaces_boundingbox(fsout->pending.surface,
548 &surf_x, &surf_y,
549 &surf_width, &surf_height);
550
Jason Ekstrand58106d72015-01-08 08:57:44 -0800551 /* The actual output mode is in physical units. We need to
Emmanuel Gil Peyrot426c2462019-02-20 16:33:32 +0100552 * transform the surface size to physical unit size by flipping and
Jason Ekstrand58106d72015-01-08 08:57:44 -0800553 * possibly scaling it.
554 */
555 switch (fsout->output->transform) {
556 case WL_OUTPUT_TRANSFORM_90:
557 case WL_OUTPUT_TRANSFORM_FLIPPED_90:
558 case WL_OUTPUT_TRANSFORM_270:
559 case WL_OUTPUT_TRANSFORM_FLIPPED_270:
560 mode.width = surf_height * fsout->output->native_scale;
561 mode.height = surf_width * fsout->output->native_scale;
562 break;
563
564 case WL_OUTPUT_TRANSFORM_NORMAL:
565 case WL_OUTPUT_TRANSFORM_FLIPPED:
566 case WL_OUTPUT_TRANSFORM_180:
567 case WL_OUTPUT_TRANSFORM_FLIPPED_180:
568 default:
569 mode.width = surf_width * fsout->output->native_scale;
570 mode.height = surf_height * fsout->output->native_scale;
571 }
Jason Ekstrand946a9482014-04-02 19:53:47 -0500572 mode.flags = 0;
Jason Ekstrand946a9482014-04-02 19:53:47 -0500573 mode.refresh = fsout->pending.framerate;
574
Derek Foreman6ae7bc92014-11-04 10:47:33 -0600575 ret = weston_output_mode_switch_to_temporary(fsout->output, &mode,
576 fsout->output->native_scale);
Jason Ekstrand946a9482014-04-02 19:53:47 -0500577
578 if (ret != 0) {
579 /* The mode switch failed. Clear the pending and
580 * reconfigure as per normal */
581 if (fsout->pending.mode_feedback) {
Jonas Ådahl496adb32015-11-17 16:00:27 +0800582 zwp_fullscreen_shell_mode_feedback_v1_send_mode_failed(
Jason Ekstrand946a9482014-04-02 19:53:47 -0500583 fsout->pending.mode_feedback);
584 wl_resource_destroy(fsout->pending.mode_feedback);
585 fsout->pending.mode_feedback = NULL;
586 }
587
588 fs_output_clear_pending(fsout);
589 return;
590 }
591
592 if (fsout->pending.mode_feedback) {
Jonas Ådahl496adb32015-11-17 16:00:27 +0800593 zwp_fullscreen_shell_mode_feedback_v1_send_mode_successful(
Jason Ekstrand946a9482014-04-02 19:53:47 -0500594 fsout->pending.mode_feedback);
595 wl_resource_destroy(fsout->pending.mode_feedback);
596 fsout->pending.mode_feedback = NULL;
597 }
598
599 fs_output_apply_pending(fsout);
600
601 weston_view_set_position(fsout->view,
602 fsout->output->x - surf_x,
603 fsout->output->y - surf_y);
604}
605
606static void
607fs_output_configure(struct fs_output *fsout,
608 struct weston_surface *surface)
609{
610 if (fsout->pending.surface == surface) {
611 if (fsout->pending.presented_for_mode)
612 fs_output_configure_for_mode(fsout, surface);
613 else
614 fs_output_configure_simple(fsout, surface);
615 } else {
616 if (fsout->presented_for_mode)
617 fs_output_configure_for_mode(fsout, surface);
618 else
619 fs_output_configure_simple(fsout, surface);
620 }
621
622 weston_output_schedule_repaint(fsout->output);
623}
624
625static void
626configure_presented_surface(struct weston_surface *surface, int32_t sx,
627 int32_t sy)
628{
Quentin Glidic2edc3d52016-08-12 10:41:33 +0200629 struct fullscreen_shell *shell = surface->committed_private;
Jason Ekstrand946a9482014-04-02 19:53:47 -0500630 struct fs_output *fsout;
631
Quentin Glidic2edc3d52016-08-12 10:41:33 +0200632 if (surface->committed != configure_presented_surface)
Jason Ekstrand946a9482014-04-02 19:53:47 -0500633 return;
634
635 wl_list_for_each(fsout, &shell->output_list, link)
636 if (fsout->surface == surface ||
637 fsout->pending.surface == surface)
638 fs_output_configure(fsout, surface);
639}
640
641static void
642fs_output_apply_pending(struct fs_output *fsout)
643{
644 assert(fsout->pending.surface);
645
646 if (fsout->surface && fsout->surface != fsout->pending.surface) {
647 wl_list_remove(&fsout->surface_destroyed.link);
648
649 weston_view_destroy(fsout->view);
650 fsout->view = NULL;
651
652 if (wl_list_empty(&fsout->surface->views)) {
Quentin Glidic2edc3d52016-08-12 10:41:33 +0200653 fsout->surface->committed = NULL;
654 fsout->surface->committed_private = NULL;
Jason Ekstrand946a9482014-04-02 19:53:47 -0500655 }
656
657 fsout->surface = NULL;
658 }
659
660 fsout->method = fsout->pending.method;
661 fsout->framerate = fsout->pending.framerate;
662 fsout->presented_for_mode = fsout->pending.presented_for_mode;
663
664 if (fsout->surface != fsout->pending.surface) {
665 fsout->surface = fsout->pending.surface;
666
667 fsout->view = weston_view_create(fsout->surface);
668 if (!fsout->view) {
669 weston_log("no memory\n");
670 return;
671 }
Armin Krezovićb1a48e62016-06-30 06:04:30 +0200672 fsout->view->is_mapped = true;
Jason Ekstrand946a9482014-04-02 19:53:47 -0500673
674 wl_signal_add(&fsout->surface->destroy_signal,
675 &fsout->surface_destroyed);
Giulio Camuffod1ceecf2014-07-29 21:57:24 +0300676 weston_layer_entry_insert(&fsout->shell->layer.view_list,
Jason Ekstrand946a9482014-04-02 19:53:47 -0500677 &fsout->view->layer_link);
678 }
679
680 fs_output_clear_pending(fsout);
681}
682
683static void
684fs_output_clear_pending(struct fs_output *fsout)
685{
686 if (!fsout->pending.surface)
687 return;
688
689 if (fsout->pending.mode_feedback) {
Jonas Ådahl496adb32015-11-17 16:00:27 +0800690 zwp_fullscreen_shell_mode_feedback_v1_send_present_cancelled(
Jason Ekstrand946a9482014-04-02 19:53:47 -0500691 fsout->pending.mode_feedback);
692 wl_resource_destroy(fsout->pending.mode_feedback);
693 fsout->pending.mode_feedback = NULL;
694 }
695
696 wl_list_remove(&fsout->pending.surface_destroyed.link);
697 fsout->pending.surface = NULL;
698}
699
700static void
701fs_output_set_surface(struct fs_output *fsout, struct weston_surface *surface,
Jonas Ådahl496adb32015-11-17 16:00:27 +0800702 enum zwp_fullscreen_shell_v1_present_method method,
Jason Ekstrand946a9482014-04-02 19:53:47 -0500703 int32_t framerate, int presented_for_mode)
704{
705 fs_output_clear_pending(fsout);
706
707 if (surface) {
Quentin Glidic2edc3d52016-08-12 10:41:33 +0200708 if (!surface->committed) {
709 surface->committed = configure_presented_surface;
710 surface->committed_private = fsout->shell;
Jason Ekstrand946a9482014-04-02 19:53:47 -0500711 }
712
713 fsout->pending.surface = surface;
714 wl_signal_add(&fsout->pending.surface->destroy_signal,
715 &fsout->pending.surface_destroyed);
716
717 fsout->pending.method = method;
718 fsout->pending.framerate = framerate;
719 fsout->pending.presented_for_mode = presented_for_mode;
720 } else if (fsout->surface) {
721 /* we clear immediately */
722 wl_list_remove(&fsout->surface_destroyed.link);
723
724 weston_view_destroy(fsout->view);
725 fsout->view = NULL;
726
727 if (wl_list_empty(&fsout->surface->views)) {
Quentin Glidic2edc3d52016-08-12 10:41:33 +0200728 fsout->surface->committed = NULL;
729 fsout->surface->committed_private = NULL;
Jason Ekstrand946a9482014-04-02 19:53:47 -0500730 }
731
732 fsout->surface = NULL;
733
734 weston_output_schedule_repaint(fsout->output);
735 }
736}
737
738static void
739fullscreen_shell_release(struct wl_client *client,
740 struct wl_resource *resource)
741{
742 wl_resource_destroy(resource);
743}
744
745static void
746fullscreen_shell_present_surface(struct wl_client *client,
747 struct wl_resource *resource,
748 struct wl_resource *surface_res,
749 uint32_t method,
750 struct wl_resource *output_res)
751{
752 struct fullscreen_shell *shell =
753 wl_resource_get_user_data(resource);
754 struct weston_output *output;
755 struct weston_surface *surface;
756 struct weston_seat *seat;
757 struct fs_output *fsout;
758
759 surface = surface_res ? wl_resource_get_user_data(surface_res) : NULL;
760
761 switch(method) {
Jonas Ådahl496adb32015-11-17 16:00:27 +0800762 case ZWP_FULLSCREEN_SHELL_V1_PRESENT_METHOD_DEFAULT:
763 case ZWP_FULLSCREEN_SHELL_V1_PRESENT_METHOD_CENTER:
764 case ZWP_FULLSCREEN_SHELL_V1_PRESENT_METHOD_ZOOM:
765 case ZWP_FULLSCREEN_SHELL_V1_PRESENT_METHOD_ZOOM_CROP:
766 case ZWP_FULLSCREEN_SHELL_V1_PRESENT_METHOD_STRETCH:
Jason Ekstrand946a9482014-04-02 19:53:47 -0500767 break;
768 default:
769 wl_resource_post_error(resource,
Jonas Ådahl496adb32015-11-17 16:00:27 +0800770 ZWP_FULLSCREEN_SHELL_V1_ERROR_INVALID_METHOD,
Jason Ekstrand946a9482014-04-02 19:53:47 -0500771 "Invalid presentation method");
772 }
773
774 if (output_res) {
Pekka Paalanen055c1132017-03-27 16:31:25 +0300775 output = weston_head_from_resource(output_res)->output;
Jason Ekstrand946a9482014-04-02 19:53:47 -0500776 fsout = fs_output_for_output(output);
777 fs_output_set_surface(fsout, surface, method, 0, 0);
778 } else {
Armin Krezovićccfd0292016-08-11 15:49:59 +0200779 replace_default_surface(shell, surface, method);
780
Jason Ekstrand946a9482014-04-02 19:53:47 -0500781 wl_list_for_each(fsout, &shell->output_list, link)
782 fs_output_set_surface(fsout, surface, method, 0, 0);
783 }
784
785 if (surface) {
786 wl_list_for_each(seat, &shell->compositor->seat_list, link) {
Derek Foreman1281a362015-07-31 16:55:32 -0500787 struct weston_keyboard *keyboard =
788 weston_seat_get_keyboard(seat);
789
790 if (keyboard && !keyboard->focus)
Bryce Harrington260c2ff2016-06-29 19:04:06 -0700791 weston_seat_set_keyboard_focus(seat, surface);
Jason Ekstrand946a9482014-04-02 19:53:47 -0500792 }
793 }
794}
795
796static void
797mode_feedback_destroyed(struct wl_resource *resource)
798{
799 struct fs_output *fsout = wl_resource_get_user_data(resource);
800
801 fsout->pending.mode_feedback = NULL;
802}
803
804static void
805fullscreen_shell_present_surface_for_mode(struct wl_client *client,
806 struct wl_resource *resource,
807 struct wl_resource *surface_res,
808 struct wl_resource *output_res,
809 int32_t framerate,
810 uint32_t feedback_id)
811{
812 struct fullscreen_shell *shell =
813 wl_resource_get_user_data(resource);
814 struct weston_output *output;
815 struct weston_surface *surface;
816 struct weston_seat *seat;
817 struct fs_output *fsout;
818
Pekka Paalanen055c1132017-03-27 16:31:25 +0300819 output = weston_head_from_resource(output_res)->output;
Jason Ekstrand946a9482014-04-02 19:53:47 -0500820 fsout = fs_output_for_output(output);
821
822 if (surface_res == NULL) {
823 fs_output_set_surface(fsout, NULL, 0, 0, 0);
824 return;
825 }
826
827 surface = wl_resource_get_user_data(surface_res);
828 fs_output_set_surface(fsout, surface, 0, framerate, 1);
829
830 fsout->pending.mode_feedback =
831 wl_resource_create(client,
Jonas Ådahl496adb32015-11-17 16:00:27 +0800832 &zwp_fullscreen_shell_mode_feedback_v1_interface,
Jason Ekstrand946a9482014-04-02 19:53:47 -0500833 1, feedback_id);
834 wl_resource_set_implementation(fsout->pending.mode_feedback, NULL,
835 fsout, mode_feedback_destroyed);
836
837 wl_list_for_each(seat, &shell->compositor->seat_list, link) {
Derek Foreman1281a362015-07-31 16:55:32 -0500838 struct weston_keyboard *keyboard =
839 weston_seat_get_keyboard(seat);
840
841 if (keyboard && !keyboard->focus)
Bryce Harrington260c2ff2016-06-29 19:04:06 -0700842 weston_seat_set_keyboard_focus(seat, surface);
Jason Ekstrand946a9482014-04-02 19:53:47 -0500843 }
844}
845
Jonas Ådahl496adb32015-11-17 16:00:27 +0800846struct zwp_fullscreen_shell_v1_interface fullscreen_shell_implementation = {
Jason Ekstrand946a9482014-04-02 19:53:47 -0500847 fullscreen_shell_release,
848 fullscreen_shell_present_surface,
849 fullscreen_shell_present_surface_for_mode,
850};
851
852static void
853output_created(struct wl_listener *listener, void *data)
854{
855 struct fullscreen_shell *shell;
856
857 shell = container_of(listener, struct fullscreen_shell,
858 output_created_listener);
859
860 fs_output_create(shell, data);
861}
862
863static void
864client_destroyed(struct wl_listener *listener, void *data)
865{
866 struct fullscreen_shell *shell = container_of(listener,
867 struct fullscreen_shell,
868 client_destroyed);
869 shell->client = NULL;
870}
871
872static void
873bind_fullscreen_shell(struct wl_client *client, void *data, uint32_t version,
874 uint32_t id)
875{
876 struct fullscreen_shell *shell = data;
877 struct wl_resource *resource;
878
879 if (shell->client != NULL && shell->client != client)
880 return;
881 else if (shell->client == NULL) {
882 shell->client = client;
883 wl_client_add_destroy_listener(client, &shell->client_destroyed);
884 }
885
Jonas Ådahl496adb32015-11-17 16:00:27 +0800886 resource = wl_resource_create(client,
887 &zwp_fullscreen_shell_v1_interface,
Jason Ekstrand946a9482014-04-02 19:53:47 -0500888 1, id);
889 wl_resource_set_implementation(resource,
890 &fullscreen_shell_implementation,
891 shell, NULL);
892
893 if (shell->compositor->capabilities & WESTON_CAP_CURSOR_PLANE)
Jonas Ådahl496adb32015-11-17 16:00:27 +0800894 zwp_fullscreen_shell_v1_send_capability(resource,
895 ZWP_FULLSCREEN_SHELL_V1_CAPABILITY_CURSOR_PLANE);
Jason Ekstrand946a9482014-04-02 19:53:47 -0500896
897 if (shell->compositor->capabilities & WESTON_CAP_ARBITRARY_MODES)
Jonas Ådahl496adb32015-11-17 16:00:27 +0800898 zwp_fullscreen_shell_v1_send_capability(resource,
899 ZWP_FULLSCREEN_SHELL_V1_CAPABILITY_ARBITRARY_MODES);
Jason Ekstrand946a9482014-04-02 19:53:47 -0500900}
901
902WL_EXPORT int
Quentin Glidicda01c1d2016-12-02 14:17:08 +0100903wet_shell_init(struct weston_compositor *compositor,
904 int *argc, char *argv[])
Jason Ekstrand946a9482014-04-02 19:53:47 -0500905{
906 struct fullscreen_shell *shell;
907 struct weston_seat *seat;
908 struct weston_output *output;
909
Ryo Munakata5e653ca2015-08-07 20:20:46 +0900910 shell = zalloc(sizeof *shell);
Jason Ekstrand946a9482014-04-02 19:53:47 -0500911 if (shell == NULL)
912 return -1;
913
Jason Ekstrand946a9482014-04-02 19:53:47 -0500914 shell->compositor = compositor;
Armin Krezovićccfd0292016-08-11 15:49:59 +0200915 wl_list_init(&shell->default_surface_list);
Jason Ekstrand946a9482014-04-02 19:53:47 -0500916
917 shell->client_destroyed.notify = client_destroyed;
918
Quentin Glidic82681572016-12-17 13:40:51 +0100919 weston_layer_init(&shell->layer, compositor);
920 weston_layer_set_position(&shell->layer,
921 WESTON_LAYER_POSITION_FULLSCREEN);
Jason Ekstrand946a9482014-04-02 19:53:47 -0500922
923 wl_list_init(&shell->output_list);
924 shell->output_created_listener.notify = output_created;
925 wl_signal_add(&compositor->output_created_signal,
926 &shell->output_created_listener);
927 wl_list_for_each(output, &compositor->output_list, link)
928 fs_output_create(shell, output);
929
930 shell->seat_created_listener.notify = seat_created;
931 wl_signal_add(&compositor->seat_created_signal,
932 &shell->seat_created_listener);
933 wl_list_for_each(seat, &compositor->seat_list, link)
ahe401b8762020-02-19 19:43:52 +0100934 seat_created(&shell->seat_created_listener, seat);
Jason Ekstrand946a9482014-04-02 19:53:47 -0500935
936 wl_global_create(compositor->wl_display,
Jonas Ådahl496adb32015-11-17 16:00:27 +0800937 &zwp_fullscreen_shell_v1_interface, 1, shell,
Jason Ekstrand946a9482014-04-02 19:53:47 -0500938 bind_fullscreen_shell);
939
940 return 0;
941}