[RFC 4/5] Add a wl_fullscreen_shell implementation
Jason Ekstrand
jason at jlekstrand.net
Wed Oct 23 03:48:28 CEST 2013
---
configure.ac | 8 +
src/Makefile.am | 12 ++
src/fullscreen-shell.c | 574 +++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 594 insertions(+)
create mode 100644 src/fullscreen-shell.c
diff --git a/configure.ac b/configure.ac
index cfd4540..815e3c5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -367,6 +367,13 @@ AC_ARG_ENABLE(tablet-shell,
AM_CONDITIONAL(ENABLE_TABLET_SHELL,
test "x$enable_tablet_shell" = "xyes")
+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],
@@ -500,6 +507,7 @@ AC_MSG_RESULT([
Build wcap utility ${enable_wcap_tools}
Build Tablet Shell ${enable_tablet_shell}
+ Build Fullscreen Shell ${enable_fullscreen_shell}
weston-launch utility ${enable_weston_launch}
systemd-login support ${have_systemd_login}
diff --git a/src/Makefile.am b/src/Makefile.am
index 792d58e..5f06bbc 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -112,6 +112,7 @@ moduledir = $(libdir)/weston
module_LTLIBRARIES = \
$(desktop_shell) \
$(tablet_shell) \
+ $(fullscreen_shell) \
$(cms_static) \
$(cms_colord) \
$(gl_renderer) \
@@ -299,6 +300,17 @@ tablet_shell_la_SOURCES = \
tablet-shell-server-protocol.h
endif
+if ENABLE_FULLSCREEN_SHELL
+fullscreen_shell = fullscreen-shell.la
+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.c \
+ fullscreen-shell-protocol.c \
+ fullscreen-shell-server-protocol.h
+endif
+
if HAVE_LCMS
cms_static = cms-static.la
cms_static_la_LDFLAGS = -module -avoid-version
diff --git a/src/fullscreen-shell.c b/src/fullscreen-shell.c
new file mode 100644
index 0000000..ff7c303
--- /dev/null
+++ b/src/fullscreen-shell.c
@@ -0,0 +1,574 @@
+/*
+ * 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 outputs;
+ 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 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 */
+
+ enum wl_fullscreen_shell_fullscreen_method method;
+ uint32_t framerate;
+};
+
+struct pointer_focus_listener {
+ 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)
+ 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;
+
+ 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);
+ }
+ }
+}
+
+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->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,
+ int32_t width, int32_t height)
+{
+}
+
+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;
+ surface->width = w;
+ surface->height = h;
+ 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_view_configure(view, x, y, w, h);
+
+ return view;
+}
+
+static void
+fs_output_destroy(struct fs_output *fsout)
+{
+ wl_list_remove(&fsout->link);
+
+ if (fsout->output)
+ wl_list_remove(&fsout->output_destroyed.link);
+
+ if (fsout->view) {
+ weston_view_destroy(fsout->view);
+ wl_list_remove(&fsout->surface_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 *output = container_of(listener,
+ struct fs_output,
+ output_destroyed);
+ output->surface = NULL;
+ output->view = 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->outputs, &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->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);
+ 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(®ion, 0, 0,
+ surface->width,
+ surface->height);
+
+ wl_list_for_each(subsurface, &surface->subsurface_list, parent_link) {
+ pixman_region32_union_rect(®ion, ®ion,
+ subsurface->position.x,
+ subsurface->position.y,
+ subsurface->surface->width,
+ subsurface->surface->height);
+ }
+
+ box = pixman_region32_extents(®ion);
+ 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(®ion);
+}
+
+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 scale, output_aspect, surface_aspect, 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);
+
+ output_aspect = (float) output->width / (float) output->height;
+ surface_aspect = (float) surf_width / (float) surf_height;
+
+ if (output_aspect < surface_aspect)
+ scale = (float) output->width / (float) surf_width;
+ else
+ scale = (float) output->height / (float) surf_height;
+
+ weston_matrix_scale(matrix, scale, scale, 1);
+ wl_list_remove(&fsout->transform.link);
+ wl_list_insert(&fsout->view->geometry.transformation_list,
+ &fsout->transform.link);
+
+ x = output->x + (output->width - surf_width * scale) / 2 - surf_x;
+ y = output->y + (output->height - surf_height * scale) / 2 - surf_y;
+
+ weston_view_set_position(view, x, y);
+ }
+}
+
+static void
+configure_output(struct fs_output *fsout)
+{
+ int32_t surf_x, surf_y, surf_width, surf_height;
+ struct weston_mode mode;
+
+ assert(fsout->view);
+
+ weston_view_configure(fsout->view, fsout->output->x, fsout->output->y,
+ fsout->surface->width,
+ fsout->surface->height);
+
+ if (fsout->method != WL_FULLSCREEN_SHELL_FULLSCREEN_METHOD_DRIVER)
+ restore_output_mode(fsout->output);
+
+ switch (fsout->method) {
+ case WL_FULLSCREEN_SHELL_FULLSCREEN_METHOD_DEFAULT:
+ fs_output_center_view(fsout);
+ break;
+
+ case WL_FULLSCREEN_SHELL_FULLSCREEN_METHOD_SCALE:
+ fs_output_scale_view(fsout);
+ break;
+
+ case WL_FULLSCREEN_SHELL_FULLSCREEN_METHOD_DRIVER:
+ surface_subsurfaces_boundingbox(fsout->view->surface,
+ &surf_x, &surf_y,
+ &surf_width, &surf_height);
+
+ mode.flags = 0;
+ mode.width = surf_width * fsout->view->surface->buffer_scale;
+ mode.height = surf_height * fsout->view->surface->buffer_scale;
+ mode.refresh = fsout->framerate;
+
+ if (weston_output_switch_mode(fsout->output, &mode,
+ fsout->view->surface->buffer_scale,
+ WESTON_MODE_SWITCH_SET_TEMPORARY) == 0) {
+ weston_view_set_position(fsout->view,
+ fsout->output->x - surf_x,
+ fsout->output->y - surf_y);
+ weston_view_configure(fsout->black_view,
+ fsout->output->x - surf_x,
+ fsout->output->y - surf_y,
+ fsout->output->width,
+ fsout->output->height);
+ break;
+ } else {
+ restore_output_mode(fsout->output);
+ fs_output_center_view(fsout);
+ }
+ break;
+
+ case WL_FULLSCREEN_SHELL_FULLSCREEN_METHOD_FILL:
+ fs_output_center_view(fsout);
+ break;
+
+ default:
+ break;
+ }
+
+ weston_output_schedule_repaint(fsout->output);
+}
+
+static void
+configure_presented_surface(struct weston_surface *surface, int32_t sx,
+ int32_t sy, int32_t width, int32_t height)
+{
+ struct fullscreen_shell *shell = surface->configure_private;
+ struct fs_output *fsout;
+
+ if (surface->configure != configure_presented_surface)
+ return;
+
+ wl_list_for_each(fsout, &shell->outputs, link)
+ if (fsout->view && fsout->view->surface == surface)
+ configure_output(fsout);
+}
+
+static void
+fs_output_set_surface(struct fs_output *fsout, struct weston_surface *surface,
+ enum wl_fullscreen_shell_fullscreen_method method,
+ uint32_t framerate)
+{
+ if (fsout->view && fsout->surface != 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->method = method;
+ fsout->framerate = framerate;
+
+ if (surface && fsout->surface != surface) {
+ if (!surface->configure) {
+ surface->configure = configure_presented_surface;
+ surface->configure_private = fsout->shell;
+ }
+
+ fsout->view = weston_view_create(surface);
+ if (!fsout->view) {
+ weston_log("no memory\n");
+ return;
+ }
+
+ fsout->surface = surface;
+ wl_signal_add(&surface->destroy_signal,
+ &fsout->surface_destroyed);
+ wl_list_insert(&fsout->shell->layer.view_list,
+ &fsout->view->layer_link);
+ }
+
+ configure_output(fsout);
+ weston_output_schedule_repaint(fsout->output);
+}
+
+static void
+fullscreen_shell_present_surface(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *surface_res,
+ uint32_t method, uint32_t framerate,
+ struct wl_resource *output_res)
+{
+ struct fullscreen_shell *shell =
+ wl_resource_get_user_data(resource);
+ struct weston_output *output;
+ struct weston_surface *surface;
+ struct fs_output *fsout;
+
+ surface = surface_res ? wl_resource_get_user_data(surface_res) : NULL;
+
+ if (output_res) {
+ output = wl_resource_get_user_data(output_res);
+ fsout = fs_output_for_output(output);
+ fs_output_set_surface(fsout, surface, method, framerate);
+ } else {
+ wl_list_for_each(fsout, &shell->outputs, link)
+ fs_output_set_surface(fsout, surface, method, framerate);
+ }
+}
+
+struct wl_fullscreen_shell_interface fullscreen_shell_implementation = {
+ fullscreen_shell_present_surface,
+};
+
+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);
+}
+
+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->outputs);
+ 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;
+}
--
1.8.3.1
More information about the wayland-devel
mailing list