summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Bragg <robert@linux.intel.com>2011-12-08 16:07:09 +0000
committerRobert Bragg <robert@linux.intel.com>2011-12-15 18:30:19 +0000
commit76d2a3306ae12f3145f0f545eecfe815bd91c1a5 (patch)
tree10861e8376e622d64308f1598d7be9dcbb7f2e25
parentc4590e59c72ac4253c4ede1badd4684bb616b30c (diff)
downloadclutter-wip/wayland-compositor.tar.gz
test-wayland-surface: Adds basic xwayland supportwip/wayland-compositor
This adds basic support for spawning a headless xwayland server when running test-wayland-surface so that legacy X clients can be composited. Since this depends on the xserver extension protocol which currently lives in the wayland-demos repository this patch adds a --with-wayland-protocols ./configure option so clutter can be told where to find the xml for the xserver extension xml. FIXME: This patch is currently hard coding the patch for xwayland to be /home/bob/local/xserver-xwayland/bin/X
-rw-r--r--configure.ac17
-rw-r--r--tests/interactive/Makefile.am36
-rw-r--r--tests/interactive/test-wayland-surface.c381
3 files changed, 428 insertions, 6 deletions
diff --git a/configure.ac b/configure.ac
index 977fe4d8d..4d88412af 100644
--- a/configure.ac
+++ b/configure.ac
@@ -331,6 +331,22 @@ AC_ARG_ENABLE([wayland-compositor],
[enable_wayland_compositor=no])
])
+dnl The tests/interactive wayland compositor needs to refer to the xserver.xml
+dnl protocol extension that's currently part of the wayland demo compositor repo,
+dnl so we need this option to be told where that can be found.
+AC_ARG_WITH([wayland-protocols],
+ [AS_HELP_STRING([--with-wayland-protocols], [Location for wayland extension protocol specs])],
+ [
+ AC_PATH_PROG([WAYLAND_SCANNER],[wayland-scanner],[no])
+ AS_IF([test "x$WAYLAND_SCANNER" = "xno"],
+ AC_MSG_ERROR([Could not find wayland-scanner in your PATH, required for parsing wayland extension protocols]))
+ AC_SUBST([WAYLAND_SCANNER])
+ have_wayland_protocols_dir="yes"
+ AC_SUBST([WAYLAND_EXTENSION_PROTOCOLS_DIR],$withval)
+ ],
+ [])
+AM_CONDITIONAL(HAVE_WAYLAND_EXTENSION_PROTOCOLS_DIR, [test "x$have_wayland_protocols_dir" = "xyes"])
+
AS_IF([test "x$enable_wayland_compositor" = "xyes"],
[
PKG_CHECK_EXISTS([wayland-server],
@@ -961,7 +977,6 @@ CLUTTER_LIBS="$FLAVOUR_LIBS $CLUTTER_DEPS_LIBS $CLUTTER_PROFILE_LDFLAGS $GLIB_LI
AC_SUBST(CLUTTER_CFLAGS)
AC_SUBST(CLUTTER_LIBS)
-
dnl === GObject-Introspection check ===========================================
GOBJECT_INTROSPECTION_CHECK([gi_req_version])
diff --git a/tests/interactive/Makefile.am b/tests/interactive/Makefile.am
index 357b6bd56..6474b0690 100644
--- a/tests/interactive/Makefile.am
+++ b/tests/interactive/Makefile.am
@@ -65,8 +65,10 @@ UNIT_TESTS += test-pixmap.c
endif
if SUPPORT_WAYLAND_COMPOSITOR
+if HAVE_WAYLAND_EXTENSION_PROTOCOLS_DIR
UNIT_TESTS += test-wayland-surface.c
endif
+endif
if OS_WIN32
SHEXT =
@@ -153,7 +155,20 @@ common_ldadd = $(top_builddir)/clutter/libclutter-@CLUTTER_API_VERSION@.la
noinst_PROGRAMS = test-interactive
-test_interactive_SOURCES = test-main.c test-unit-names.h $(UNIT_TESTS)
+test_interactive_SOURCES = \
+ test-main.c \
+ test-unit-names.h
+
+if SUPPORT_WAYLAND_COMPOSITOR
+if HAVE_WAYLAND_EXTENSION_PROTOCOLS_DIR
+test_interactive_SOURCES += \
+ xserver-protocol.c \
+ xserver-server-protocol.h
+endif
+endif
+
+test_interactive_SOURCES += $(UNIT_TESTS)
+
test_interactive_CFLAGS = $(CLUTTER_CFLAGS) $(MAINTAINER_CFLAGS)
test_interactive_CPPFLAGS = \
-DTESTS_DATADIR=\""$(abs_top_srcdir)/tests/data"\" \
@@ -169,7 +184,16 @@ EXTRA_DIST = \
DISTCLEANFILES = wrapper.sh .gitignore test-unit-names.h
-BUILT_SOURCES = test-unit-names.h wrappers
+BUILT_SOURCES = \
+ test-unit-names.h \
+ wrappers
+
+if SUPPORT_WAYLAND_COMPOSITOR
+if HAVE_WAYLAND_EXTENSION_PROTOCOLS_DIR
+BUILT_SOURCES += xserver-server-protocol.h xserver-protocol.c
+DISTCLEANFILES += xserver-server-protocol.h xserver-protocol.c
+endif
+endif
dist-hook: $(top_builddir)/build/win32/vs9/test-interactive-clutter.vcproj $(top_builddir)/build/win32/vs10/test-interactive-clutter.vcxproj $(top_builddir)/build/win32/vs10/test-interactive-clutter.vcxproj.filters
@@ -219,3 +243,11 @@ EXTRA_DIST += \
$(top_builddir)/build/win32/vs10/test-interactive-clutter.vcxproj.filters
clean-local: clean-wrappers
+
+%-protocol.c : @WAYLAND_EXTENSION_PROTOCOLS_DIR@/%.xml
+ $(AM_V_GEN)$(WAYLAND_SCANNER) code < $< > $@
+%-server-protocol.h : @WAYLAND_EXTENSION_PROTOCOLS_DIR@/%.xml
+ $(AM_V_GEN)$(WAYLAND_SCANNER) server-header < $< > $@
+%-client-protocol.h : @WAYLAND_EXTENSION_PROTOCOLS_DIR@/%.xml'
+ $(AM_V_GEN)$(WAYLAND_SCANNER) client-header < $< > $@
+
diff --git a/tests/interactive/test-wayland-surface.c b/tests/interactive/test-wayland-surface.c
index 2627412f1..5bcc89c40 100644
--- a/tests/interactive/test-wayland-surface.c
+++ b/tests/interactive/test-wayland-surface.c
@@ -6,9 +6,18 @@
#include <glib.h>
#include <sys/time.h>
#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <stdlib.h>
#include <wayland-server.h>
+#include "xserver-server-protocol.h"
+
typedef struct _TWSCompositor TWSCompositor;
typedef struct
@@ -69,6 +78,14 @@ struct _TWSCompositor
GSource *wayland_event_source;
GList *surfaces;
GArray *frame_callbacks;
+
+ int xwayland_display_index;
+ char *xwayland_lockfile;
+ int xwayland_abstract_fd;
+ int xwayland_unix_fd;
+ pid_t xwayland_pid;
+ struct wl_client *xwayland_client;
+ struct wl_resource *xserver_resource;
};
static guint32
@@ -508,6 +525,338 @@ bind_shell (struct wl_client *client,
&tws_shell_interface, id, data);
}
+static char *
+create_lockfile (int display, int *display_out)
+{
+ char *filename;
+ int size;
+ char pid[11];
+ int fd;
+
+ do
+ {
+ char *end;
+ pid_t other;
+
+ filename = g_strdup_printf ("/tmp/.X%d-lock", display);
+ fd = open (filename, O_WRONLY | O_CLOEXEC | O_CREAT | O_EXCL, 0444);
+
+ if (fd < 0 && errno == EEXIST)
+ {
+ fd = open (filename, O_CLOEXEC, O_RDONLY);
+ if (fd < 0 || read (fd, pid, 11) != 11)
+ {
+ const char *msg = strerror (errno);
+ g_warning ("can't read lock file %s: %s", filename, msg);
+ g_free (filename);
+
+ /* ignore error and try the next display number */
+ display++;
+ continue;
+ }
+
+ other = strtol (pid, &end, 0);
+ if (end != pid + 10)
+ {
+ g_warning ("can't parse lock file %s", filename);
+ g_free (filename);
+
+ /* ignore error and try the next display number */
+ display++;
+ continue;
+ }
+
+ if (kill (other, 0) < 0 && errno == ESRCH)
+ {
+ g_warning ("unlinking stale lock file %s", filename);
+ unlink (filename);
+ continue; /* try again */
+ }
+
+ g_free (filename);
+ display++;
+ continue;
+ }
+ else if (fd < 0)
+ {
+ const char *msg = strerror (errno);
+ g_warning ("failed to create lock file %s: %s", filename , msg);
+ g_free (filename);
+ return NULL;
+ }
+
+ break;
+ }
+ while (1);
+
+ /* Subtle detail: we use the pid of the wayland compositor, not the xserver
+ * in the lock file. */
+ size = snprintf (pid, 11, "%10d\n", getpid ());
+ if (size != 11 || write (fd, pid, 11) != 11)
+ {
+ unlink (filename);
+ close (fd);
+ g_warning ("failed to write pid to lock file %s", filename);
+ g_free (filename);
+ return NULL;
+ }
+
+ close (fd);
+
+ *display_out = display;
+ return filename;
+}
+
+static int
+bind_to_abstract_socket (int display)
+{
+ struct sockaddr_un addr;
+ socklen_t size, name_size;
+ int fd;
+
+ fd = socket (PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
+ if (fd < 0)
+ return -1;
+
+ addr.sun_family = AF_LOCAL;
+ name_size = snprintf (addr.sun_path, sizeof addr.sun_path,
+ "%c/tmp/.X11-unix/X%d", 0, display);
+ size = offsetof (struct sockaddr_un, sun_path) + name_size;
+ if (bind (fd, (struct sockaddr *) &addr, size) < 0)
+ {
+ g_warning ("failed to bind to @%s: %s\n",
+ addr.sun_path + 1, strerror (errno));
+ close (fd);
+ return -1;
+ }
+
+ if (listen (fd, 1) < 0)
+ {
+ close (fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+static int
+bind_to_unix_socket (int display)
+{
+ struct sockaddr_un addr;
+ socklen_t size, name_size;
+ int fd;
+
+ fd = socket (PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
+ if (fd < 0)
+ return -1;
+
+ addr.sun_family = AF_LOCAL;
+ name_size = snprintf (addr.sun_path, sizeof addr.sun_path,
+ "/tmp/.X11-unix/X%d", display) + 1;
+ size = offsetof (struct sockaddr_un, sun_path) + name_size;
+ unlink (addr.sun_path);
+ if (bind (fd, (struct sockaddr *) &addr, size) < 0)
+ {
+ char *msg = strerror (errno);
+ g_warning ("failed to bind to %s (%s)\n", addr.sun_path, msg);
+ close (fd);
+ return -1;
+ }
+
+ if (listen (fd, 1) < 0) {
+ unlink (addr.sun_path);
+ close (fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+static gboolean
+start_xwayland (TWSCompositor *compositor)
+{
+ int display = 0;
+ char *lockfile = NULL;
+ int sp[2];
+ pid_t pid;
+
+ do
+ {
+ lockfile = create_lockfile (display, &display);
+ if (!lockfile)
+ {
+ g_warning ("Failed to create an X lock file");
+ return FALSE;
+ }
+
+ compositor->xwayland_abstract_fd = bind_to_abstract_socket (display);
+ if (compositor->xwayland_abstract_fd < 0 ||
+ compositor->xwayland_abstract_fd == EADDRINUSE)
+ {
+ unlink (lockfile);
+ display++;
+ continue;
+ }
+ compositor->xwayland_unix_fd = bind_to_unix_socket (display);
+ if (compositor->xwayland_abstract_fd < 0)
+ {
+ unlink (lockfile);
+ return FALSE;
+ }
+
+ break;
+ }
+ while (1);
+
+ compositor->xwayland_display_index = display;
+ compositor->xwayland_lockfile = lockfile;
+
+ /* We want xwayland to be a wayland client so we make a socketpair to setup a
+ * wayland protocol connection. */
+ if (socketpair (AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sp) < 0)
+ {
+ g_warning ("socketpair failed\n");
+ unlink (lockfile);
+ return 1;
+ }
+
+ switch ((pid = fork()))
+ {
+ case 0:
+ {
+ char *fd_string;
+ char *display_name;
+ /* Make sure the client end of the socket pair doesn't get closed
+ * when we exec xwayland. */
+ int flags = fcntl (sp[1], F_GETFD);
+ if (flags != -1)
+ fcntl (sp[1], F_SETFD, flags & ~FD_CLOEXEC);
+
+ fd_string = g_strdup_printf ("%d", sp[1]);
+ setenv ("WAYLAND_SOCKET", fd_string, 1);
+ g_free (fd_string);
+
+ display_name = g_strdup_printf (":%d",
+ compositor->xwayland_display_index);
+
+ if (execl ("/home/bob/local/xserver-xwayland/bin/X",
+ "/home/bob/local/xserver-xwayland/bin/X",
+ display_name,
+ "-wayland",
+ "-rootless",
+ "-retro",
+ /* FIXME: does it make sense to log to the filesystem by
+ * default? */
+ "-logfile", "/tmp/xwayland.log",
+ "-nolisten", "all",
+ "-terminate",
+ NULL) < 0)
+ {
+ char *msg = strerror (errno);
+ g_warning ("xwayland exec failed: %s", msg);
+ }
+ exit (-1);
+ return FALSE;
+ }
+ default:
+ g_message ("forked X server, pid %d\n", pid);
+
+ close (sp[1]);
+ compositor->xwayland_client =
+ wl_client_create (compositor->wayland_display, sp[0]);
+
+ /* TODO: install a SIGCHILD handler to catch if the server exists/crashes! */
+ break;
+
+ case -1:
+ g_error ("Failed to fork for xwayland server");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+stop_xwayland (TWSCompositor *compositor)
+{
+ char path[256];
+
+ snprintf (path, sizeof path, "/tmp/.X%d-lock",
+ compositor->xwayland_display_index);
+ unlink (path);
+ snprintf (path, sizeof path, "/tmp/.X11-unix/X%d",
+ compositor->xwayland_display_index);
+ unlink (path);
+
+ unlink (compositor->xwayland_lockfile);
+}
+
+static void
+xserver_set_window_id (struct wl_client *client,
+ struct wl_resource *compositor_resource,
+ struct wl_resource *surface_resource,
+ guint32 id)
+{
+#if 0
+ TWSCompositor *compositor = compositor_resource->data;
+ struct wlsc_wm *wm = wxs->wm;
+ struct wl_surface *surface = surface_resource->data;
+ struct wlsc_wm_window *window;
+
+ if (client != wxs->client)
+ return;
+
+ window = wl_hash_table_lookup (wm->window_hash, id);
+ if (window == NULL)
+ {
+ g_warning ("set_window_id for unknown window %d", id);
+ return;
+ }
+
+ g_message ("set_window_id %d for surface %p", id, surface);
+
+ window->surface = (struct wlsc_surface *) surface;
+ window->surface_destroy_listener.func = surface_destroy;
+ wl_list_insert(surface->resource.destroy_listener_list.prev,
+ &window->surface_destroy_listener.link);
+#endif
+ g_message ("TODO: xserver_set_window_id");
+}
+
+static const struct xserver_interface xserver_implementation = {
+ xserver_set_window_id
+};
+
+static void
+bind_xserver (struct wl_client *client,
+ void *data,
+ guint32 version,
+ guint32 id)
+{
+ TWSCompositor *compositor = data;
+
+ /* If it's a different client than the xserver we launched,
+ * don't start the wm. */
+ if (client != compositor->xwayland_client)
+ return;
+
+ compositor->xserver_resource =
+ wl_client_add_object (client, &xserver_interface,
+ &xserver_implementation, id,
+ compositor);
+
+ /* TODO: Become the window manager for the xserver */
+
+ wl_resource_post_event (compositor->xserver_resource,
+ XSERVER_LISTEN_SOCKET,
+ compositor->xwayland_abstract_fd);
+
+ wl_resource_post_event (compositor->xserver_resource,
+ XSERVER_LISTEN_SOCKET,
+ compositor->xwayland_unix_fd);
+ g_warning ("bind_xserver");
+}
+
G_MODULE_EXPORT int
test_wayland_surface_main (int argc, char **argv)
{
@@ -545,7 +894,7 @@ test_wayland_surface_main (int argc, char **argv)
if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS)
return 1;
- compositor.stage = clutter_stage_get_default ();
+ compositor.stage = clutter_stage_new ();
clutter_stage_set_user_resizable (CLUTTER_STAGE (compositor.stage), FALSE);
g_signal_connect_after (compositor.stage, "paint",
G_CALLBACK (paint_finished_cb), &compositor);
@@ -554,14 +903,40 @@ test_wayland_surface_main (int argc, char **argv)
if (wl_display_add_global (compositor.wayland_display, &wl_shell_interface,
&compositor, bind_shell) == NULL)
- g_error ("Failed to register a global shell object");
+ {
+ stop_xwayland (&compositor);
+ g_error ("Failed to register a global shell object");
+ }
clutter_actor_show (compositor.stage);
if (wl_display_add_socket (compositor.wayland_display, "wayland-0"))
- g_error ("Failed to create socket");
+ {
+ stop_xwayland (&compositor);
+ g_error ("Failed to create socket");
+ }
+
+ wl_display_add_global (compositor.wayland_display,
+ &xserver_interface,
+ &compositor,
+ bind_xserver);
+
+ /* XXX: It's important that we only try and start xwayland after we have
+ * initialized EGL because EGL implements the "wl_drm" interface which
+ * xwayland requires to determine what drm device name it should use.
+ *
+ * By waiting until we've shown the stage above we ensure that the underlying
+ * GL resources for the surface have also been allocated and so EGL must be
+ * initialized by this point.
+ */
+
+ if (!start_xwayland (&compositor))
+ return 1;
+
g_main_loop_run (loop);
+ stop_xwayland (&compositor);
+
return 0;
}