2 * Copyright © 2013 Jason Ekstrand
4 * Permission to use, copy, modify, distribute, and sell this software and
5 * its documentation for any purpose is hereby granted without fee, provided
6 * that the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation, and that the name of the copyright holders not be used in
9 * advertising or publicity pertaining to distribution of the software
10 * without specific, written prior permission. The copyright holders make
11 * no representations about the suitability of this software for any
12 * purpose. It is provided "as is" without express or implied warranty.
14 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
15 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
16 * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
17 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
18 * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
19 * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
20 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
32 #include "compositor.h"
33 #include "fullscreen-shell-server-protocol.h"
35 struct fullscreen_shell {
36 struct wl_client *client;
37 struct wl_listener client_destroyed;
38 struct weston_compositor *compositor;
40 struct weston_layer layer;
41 struct wl_list output_list;
42 struct wl_listener output_created_listener;
44 struct wl_listener seat_created_listener;
48 struct fullscreen_shell *shell;
51 struct weston_output *output;
52 struct wl_listener output_destroyed;
55 struct weston_surface *surface;
56 struct wl_listener surface_destroyed;
57 struct wl_resource *mode_feedback;
59 int presented_for_mode;
60 enum _wl_fullscreen_shell_present_method method;
64 struct weston_surface *surface;
65 struct wl_listener surface_destroyed;
66 struct weston_view *view;
67 struct weston_view *black_view;
68 struct weston_transform transform; /* matrix from x, y */
70 int presented_for_mode;
71 enum _wl_fullscreen_shell_present_method method;
75 struct pointer_focus_listener {
76 struct fullscreen_shell *shell;
77 struct wl_listener pointer_focus;
78 struct wl_listener seat_caps;
79 struct wl_listener seat_destroyed;
83 pointer_focus_changed(struct wl_listener *listener, void *data)
85 struct weston_pointer *pointer = data;
87 if (pointer->focus && pointer->focus->surface->resource)
88 weston_surface_activate(pointer->focus->surface, pointer->seat);
92 seat_caps_changed(struct wl_listener *l, void *data)
94 struct weston_seat *seat = data;
95 struct pointer_focus_listener *listener;
96 struct fs_output *fsout;
98 listener = container_of(l, struct pointer_focus_listener, seat_caps);
102 if (!listener->pointer_focus.link.prev) {
103 wl_signal_add(&seat->pointer->focus_signal,
104 &listener->pointer_focus);
107 if (listener->pointer_focus.link.prev) {
108 wl_list_remove(&listener->pointer_focus.link);
112 if (seat->keyboard && seat->keyboard->focus != NULL) {
113 wl_list_for_each(fsout, &listener->shell->output_list, link) {
114 if (fsout->surface) {
115 weston_surface_activate(fsout->surface, seat);
123 seat_destroyed(struct wl_listener *l, void *data)
125 struct pointer_focus_listener *listener;
127 listener = container_of(l, struct pointer_focus_listener,
134 seat_created(struct wl_listener *l, void *data)
136 struct weston_seat *seat = data;
137 struct pointer_focus_listener *listener;
139 listener = malloc(sizeof *listener);
142 memset(listener, 0, sizeof *listener);
144 listener->shell = container_of(l, struct fullscreen_shell,
145 seat_created_listener);
146 listener->pointer_focus.notify = pointer_focus_changed;
147 listener->seat_caps.notify = seat_caps_changed;
148 listener->seat_destroyed.notify = seat_destroyed;
150 wl_signal_add(&seat->destroy_signal, &listener->seat_destroyed);
151 wl_signal_add(&seat->updated_caps_signal, &listener->seat_caps);
153 seat_caps_changed(&listener->seat_caps, seat);
157 black_surface_configure(struct weston_surface *es, int32_t sx, int32_t sy)
161 static struct weston_view *
162 create_black_surface(struct weston_compositor *ec, struct fs_output *fsout,
163 float x, float y, int w, int h)
165 struct weston_surface *surface = NULL;
166 struct weston_view *view;
168 surface = weston_surface_create(ec);
169 if (surface == NULL) {
170 weston_log("no memory\n");
173 view = weston_view_create(surface);
175 weston_surface_destroy(surface);
176 weston_log("no memory\n");
180 surface->configure = black_surface_configure;
181 surface->configure_private = fsout;
182 weston_surface_set_color(surface, 0.0f, 0.0f, 0.0f, 1.0f);
183 pixman_region32_fini(&surface->opaque);
184 pixman_region32_init_rect(&surface->opaque, 0, 0, w, h);
185 pixman_region32_fini(&surface->input);
186 pixman_region32_init_rect(&surface->input, 0, 0, w, h);
188 weston_surface_set_size(surface, w, h);
189 weston_view_set_position(view, x, y);
195 fs_output_set_surface(struct fs_output *fsout, struct weston_surface *surface,
196 enum _wl_fullscreen_shell_present_method method,
197 int32_t framerate, int presented_for_mode);
199 fs_output_apply_pending(struct fs_output *fsout);
201 fs_output_clear_pending(struct fs_output *fsout);
204 fs_output_destroy(struct fs_output *fsout)
206 fs_output_set_surface(fsout, NULL, 0, 0, 0);
207 fs_output_clear_pending(fsout);
209 wl_list_remove(&fsout->link);
212 wl_list_remove(&fsout->output_destroyed.link);
216 output_destroyed(struct wl_listener *listener, void *data)
218 struct fs_output *output = container_of(listener,
221 fs_output_destroy(output);
225 surface_destroyed(struct wl_listener *listener, void *data)
227 struct fs_output *fsout = container_of(listener,
230 fsout->surface = NULL;
235 pending_surface_destroyed(struct wl_listener *listener, void *data)
237 struct fs_output *fsout = container_of(listener,
239 pending.surface_destroyed);
240 fsout->pending.surface = NULL;
243 static struct fs_output *
244 fs_output_create(struct fullscreen_shell *shell, struct weston_output *output)
246 struct fs_output *fsout;
248 fsout = malloc(sizeof *fsout);
251 memset(fsout, 0, sizeof *fsout);
253 fsout->shell = shell;
254 wl_list_insert(&shell->output_list, &fsout->link);
256 fsout->output = output;
257 fsout->output_destroyed.notify = output_destroyed;
258 wl_signal_add(&output->destroy_signal, &fsout->output_destroyed);
260 fsout->surface_destroyed.notify = surface_destroyed;
261 fsout->pending.surface_destroyed.notify = pending_surface_destroyed;
262 fsout->black_view = create_black_surface(shell->compositor, fsout,
263 output->x, output->y,
264 output->width, output->height);
265 weston_layer_entry_insert(&shell->layer.view_list,
266 &fsout->black_view->layer_link);
267 wl_list_init(&fsout->transform.link);
271 static struct fs_output *
272 fs_output_for_output(struct weston_output *output)
274 struct wl_listener *listener;
279 listener = wl_signal_get(&output->destroy_signal, output_destroyed);
281 return container_of(listener, struct fs_output, output_destroyed);
285 restore_output_mode(struct weston_output *output)
287 if (output->current_mode != output->original_mode ||
288 (int32_t)output->current_scale != output->original_scale)
289 weston_output_switch_mode(output,
290 output->original_mode,
291 output->original_scale,
292 WESTON_MODE_SWITCH_RESTORE_NATIVE);
296 * Returns the bounding box of a surface and all its sub-surfaces,
297 * in the surface coordinates system. */
299 surface_subsurfaces_boundingbox(struct weston_surface *surface, int32_t *x,
300 int32_t *y, int32_t *w, int32_t *h) {
301 pixman_region32_t region;
303 struct weston_subsurface *subsurface;
305 pixman_region32_init_rect(®ion, 0, 0,
309 wl_list_for_each(subsurface, &surface->subsurface_list, parent_link) {
310 pixman_region32_union_rect(®ion, ®ion,
311 subsurface->position.x,
312 subsurface->position.y,
313 subsurface->surface->width,
314 subsurface->surface->height);
317 box = pixman_region32_extents(®ion);
323 *w = box->x2 - box->x1;
325 *h = box->y2 - box->y1;
327 pixman_region32_fini(®ion);
331 fs_output_center_view(struct fs_output *fsout)
333 int32_t surf_x, surf_y, surf_width, surf_height;
335 struct weston_output *output = fsout->output;
337 surface_subsurfaces_boundingbox(fsout->view->surface, &surf_x, &surf_y,
338 &surf_width, &surf_height);
340 x = output->x + (output->width - surf_width) / 2 - surf_x / 2;
341 y = output->y + (output->height - surf_height) / 2 - surf_y / 2;
343 weston_view_set_position(fsout->view, x, y);
347 fs_output_scale_view(struct fs_output *fsout, float width, float height)
350 int32_t surf_x, surf_y, surf_width, surf_height;
351 struct weston_matrix *matrix;
352 struct weston_view *view = fsout->view;
353 struct weston_output *output = fsout->output;
355 surface_subsurfaces_boundingbox(view->surface, &surf_x, &surf_y,
356 &surf_width, &surf_height);
358 if (output->width == surf_width && output->height == surf_height) {
359 weston_view_set_position(view,
360 fsout->output->x - surf_x,
361 fsout->output->y - surf_y);
363 matrix = &fsout->transform.matrix;
364 weston_matrix_init(matrix);
366 weston_matrix_scale(matrix, width / surf_width,
367 height / surf_height, 1);
368 wl_list_remove(&fsout->transform.link);
369 wl_list_insert(&fsout->view->geometry.transformation_list,
370 &fsout->transform.link);
372 x = output->x + (output->width - width) / 2 - surf_x;
373 y = output->y + (output->height - height) / 2 - surf_y;
375 weston_view_set_position(view, x, y);
380 fs_output_configure(struct fs_output *fsout, struct weston_surface *surface);
383 fs_output_configure_simple(struct fs_output *fsout,
384 struct weston_surface *configured_surface)
386 struct weston_output *output = fsout->output;
387 float output_aspect, surface_aspect;
388 int32_t surf_x, surf_y, surf_width, surf_height;
390 if (fsout->pending.surface == configured_surface)
391 fs_output_apply_pending(fsout);
395 restore_output_mode(fsout->output);
397 wl_list_remove(&fsout->transform.link);
398 wl_list_init(&fsout->transform.link);
400 surface_subsurfaces_boundingbox(fsout->view->surface,
402 &surf_width, &surf_height);
404 output_aspect = (float) output->width / (float) output->height;
405 surface_aspect = (float) surf_width / (float) surf_height;
407 switch (fsout->method) {
408 case _WL_FULLSCREEN_SHELL_PRESENT_METHOD_DEFAULT:
409 case _WL_FULLSCREEN_SHELL_PRESENT_METHOD_CENTER:
410 fs_output_center_view(fsout);
413 case _WL_FULLSCREEN_SHELL_PRESENT_METHOD_ZOOM:
414 if (output_aspect < surface_aspect)
415 fs_output_scale_view(fsout,
417 output->width / surface_aspect);
419 fs_output_scale_view(fsout,
420 output->height * surface_aspect,
424 case _WL_FULLSCREEN_SHELL_PRESENT_METHOD_ZOOM_CROP:
425 if (output_aspect < surface_aspect)
426 fs_output_scale_view(fsout,
427 output->height * surface_aspect,
430 fs_output_scale_view(fsout,
432 output->width / surface_aspect);
435 case _WL_FULLSCREEN_SHELL_PRESENT_METHOD_STRETCH:
436 fs_output_scale_view(fsout, output->width, output->height);
442 weston_view_set_position(fsout->black_view,
443 fsout->output->x - surf_x,
444 fsout->output->y - surf_y);
445 weston_surface_set_size(fsout->black_view->surface,
446 fsout->output->width,
447 fsout->output->height);
451 fs_output_configure_for_mode(struct fs_output *fsout,
452 struct weston_surface *configured_surface)
454 int32_t surf_x, surf_y, surf_width, surf_height;
455 struct weston_mode mode;
458 if (fsout->pending.surface != configured_surface) {
459 /* Nothing to really reconfigure. We'll just recenter the
460 * view in case they played with subsurfaces */
461 fs_output_center_view(fsout);
465 /* We have a pending surface */
466 surface_subsurfaces_boundingbox(fsout->pending.surface,
468 &surf_width, &surf_height);
471 mode.width = surf_width * fsout->output->native_scale;
472 mode.height = surf_height * fsout->output->native_scale;
473 mode.refresh = fsout->pending.framerate;
475 ret = weston_output_switch_mode(fsout->output, &mode,
476 fsout->output->native_scale,
477 WESTON_MODE_SWITCH_SET_TEMPORARY);
480 /* The mode switch failed. Clear the pending and
481 * reconfigure as per normal */
482 if (fsout->pending.mode_feedback) {
483 _wl_fullscreen_shell_mode_feedback_send_mode_failed(
484 fsout->pending.mode_feedback);
485 wl_resource_destroy(fsout->pending.mode_feedback);
486 fsout->pending.mode_feedback = NULL;
489 fs_output_clear_pending(fsout);
493 if (fsout->pending.mode_feedback) {
494 _wl_fullscreen_shell_mode_feedback_send_mode_successful(
495 fsout->pending.mode_feedback);
496 wl_resource_destroy(fsout->pending.mode_feedback);
497 fsout->pending.mode_feedback = NULL;
500 fs_output_apply_pending(fsout);
502 weston_view_set_position(fsout->view,
503 fsout->output->x - surf_x,
504 fsout->output->y - surf_y);
508 fs_output_configure(struct fs_output *fsout,
509 struct weston_surface *surface)
511 if (fsout->pending.surface == surface) {
512 if (fsout->pending.presented_for_mode)
513 fs_output_configure_for_mode(fsout, surface);
515 fs_output_configure_simple(fsout, surface);
517 if (fsout->presented_for_mode)
518 fs_output_configure_for_mode(fsout, surface);
520 fs_output_configure_simple(fsout, surface);
523 weston_output_schedule_repaint(fsout->output);
527 configure_presented_surface(struct weston_surface *surface, int32_t sx,
530 struct fullscreen_shell *shell = surface->configure_private;
531 struct fs_output *fsout;
533 if (surface->configure != configure_presented_surface)
536 wl_list_for_each(fsout, &shell->output_list, link)
537 if (fsout->surface == surface ||
538 fsout->pending.surface == surface)
539 fs_output_configure(fsout, surface);
543 fs_output_apply_pending(struct fs_output *fsout)
545 assert(fsout->pending.surface);
547 if (fsout->surface && fsout->surface != fsout->pending.surface) {
548 wl_list_remove(&fsout->surface_destroyed.link);
550 weston_view_destroy(fsout->view);
553 if (wl_list_empty(&fsout->surface->views)) {
554 fsout->surface->configure = NULL;
555 fsout->surface->configure_private = NULL;
558 fsout->surface = NULL;
561 fsout->method = fsout->pending.method;
562 fsout->framerate = fsout->pending.framerate;
563 fsout->presented_for_mode = fsout->pending.presented_for_mode;
565 if (fsout->surface != fsout->pending.surface) {
566 fsout->surface = fsout->pending.surface;
568 fsout->view = weston_view_create(fsout->surface);
570 weston_log("no memory\n");
574 wl_signal_add(&fsout->surface->destroy_signal,
575 &fsout->surface_destroyed);
576 weston_layer_entry_insert(&fsout->shell->layer.view_list,
577 &fsout->view->layer_link);
580 fs_output_clear_pending(fsout);
584 fs_output_clear_pending(struct fs_output *fsout)
586 if (!fsout->pending.surface)
589 if (fsout->pending.mode_feedback) {
590 _wl_fullscreen_shell_mode_feedback_send_present_cancelled(
591 fsout->pending.mode_feedback);
592 wl_resource_destroy(fsout->pending.mode_feedback);
593 fsout->pending.mode_feedback = NULL;
596 wl_list_remove(&fsout->pending.surface_destroyed.link);
597 fsout->pending.surface = NULL;
601 fs_output_set_surface(struct fs_output *fsout, struct weston_surface *surface,
602 enum _wl_fullscreen_shell_present_method method,
603 int32_t framerate, int presented_for_mode)
605 fs_output_clear_pending(fsout);
608 if (!surface->configure) {
609 surface->configure = configure_presented_surface;
610 surface->configure_private = fsout->shell;
613 fsout->pending.surface = surface;
614 wl_signal_add(&fsout->pending.surface->destroy_signal,
615 &fsout->pending.surface_destroyed);
617 fsout->pending.method = method;
618 fsout->pending.framerate = framerate;
619 fsout->pending.presented_for_mode = presented_for_mode;
620 } else if (fsout->surface) {
621 /* we clear immediately */
622 wl_list_remove(&fsout->surface_destroyed.link);
624 weston_view_destroy(fsout->view);
627 if (wl_list_empty(&fsout->surface->views)) {
628 fsout->surface->configure = NULL;
629 fsout->surface->configure_private = NULL;
632 fsout->surface = NULL;
634 weston_output_schedule_repaint(fsout->output);
639 fullscreen_shell_release(struct wl_client *client,
640 struct wl_resource *resource)
642 wl_resource_destroy(resource);
646 fullscreen_shell_present_surface(struct wl_client *client,
647 struct wl_resource *resource,
648 struct wl_resource *surface_res,
650 struct wl_resource *output_res)
652 struct fullscreen_shell *shell =
653 wl_resource_get_user_data(resource);
654 struct weston_output *output;
655 struct weston_surface *surface;
656 struct weston_seat *seat;
657 struct fs_output *fsout;
659 surface = surface_res ? wl_resource_get_user_data(surface_res) : NULL;
662 case _WL_FULLSCREEN_SHELL_PRESENT_METHOD_DEFAULT:
663 case _WL_FULLSCREEN_SHELL_PRESENT_METHOD_CENTER:
664 case _WL_FULLSCREEN_SHELL_PRESENT_METHOD_ZOOM:
665 case _WL_FULLSCREEN_SHELL_PRESENT_METHOD_ZOOM_CROP:
666 case _WL_FULLSCREEN_SHELL_PRESENT_METHOD_STRETCH:
669 wl_resource_post_error(resource,
670 _WL_FULLSCREEN_SHELL_ERROR_INVALID_METHOD,
671 "Invalid presentation method");
675 output = wl_resource_get_user_data(output_res);
676 fsout = fs_output_for_output(output);
677 fs_output_set_surface(fsout, surface, method, 0, 0);
679 wl_list_for_each(fsout, &shell->output_list, link)
680 fs_output_set_surface(fsout, surface, method, 0, 0);
684 wl_list_for_each(seat, &shell->compositor->seat_list, link) {
685 if (seat->keyboard && seat->keyboard->focus == NULL)
686 weston_surface_activate(surface, seat);
692 mode_feedback_destroyed(struct wl_resource *resource)
694 struct fs_output *fsout = wl_resource_get_user_data(resource);
696 fsout->pending.mode_feedback = NULL;
700 fullscreen_shell_present_surface_for_mode(struct wl_client *client,
701 struct wl_resource *resource,
702 struct wl_resource *surface_res,
703 struct wl_resource *output_res,
705 uint32_t feedback_id)
707 struct fullscreen_shell *shell =
708 wl_resource_get_user_data(resource);
709 struct weston_output *output;
710 struct weston_surface *surface;
711 struct weston_seat *seat;
712 struct fs_output *fsout;
714 output = wl_resource_get_user_data(output_res);
715 fsout = fs_output_for_output(output);
717 if (surface_res == NULL) {
718 fs_output_set_surface(fsout, NULL, 0, 0, 0);
722 surface = wl_resource_get_user_data(surface_res);
723 fs_output_set_surface(fsout, surface, 0, framerate, 1);
725 fsout->pending.mode_feedback =
726 wl_resource_create(client,
727 &_wl_fullscreen_shell_mode_feedback_interface,
729 wl_resource_set_implementation(fsout->pending.mode_feedback, NULL,
730 fsout, mode_feedback_destroyed);
732 wl_list_for_each(seat, &shell->compositor->seat_list, link) {
733 if (seat->keyboard && seat->keyboard->focus == NULL)
734 weston_surface_activate(surface, seat);
738 struct _wl_fullscreen_shell_interface fullscreen_shell_implementation = {
739 fullscreen_shell_release,
740 fullscreen_shell_present_surface,
741 fullscreen_shell_present_surface_for_mode,
745 output_created(struct wl_listener *listener, void *data)
747 struct fullscreen_shell *shell;
749 shell = container_of(listener, struct fullscreen_shell,
750 output_created_listener);
752 fs_output_create(shell, data);
756 client_destroyed(struct wl_listener *listener, void *data)
758 struct fullscreen_shell *shell = container_of(listener,
759 struct fullscreen_shell,
761 shell->client = NULL;
765 bind_fullscreen_shell(struct wl_client *client, void *data, uint32_t version,
768 struct fullscreen_shell *shell = data;
769 struct wl_resource *resource;
771 if (shell->client != NULL && shell->client != client)
773 else if (shell->client == NULL) {
774 shell->client = client;
775 wl_client_add_destroy_listener(client, &shell->client_destroyed);
778 resource = wl_resource_create(client, &_wl_fullscreen_shell_interface,
780 wl_resource_set_implementation(resource,
781 &fullscreen_shell_implementation,
784 if (shell->compositor->capabilities & WESTON_CAP_CURSOR_PLANE)
785 _wl_fullscreen_shell_send_capability(resource,
786 _WL_FULLSCREEN_SHELL_CAPABILITY_CURSOR_PLANE);
788 if (shell->compositor->capabilities & WESTON_CAP_ARBITRARY_MODES)
789 _wl_fullscreen_shell_send_capability(resource,
790 _WL_FULLSCREEN_SHELL_CAPABILITY_ARBITRARY_MODES);
794 module_init(struct weston_compositor *compositor,
795 int *argc, char *argv[])
797 struct fullscreen_shell *shell;
798 struct weston_seat *seat;
799 struct weston_output *output;
801 shell = malloc(sizeof *shell);
805 memset(shell, 0, sizeof *shell);
806 shell->compositor = compositor;
808 shell->client_destroyed.notify = client_destroyed;
810 weston_layer_init(&shell->layer, &compositor->cursor_layer.link);
812 wl_list_init(&shell->output_list);
813 shell->output_created_listener.notify = output_created;
814 wl_signal_add(&compositor->output_created_signal,
815 &shell->output_created_listener);
816 wl_list_for_each(output, &compositor->output_list, link)
817 fs_output_create(shell, output);
819 shell->seat_created_listener.notify = seat_created;
820 wl_signal_add(&compositor->seat_created_signal,
821 &shell->seat_created_listener);
822 wl_list_for_each(seat, &compositor->seat_list, link)
823 seat_created(NULL, seat);
825 wl_global_create(compositor->wl_display,
826 &_wl_fullscreen_shell_interface, 1, shell,
827 bind_fullscreen_shell);