[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(&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 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