summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Ekstrand <jason@jlekstrand.net>2014-04-03 00:53:47 (GMT)
committerKristian Høgsberg <krh@bitplanet.net>2014-04-03 04:25:56 (GMT)
commit946a9489bfc0bf7cf5d67ba4f5fb37ffe17d0234 (patch)
tree8424b5de1ee01563e73b3f2a5fce6e2fa9f7509e
parent9fc71519912c33a270f2579e1ef52b809faf130e (diff)
Add a wl_fullscreen_shell implementation
Signed-off-by: Jason Ekstrand <jason@jlekstrand.net>
-rw-r--r--Makefile.am22
-rw-r--r--configure.ac8
-rw-r--r--fullscreen-shell/fullscreen-shell.c830
3 files changed, 860 insertions, 0 deletions
diff --git a/Makefile.am b/Makefile.am
index e181528..9beee99 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -667,6 +667,28 @@ nodist_desktop_shell_la_SOURCES = \
BUILT_SOURCES += $(nodist_desktop_shell_la_SOURCES)
endif
+if ENABLE_FULLSCREEN_SHELL
+
+module_LTLIBRARIES += fullscreen-shell.la
+
+fullscreen_shell_la_CPPFLAGS = \
+ -I$(top_builddir)/protocol \
+ -I$(top_srcdir)/shared \
+ -I$(top_srcdir)/src \
+ -I$(top_builddir)/src \
+ -DIN_WESTON
+
+fullscreen_shell_la_LDFLAGS = -module -avoid-version
+fullscreen_shell_la_LIBADD = $(COMPOSITOR_LIBS)
+fullscreen_shell_la_CFLAGS = $(GCC_CFLAGS) $(COMPOSITOR_CFLAGS)
+fullscreen_shell_la_SOURCES = \
+ fullscreen-shell/fullscreen-shell.c
+nodist_fullscreen_shell_la_SOURCES = \
+ protocol/fullscreen-shell-protocol.c \
+ protocol/fullscreen-shell-server-protocol.h
+
+BUILT_SOURCES += $(nodist_fullscreen_shell_la_SOURCES)
+endif
if ENABLE_XWAYLAND
diff --git a/configure.ac b/configure.ac
index 4db0ed1..9e68678 100644
--- a/configure.ac
+++ b/configure.ac
@@ -373,6 +373,13 @@ AM_CONDITIONAL(BUILD_SUBSURFACES_CLIENT,
AM_CONDITIONAL(ENABLE_DESKTOP_SHELL, true)
+AC_ARG_ENABLE(fullscreen-shell,
+ AS_HELP_STRING([--disable-fullscreen-shell],
+ [do not build fullscreen-shell server plugin]),,
+ enable_fullscreen_shell=yes)
+AM_CONDITIONAL(ENABLE_FULLSCREEN_SHELL,
+ test "x$enable_fullscreen_shell" = "xyes")
+
# CMS modules
AC_ARG_ENABLE(colord,
AS_HELP_STRING([--disable-colord],
@@ -504,6 +511,7 @@ AC_MSG_RESULT([
dbus ${enable_dbus}
Build wcap utility ${enable_wcap_tools}
+ Build Fullscreen Shell ${enable_fullscreen_shell}
weston-launch utility ${enable_weston_launch}
systemd-login support ${have_systemd_login}
diff --git a/fullscreen-shell/fullscreen-shell.c b/fullscreen-shell/fullscreen-shell.c
new file mode 100644
index 0000000..a8dad8e
--- /dev/null
+++ b/fullscreen-shell/fullscreen-shell.c
@@ -0,0 +1,830 @@
+/*
+ * Copyright © 2013 Jason Ekstrand
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the copyright holders not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission. The copyright holders make
+ * no representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <sys/wait.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+#include "compositor.h"
+#include "fullscreen-shell-server-protocol.h"
+
+struct fullscreen_shell {
+ struct wl_client *client;
+ struct wl_listener client_destroyed;
+ struct weston_compositor *compositor;
+
+ struct weston_layer layer;
+ struct wl_list output_list;
+ struct wl_listener output_created_listener;
+
+ struct wl_listener seat_created_listener;
+};
+
+struct fs_output {
+ struct fullscreen_shell *shell;
+ struct wl_list link;
+
+ struct weston_output *output;
+ struct wl_listener output_destroyed;
+
+ struct {
+ struct weston_surface *surface;
+ struct wl_listener surface_destroyed;
+ struct wl_resource *mode_feedback;
+
+ int presented_for_mode;
+ enum _wl_fullscreen_shell_present_method method;
+ int32_t framerate;
+ } pending;
+
+ struct weston_surface *surface;
+ struct wl_listener surface_destroyed;
+ struct weston_view *view;
+ struct weston_view *black_view;
+ struct weston_transform transform; /* matrix from x, y */
+
+ int presented_for_mode;
+ enum _wl_fullscreen_shell_present_method method;
+ uint32_t framerate;
+};
+
+struct pointer_focus_listener {
+ struct fullscreen_shell *shell;
+ struct wl_listener pointer_focus;
+ struct wl_listener seat_caps;
+ struct wl_listener seat_destroyed;
+};
+
+static void
+pointer_focus_changed(struct wl_listener *listener, void *data)
+{
+ struct weston_pointer *pointer = data;
+
+ if (pointer->focus && pointer->focus->surface->resource)
+ weston_surface_activate(pointer->focus->surface, pointer->seat);
+}
+
+static void
+seat_caps_changed(struct wl_listener *l, void *data)
+{
+ struct weston_seat *seat = data;
+ struct pointer_focus_listener *listener;
+ struct fs_output *fsout;
+
+ listener = container_of(l, struct pointer_focus_listener, seat_caps);
+
+ /* no pointer */
+ if (seat->pointer) {
+ if (!listener->pointer_focus.link.prev) {
+ wl_signal_add(&seat->pointer->focus_signal,
+ &listener->pointer_focus);
+ }
+ } else {
+ if (listener->pointer_focus.link.prev) {
+ wl_list_remove(&listener->pointer_focus.link);
+ }
+ }
+
+ if (seat->keyboard && seat->keyboard->focus != NULL) {
+ wl_list_for_each(fsout, &listener->shell->output_list, link) {
+ if (fsout->surface) {
+ weston_surface_activate(fsout->surface, seat);
+ return;
+ }
+ }
+ }
+}
+
+static void
+seat_destroyed(struct wl_listener *l, void *data)
+{
+ struct pointer_focus_listener *listener;
+
+ listener = container_of(l, struct pointer_focus_listener,
+ seat_destroyed);
+
+ free(listener);
+}
+
+static void
+seat_created(struct wl_listener *l, void *data)
+{
+ struct weston_seat *seat = data;
+ struct pointer_focus_listener *listener;
+
+ listener = malloc(sizeof *listener);
+ if (!listener)
+ return;
+ memset(listener, 0, sizeof *listener);
+
+ listener->shell = container_of(l, struct fullscreen_shell,
+ seat_created_listener);
+ listener->pointer_focus.notify = pointer_focus_changed;
+ listener->seat_caps.notify = seat_caps_changed;
+ listener->seat_destroyed.notify = seat_destroyed;
+
+ wl_signal_add(&seat->destroy_signal, &listener->seat_destroyed);
+ wl_signal_add(&seat->updated_caps_signal, &listener->seat_caps);
+
+ seat_caps_changed(&listener->seat_caps, seat);
+}
+
+static void
+black_surface_configure(struct weston_surface *es, int32_t sx, int32_t sy)
+{
+}
+
+static struct weston_view *
+create_black_surface(struct weston_compositor *ec, struct fs_output *fsout,
+ float x, float y, int w, int h)
+{
+ struct weston_surface *surface = NULL;
+ struct weston_view *view;
+
+ surface = weston_surface_create(ec);
+ if (surface == NULL) {
+ weston_log("no memory\n");
+ return NULL;
+ }
+ view = weston_view_create(surface);
+ if (!view) {
+ weston_surface_destroy(surface);
+ weston_log("no memory\n");
+ return NULL;
+ }
+
+ surface->configure = black_surface_configure;
+ surface->configure_private = fsout;
+ weston_surface_set_color(surface, 0.0f, 0.0f, 0.0f, 1.0f);
+ pixman_region32_fini(&surface->opaque);
+ pixman_region32_init_rect(&surface->opaque, 0, 0, w, h);
+ pixman_region32_fini(&surface->input);
+ pixman_region32_init_rect(&surface->input, 0, 0, w, h);
+
+ weston_surface_set_size(surface, w, h);
+ weston_view_set_position(view, x, y);
+
+ return view;
+}
+
+static void
+fs_output_set_surface(struct fs_output *fsout, struct weston_surface *surface,
+ enum _wl_fullscreen_shell_present_method method,
+ int32_t framerate, int presented_for_mode);
+static void
+fs_output_apply_pending(struct fs_output *fsout);
+static void
+fs_output_clear_pending(struct fs_output *fsout);
+
+static void
+fs_output_destroy(struct fs_output *fsout)
+{
+ fs_output_set_surface(fsout, NULL, 0, 0, 0);
+ fs_output_clear_pending(fsout);
+
+ wl_list_remove(&fsout->link);
+
+ if (fsout->output)
+ wl_list_remove(&fsout->output_destroyed.link);
+}
+
+static void
+output_destroyed(struct wl_listener *listener, void *data)
+{
+ struct fs_output *output = container_of(listener,
+ struct fs_output,
+ output_destroyed);
+ fs_output_destroy(output);
+}
+
+static void
+surface_destroyed(struct wl_listener *listener, void *data)
+{
+ struct fs_output *fsout = container_of(listener,
+ struct fs_output,
+ surface_destroyed);
+ fsout->surface = NULL;
+ fsout->view = NULL;
+}
+
+static void
+pending_surface_destroyed(struct wl_listener *listener, void *data)
+{
+ struct fs_output *fsout = container_of(listener,
+ struct fs_output,
+ pending.surface_destroyed);
+ fsout->pending.surface = NULL;
+}
+
+static struct fs_output *
+fs_output_create(struct fullscreen_shell *shell, struct weston_output *output)
+{
+ struct fs_output *fsout;
+
+ fsout = malloc(sizeof *fsout);
+ if (!fsout)
+ return NULL;
+ memset(fsout, 0, sizeof *fsout);
+
+ fsout->shell = shell;
+ wl_list_insert(&shell->output_list, &fsout->link);
+
+ fsout->output = output;
+ fsout->output_destroyed.notify = output_destroyed;
+ wl_signal_add(&output->destroy_signal, &fsout->output_destroyed);
+
+ fsout->surface_destroyed.notify = surface_destroyed;
+ fsout->pending.surface_destroyed.notify = pending_surface_destroyed;
+ fsout->black_view = create_black_surface(shell->compositor, fsout,
+ output->x, output->y,
+ output->width, output->height);
+ wl_list_insert(&shell->layer.view_list,
+ &fsout->black_view->layer_link);
+ wl_list_init(&fsout->transform.link);
+ return fsout;
+}
+
+static struct fs_output *
+fs_output_for_output(struct weston_output *output)
+{
+ struct wl_listener *listener;
+
+ if (!output)
+ return NULL;
+
+ listener = wl_signal_get(&output->destroy_signal, output_destroyed);
+
+ return container_of(listener, struct fs_output, output_destroyed);
+}
+
+static void
+restore_output_mode(struct weston_output *output)
+{
+ if (output->current_mode != output->original_mode ||
+ (int32_t)output->current_scale != output->original_scale)
+ weston_output_switch_mode(output,
+ output->original_mode,
+ output->original_scale,
+ WESTON_MODE_SWITCH_RESTORE_NATIVE);
+}
+
+/*
+ * Returns the bounding box of a surface and all its sub-surfaces,
+ * in the surface coordinates system. */
+static void
+surface_subsurfaces_boundingbox(struct weston_surface *surface, int32_t *x,
+ int32_t *y, int32_t *w, int32_t *h) {
+ pixman_region32_t region;
+ pixman_box32_t *box;
+ struct weston_subsurface *subsurface;
+
+ pixman_region32_init_rect(&region, 0, 0,
+ surface->width,
+ surface->height);
+
+ wl_list_for_each(subsurface, &surface->subsurface_list, parent_link) {
+ pixman_region32_union_rect(&region, &region,
+ subsurface->position.x,
+ subsurface->position.y,
+ subsurface->surface->width,
+ subsurface->surface->height);
+ }
+
+ box = pixman_region32_extents(&region);
+ if (x)
+ *x = box->x1;
+ if (y)
+ *y = box->y1;
+ if (w)
+ *w = box->x2 - box->x1;
+ if (h)
+ *h = box->y2 - box->y1;
+
+ pixman_region32_fini(&region);
+}
+
+static void
+fs_output_center_view(struct fs_output *fsout)
+{
+ int32_t surf_x, surf_y, surf_width, surf_height;
+ float x, y;
+ struct weston_output *output = fsout->output;
+
+ surface_subsurfaces_boundingbox(fsout->view->surface, &surf_x, &surf_y,
+ &surf_width, &surf_height);
+
+ x = output->x + (output->width - surf_width) / 2 - surf_x / 2;
+ y = output->y + (output->height - surf_height) / 2 - surf_y / 2;
+
+ weston_view_set_position(fsout->view, x, y);
+}
+
+static void
+fs_output_scale_view(struct fs_output *fsout, float width, float height)
+{
+ float x, y;
+ int32_t surf_x, surf_y, surf_width, surf_height;
+ struct weston_matrix *matrix;
+ struct weston_view *view = fsout->view;
+ struct weston_output *output = fsout->output;
+
+ surface_subsurfaces_boundingbox(view->surface, &surf_x, &surf_y,
+ &surf_width, &surf_height);
+
+ if (output->width == surf_width && output->height == surf_height) {
+ weston_view_set_position(view,
+ fsout->output->x - surf_x,
+ fsout->output->y - surf_y);
+ } else {
+ matrix = &fsout->transform.matrix;
+ weston_matrix_init(matrix);
+
+ weston_matrix_scale(matrix, width / surf_width,
+ height / surf_height, 1);
+ wl_list_remove(&fsout->transform.link);
+ wl_list_insert(&fsout->view->geometry.transformation_list,
+ &fsout->transform.link);
+
+ x = output->x + (output->width - width) / 2 - surf_x;
+ y = output->y + (output->height - height) / 2 - surf_y;
+
+ weston_view_set_position(view, x, y);
+ }
+}
+
+static void
+fs_output_configure(struct fs_output *fsout, struct weston_surface *surface);
+
+static void
+fs_output_configure_simple(struct fs_output *fsout,
+ struct weston_surface *configured_surface)
+{
+ struct weston_output *output = fsout->output;
+ float output_aspect, surface_aspect;
+ int32_t surf_x, surf_y, surf_width, surf_height;
+
+ if (fsout->pending.surface == configured_surface)
+ fs_output_apply_pending(fsout);
+
+ assert(fsout->view);
+
+ restore_output_mode(fsout->output);
+
+ wl_list_remove(&fsout->transform.link);
+ wl_list_init(&fsout->transform.link);
+
+ surface_subsurfaces_boundingbox(fsout->view->surface,
+ &surf_x, &surf_y,
+ &surf_width, &surf_height);
+
+ output_aspect = (float) output->width / (float) output->height;
+ surface_aspect = (float) surf_width / (float) surf_height;
+
+ switch (fsout->method) {
+ case _WL_FULLSCREEN_SHELL_PRESENT_METHOD_DEFAULT:
+ case _WL_FULLSCREEN_SHELL_PRESENT_METHOD_CENTER:
+ fs_output_center_view(fsout);
+ break;
+
+ case _WL_FULLSCREEN_SHELL_PRESENT_METHOD_ZOOM:
+ if (output_aspect < surface_aspect)
+ fs_output_scale_view(fsout,
+ output->width,
+ output->width / surface_aspect);
+ else
+ fs_output_scale_view(fsout,
+ output->height * surface_aspect,
+ output->height);
+ break;
+
+ case _WL_FULLSCREEN_SHELL_PRESENT_METHOD_ZOOM_CROP:
+ if (output_aspect < surface_aspect)
+ fs_output_scale_view(fsout,
+ output->height * surface_aspect,
+ output->height);
+ else
+ fs_output_scale_view(fsout,
+ output->width,
+ output->width / surface_aspect);
+ break;
+
+ case _WL_FULLSCREEN_SHELL_PRESENT_METHOD_STRETCH:
+ fs_output_scale_view(fsout, output->width, output->height);
+ break;
+ default:
+ break;
+ }
+
+ weston_view_set_position(fsout->black_view,
+ fsout->output->x - surf_x,
+ fsout->output->y - surf_y);
+ weston_surface_set_size(fsout->black_view->surface,
+ fsout->output->width,
+ fsout->output->height);
+}
+
+static void
+fs_output_configure_for_mode(struct fs_output *fsout,
+ struct weston_surface *configured_surface)
+{
+ int32_t surf_x, surf_y, surf_width, surf_height;
+ struct weston_mode mode;
+ int ret;
+
+ if (fsout->pending.surface != configured_surface) {
+ /* Nothing to really reconfigure. We'll just recenter the
+ * view in case they played with subsurfaces */
+ fs_output_center_view(fsout);
+ return;
+ }
+
+ /* We have a pending surface */
+ surface_subsurfaces_boundingbox(fsout->pending.surface,
+ &surf_x, &surf_y,
+ &surf_width, &surf_height);
+
+ mode.flags = 0;
+ mode.width = surf_width * fsout->output->native_scale;
+ mode.height = surf_height * fsout->output->native_scale;
+ mode.refresh = fsout->pending.framerate;
+
+ ret = weston_output_switch_mode(fsout->output, &mode,
+ fsout->output->native_scale,
+ WESTON_MODE_SWITCH_SET_TEMPORARY);
+
+ if (ret != 0) {
+ /* The mode switch failed. Clear the pending and
+ * reconfigure as per normal */
+ if (fsout->pending.mode_feedback) {
+ _wl_fullscreen_shell_mode_feedback_send_mode_failed(
+ fsout->pending.mode_feedback);
+ wl_resource_destroy(fsout->pending.mode_feedback);
+ fsout->pending.mode_feedback = NULL;
+ }
+
+ fs_output_clear_pending(fsout);
+ return;
+ }
+
+ if (fsout->pending.mode_feedback) {
+ _wl_fullscreen_shell_mode_feedback_send_mode_successful(
+ fsout->pending.mode_feedback);
+ wl_resource_destroy(fsout->pending.mode_feedback);
+ fsout->pending.mode_feedback = NULL;
+ }
+
+ fs_output_apply_pending(fsout);
+
+ weston_view_set_position(fsout->view,
+ fsout->output->x - surf_x,
+ fsout->output->y - surf_y);
+}
+
+static void
+fs_output_configure(struct fs_output *fsout,
+ struct weston_surface *surface)
+{
+ if (fsout->pending.surface == surface) {
+ if (fsout->pending.presented_for_mode)
+ fs_output_configure_for_mode(fsout, surface);
+ else
+ fs_output_configure_simple(fsout, surface);
+ } else {
+ if (fsout->presented_for_mode)
+ fs_output_configure_for_mode(fsout, surface);
+ else
+ fs_output_configure_simple(fsout, surface);
+ }
+
+ weston_output_schedule_repaint(fsout->output);
+}
+
+static void
+configure_presented_surface(struct weston_surface *surface, int32_t sx,
+ int32_t sy)
+{
+ struct fullscreen_shell *shell = surface->configure_private;
+ struct fs_output *fsout;
+
+ if (surface->configure != configure_presented_surface)
+ return;
+
+ wl_list_for_each(fsout, &shell->output_list, link)
+ if (fsout->surface == surface ||
+ fsout->pending.surface == surface)
+ fs_output_configure(fsout, surface);
+}
+
+static void
+fs_output_apply_pending(struct fs_output *fsout)
+{
+ assert(fsout->pending.surface);
+
+ if (fsout->surface && fsout->surface != fsout->pending.surface) {
+ wl_list_remove(&fsout->surface_destroyed.link);
+
+ weston_view_destroy(fsout->view);
+ fsout->view = NULL;
+
+ if (wl_list_empty(&fsout->surface->views)) {
+ fsout->surface->configure = NULL;
+ fsout->surface->configure_private = NULL;
+ }
+
+ fsout->surface = NULL;
+ }
+
+ fsout->method = fsout->pending.method;
+ fsout->framerate = fsout->pending.framerate;
+ fsout->presented_for_mode = fsout->pending.presented_for_mode;
+
+ if (fsout->surface != fsout->pending.surface) {
+ fsout->surface = fsout->pending.surface;
+
+ fsout->view = weston_view_create(fsout->surface);
+ if (!fsout->view) {
+ weston_log("no memory\n");
+ return;
+ }
+
+ wl_signal_add(&fsout->surface->destroy_signal,
+ &fsout->surface_destroyed);
+ wl_list_insert(&fsout->shell->layer.view_list,
+ &fsout->view->layer_link);
+ }
+
+ fs_output_clear_pending(fsout);
+}
+
+static void
+fs_output_clear_pending(struct fs_output *fsout)
+{
+ if (!fsout->pending.surface)
+ return;
+
+ if (fsout->pending.mode_feedback) {
+ _wl_fullscreen_shell_mode_feedback_send_present_cancelled(
+ fsout->pending.mode_feedback);
+ wl_resource_destroy(fsout->pending.mode_feedback);
+ fsout->pending.mode_feedback = NULL;
+ }
+
+ wl_list_remove(&fsout->pending.surface_destroyed.link);
+ fsout->pending.surface = NULL;
+}
+
+static void
+fs_output_set_surface(struct fs_output *fsout, struct weston_surface *surface,
+ enum _wl_fullscreen_shell_present_method method,
+ int32_t framerate, int presented_for_mode)
+{
+ fs_output_clear_pending(fsout);
+
+ if (surface) {
+ if (!surface->configure) {
+ surface->configure = configure_presented_surface;
+ surface->configure_private = fsout->shell;
+ }
+
+ fsout->pending.surface = surface;
+ wl_signal_add(&fsout->pending.surface->destroy_signal,
+ &fsout->pending.surface_destroyed);
+
+ fsout->pending.method = method;
+ fsout->pending.framerate = framerate;
+ fsout->pending.presented_for_mode = presented_for_mode;
+ } else if (fsout->surface) {
+ /* we clear immediately */
+ wl_list_remove(&fsout->surface_destroyed.link);
+
+ weston_view_destroy(fsout->view);
+ fsout->view = NULL;
+
+ if (wl_list_empty(&fsout->surface->views)) {
+ fsout->surface->configure = NULL;
+ fsout->surface->configure_private = NULL;
+ }
+
+ fsout->surface = NULL;
+
+ weston_output_schedule_repaint(fsout->output);
+ }
+}
+
+static void
+fullscreen_shell_release(struct wl_client *client,
+ struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+static void
+fullscreen_shell_present_surface(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *surface_res,
+ uint32_t method,
+ struct wl_resource *output_res)
+{
+ struct fullscreen_shell *shell =
+ wl_resource_get_user_data(resource);
+ struct weston_output *output;
+ struct weston_surface *surface;
+ struct weston_seat *seat;
+ struct fs_output *fsout;
+
+ surface = surface_res ? wl_resource_get_user_data(surface_res) : NULL;
+
+ switch(method) {
+ case _WL_FULLSCREEN_SHELL_PRESENT_METHOD_DEFAULT:
+ case _WL_FULLSCREEN_SHELL_PRESENT_METHOD_CENTER:
+ case _WL_FULLSCREEN_SHELL_PRESENT_METHOD_ZOOM:
+ case _WL_FULLSCREEN_SHELL_PRESENT_METHOD_ZOOM_CROP:
+ case _WL_FULLSCREEN_SHELL_PRESENT_METHOD_STRETCH:
+ break;
+ default:
+ wl_resource_post_error(resource,
+ _WL_FULLSCREEN_SHELL_ERROR_INVALID_METHOD,
+ "Invalid presentation method");
+ }
+
+ if (output_res) {
+ output = wl_resource_get_user_data(output_res);
+ fsout = fs_output_for_output(output);
+ fs_output_set_surface(fsout, surface, method, 0, 0);
+ } else {
+ wl_list_for_each(fsout, &shell->output_list, link)
+ fs_output_set_surface(fsout, surface, method, 0, 0);
+ }
+
+ if (surface) {
+ wl_list_for_each(seat, &shell->compositor->seat_list, link) {
+ if (seat->keyboard && seat->keyboard->focus == NULL)
+ weston_surface_activate(surface, seat);
+ }
+ }
+}
+
+static void
+mode_feedback_destroyed(struct wl_resource *resource)
+{
+ struct fs_output *fsout = wl_resource_get_user_data(resource);
+
+ fsout->pending.mode_feedback = NULL;
+}
+
+static void
+fullscreen_shell_present_surface_for_mode(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *surface_res,
+ struct wl_resource *output_res,
+ int32_t framerate,
+ uint32_t feedback_id)
+{
+ struct fullscreen_shell *shell =
+ wl_resource_get_user_data(resource);
+ struct weston_output *output;
+ struct weston_surface *surface;
+ struct weston_seat *seat;
+ struct fs_output *fsout;
+
+ output = wl_resource_get_user_data(output_res);
+ fsout = fs_output_for_output(output);
+
+ if (surface_res == NULL) {
+ fs_output_set_surface(fsout, NULL, 0, 0, 0);
+ return;
+ }
+
+ surface = wl_resource_get_user_data(surface_res);
+ fs_output_set_surface(fsout, surface, 0, framerate, 1);
+
+ fsout->pending.mode_feedback =
+ wl_resource_create(client,
+ &_wl_fullscreen_shell_mode_feedback_interface,
+ 1, feedback_id);
+ wl_resource_set_implementation(fsout->pending.mode_feedback, NULL,
+ fsout, mode_feedback_destroyed);
+
+ wl_list_for_each(seat, &shell->compositor->seat_list, link) {
+ if (seat->keyboard && seat->keyboard->focus == NULL)
+ weston_surface_activate(surface, seat);
+ }
+}
+
+struct _wl_fullscreen_shell_interface fullscreen_shell_implementation = {
+ fullscreen_shell_release,
+ fullscreen_shell_present_surface,
+ fullscreen_shell_present_surface_for_mode,
+};
+
+static void
+output_created(struct wl_listener *listener, void *data)
+{
+ struct fullscreen_shell *shell;
+
+ shell = container_of(listener, struct fullscreen_shell,
+ output_created_listener);
+
+ fs_output_create(shell, data);
+}
+
+static void
+client_destroyed(struct wl_listener *listener, void *data)
+{
+ struct fullscreen_shell *shell = container_of(listener,
+ struct fullscreen_shell,
+ client_destroyed);
+ shell->client = NULL;
+}
+
+static void
+bind_fullscreen_shell(struct wl_client *client, void *data, uint32_t version,
+ uint32_t id)
+{
+ struct fullscreen_shell *shell = data;
+ struct wl_resource *resource;
+
+ if (shell->client != NULL && shell->client != client)
+ return;
+ else if (shell->client == NULL) {
+ shell->client = client;
+ wl_client_add_destroy_listener(client, &shell->client_destroyed);
+ }
+
+ resource = wl_resource_create(client, &_wl_fullscreen_shell_interface,
+ 1, id);
+ wl_resource_set_implementation(resource,
+ &fullscreen_shell_implementation,
+ shell, NULL);
+
+ if (shell->compositor->capabilities & WESTON_CAP_CURSOR_PLANE)
+ _wl_fullscreen_shell_send_capability(resource,
+ _WL_FULLSCREEN_SHELL_CAPABILITY_CURSOR_PLANE);
+
+ if (shell->compositor->capabilities & WESTON_CAP_ARBITRARY_MODES)
+ _wl_fullscreen_shell_send_capability(resource,
+ _WL_FULLSCREEN_SHELL_CAPABILITY_ARBITRARY_MODES);
+}
+
+WL_EXPORT int
+module_init(struct weston_compositor *compositor,
+ int *argc, char *argv[])
+{
+ struct fullscreen_shell *shell;
+ struct weston_seat *seat;
+ struct weston_output *output;
+
+ shell = malloc(sizeof *shell);
+ if (shell == NULL)
+ return -1;
+
+ memset(shell, 0, sizeof *shell);
+ shell->compositor = compositor;
+
+ shell->client_destroyed.notify = client_destroyed;
+
+ weston_layer_init(&shell->layer, &compositor->cursor_layer.link);
+
+ wl_list_init(&shell->output_list);
+ shell->output_created_listener.notify = output_created;
+ wl_signal_add(&compositor->output_created_signal,
+ &shell->output_created_listener);
+ wl_list_for_each(output, &compositor->output_list, link)
+ fs_output_create(shell, output);
+
+ shell->seat_created_listener.notify = seat_created;
+ wl_signal_add(&compositor->seat_created_signal,
+ &shell->seat_created_listener);
+ wl_list_for_each(seat, &compositor->seat_list, link)
+ seat_created(NULL, seat);
+
+ wl_global_create(compositor->wl_display,
+ &_wl_fullscreen_shell_interface, 1, shell,
+ bind_fullscreen_shell);
+
+ return 0;
+}