[mutter/wip/wayland: 2/2] wayland: Adds basic hybrid X + Wayland support
- From: Robert Bragg <rbragg src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [mutter/wip/wayland: 2/2] wayland: Adds basic hybrid X + Wayland support
- Date: Tue, 10 Jan 2012 17:54:32 +0000 (UTC)
commit 8e422cc9ff5e280adbed33fab78c0d19142d8fac
Author: Robert Bragg <robert linux intel com>
Date: Sat Jan 7 22:21:32 2012 +0000
wayland: Adds basic hybrid X + Wayland support
This adds support for running mutter as a hybrid X and Wayland
compositor. It runs a headless XWayland server for X applications
that presents wayland surfaces back to mutter which mutter can then
composite.
At this point no input is supported
The path for xwayland is simply hard coded to
/home/bob/local/xserver-xwayland/bin/X so anyone trying this
currently needs to modify wayland/meta-wayland.c!
The following special branches are required to try and run this:
repo: git://git.gnome.org/cogl.git
branch: wip/fosdem-2012
repo: git://git.gnome.org/clutter.git
branch: wip/fosdem-2012
repo: git://anongit.freedesktop.org/~krh/xserver
branch: wayland-1.10
XXX: After checking this out you must then run
git revert 7889930ad9
configure.in | 2 +-
src/Makefile.am | 22 +
src/compositor/compositor-private.h | 2 +
src/compositor/compositor.c | 78 ++
src/compositor/meta-plugin-manager.c | 2 +
src/compositor/meta-shaped-texture.c | 77 ++-
src/compositor/meta-shaped-texture.h | 11 +-
src/compositor/meta-window-actor-private.h | 12 +
src/compositor/meta-window-actor.c | 86 ++-
src/compositor/meta-window-group.c | 5 +-
src/core/display.c | 27 +-
src/core/main.c | 96 ++-
src/core/screen-private.h | 2 +
src/core/screen.c | 26 +-
src/meta/compositor-mutter.h | 2 +
src/wayland/meta-wayland-private.h | 128 ++++
src/wayland/meta-wayland.c | 1098 ++++++++++++++++++++++++++++
17 files changed, 1640 insertions(+), 36 deletions(-)
---
diff --git a/configure.in b/configure.in
index 210771b..fa2409b 100644
--- a/configure.in
+++ b/configure.in
@@ -15,7 +15,7 @@ AC_INIT([mutter], [mutter_version],
AC_CONFIG_SRCDIR(src/core/display.c)
AC_CONFIG_HEADERS(config.h)
-AM_INIT_AUTOMAKE([1.11 no-dist-gzip dist-xz tar-ustar])
+AM_INIT_AUTOMAKE([1.11 foreign no-dist-gzip dist-xz tar-ustar])
m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])],)
AM_MAINTAINER_MODE([enable])
diff --git a/src/Makefile.am b/src/Makefile.am
index 8759f76..6dd815c 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -11,6 +11,7 @@ INCLUDES= \
-I$(srcdir)/core \
-I$(srcdir)/ui \
-I$(srcdir)/compositor \
+ -I$(srcdir)/wayland \
-DMUTTER_LIBEXECDIR=\"$(libexecdir)\" \
-DHOST_ALIAS=\"@HOST_ALIAS \" \
-DMUTTER_LOCALEDIR=\"$(prefix)/@DATADIRNAME@/locale\" \
@@ -30,6 +31,12 @@ mutter_built_sources = \
mutter-enum-types.h \
mutter-enum-types.c
+if HAVE_WAYLAND
+mutter_built_sources += \
+ wayland/xserver-protocol.c \
+ wayland/xserver-server-protocol.h
+endif
+
libmutter_la_SOURCES = \
core/async-getprop.c \
core/async-getprop.h \
@@ -154,6 +161,12 @@ libmutter_la_SOURCES = \
ui/preview-widget.c \
$(mutter_built_sources)
+if HAVE_WAYLAND
+libmutter_la_SOURCES += \
+ wayland/meta-wayland.c \
+ wayland/meta-wayland-private.h
+endif
+
libmutter_la_LDFLAGS = -no-undefined
libmutter_la_LIBADD = $(MUTTER_LIBS)
@@ -329,3 +342,12 @@ mutter-enum-types.c: stamp-mutter-enum-types.h mutter-enum-types.c.in
$(libmutterinclude_base_headers) ) >> xgen-tetc && \
cp xgen-tetc mutter-enum-types.c && \
rm -f xgen-tetc
+
+if HAVE_WAYLAND
+wayland/%-protocol.c : @WAYLAND_EXTENSION_PROTOCOLS_DIR@/%.xml
+ $(AM_V_GEN)$(WAYLAND_SCANNER) code < $< > $@
+wayland/%-server-protocol.h : @WAYLAND_EXTENSION_PROTOCOLS_DIR@/%.xml
+ $(AM_V_GEN)$(WAYLAND_SCANNER) server-header < $< > $@
+wayland/%-client-protocol.h : @WAYLAND_EXTENSION_PROTOCOLS_DIR@/%.xml'
+ $(AM_V_GEN)$(WAYLAND_SCANNER) client-header < $< > $@
+endif
diff --git a/src/compositor/compositor-private.h b/src/compositor/compositor-private.h
index 23f639f..fc5b97c 100644
--- a/src/compositor/compositor-private.h
+++ b/src/compositor/compositor-private.h
@@ -42,9 +42,11 @@ struct _MetaCompScreen
GHashTable *windows_by_xid;
Window output;
+#ifndef HAVE_WAYLAND
/* Used for unredirecting fullscreen windows */
guint disable_unredirect_count;
MetaWindowActor *unredirected_window;
+#endif
/* Before we create the output window */
XserverRegion pending_input_region;
diff --git a/src/compositor/compositor.c b/src/compositor/compositor.c
index 76bf8f5..611ed8e 100644
--- a/src/compositor/compositor.c
+++ b/src/compositor/compositor.c
@@ -18,6 +18,9 @@
#include "meta-background-actor-private.h"
#include "window-private.h" /* to check window->hidden */
#include "display-private.h" /* for meta_display_lookup_x_window() */
+#ifdef HAVE_WAYLAND
+#include "meta-wayland-private.h"
+#endif
#include <X11/extensions/shape.h>
#include <X11/extensions/Xcomposite.h>
@@ -92,6 +95,7 @@ add_win (MetaWindow *window)
sync_actor_stacking (info);
}
+#ifndef HAVE_WAYLAND
static void
process_damage (MetaCompositor *compositor,
XDamageNotifyEvent *event,
@@ -108,6 +112,7 @@ process_damage (MetaCompositor *compositor,
meta_window_actor_process_damage (window_actor, event);
}
+#endif
static void
process_property_notify (MetaCompositor *compositor,
@@ -149,6 +154,7 @@ process_property_notify (MetaCompositor *compositor,
DEBUG_TRACE ("process_property_notify: unknown\n");
}
+#ifndef HAVE_WAYLAND
static Window
get_output_window (MetaScreen *screen)
{
@@ -179,6 +185,7 @@ get_output_window (MetaScreen *screen)
return output;
}
+#endif /* HAVE_WAYLAND */
/**
* meta_get_stage_for_screen:
@@ -269,6 +276,7 @@ meta_get_window_actors (MetaScreen *screen)
return info->windows;
}
+#ifndef HAVE_WAYLAND
static void
do_set_stage_input_region (MetaScreen *screen,
XserverRegion region)
@@ -287,11 +295,18 @@ do_set_stage_input_region (MetaScreen *screen,
meta_display_add_ignored_crossing_serial (display, XNextRequest (xdpy));
XFixesSetWindowShapeRegion (xdpy, info->output, ShapeInput, 0, 0, region);
}
+#endif
void
meta_set_stage_input_region (MetaScreen *screen,
XserverRegion region)
{
+ /* As a wayland compositor we can simply ignore all this trickery
+ * for setting an input region on the stage for capturing events in
+ * clutter since all input comes to us first and we get to choose
+ * who else see it.
+ */
+#ifndef HAVE_WAYLAND
MetaCompScreen *info = meta_screen_get_compositor_data (screen);
MetaDisplay *display = meta_screen_get_display (screen);
Display *xdpy = meta_display_get_xdisplay (display);
@@ -315,6 +330,7 @@ meta_set_stage_input_region (MetaScreen *screen,
XFixesCopyRegion (xdpy, info->pending_input_region, region);
}
}
+#endif
}
void
@@ -450,6 +466,7 @@ meta_compositor_manage_screen (MetaCompositor *compositor,
MetaScreen *screen)
{
MetaCompScreen *info;
+#ifndef HAVE_WAYLAND
MetaDisplay *display = meta_screen_get_display (screen);
Display *xdisplay = meta_display_get_xdisplay (display);
int screen_number = meta_screen_get_screen_number (screen);
@@ -460,11 +477,20 @@ meta_compositor_manage_screen (MetaCompositor *compositor,
long event_mask;
guint n_retries;
guint max_retries;
+#else
+ MetaWaylandCompositor *wayland_compositor;
+#endif
/* Check if the screen is already managed */
if (meta_screen_get_compositor_data (screen))
return;
+ /* If we're running with wayland, connected to a headless xwayland
+ * server then all the windows are implicitly redirected offscreen
+ * already and it would generate an error to try and explicitly
+ * redirect them via XCompositeRedirectSubwindows() */
+#ifndef HAVE_WAYLAND
+
if (meta_get_replace_current_wm ())
max_retries = 5;
else
@@ -496,6 +522,7 @@ meta_compositor_manage_screen (MetaCompositor *compositor,
n_retries++;
g_usleep (G_USEC_PER_SEC);
}
+#endif
info = g_new0 (MetaCompScreen, 1);
/*
@@ -504,7 +531,13 @@ meta_compositor_manage_screen (MetaCompositor *compositor,
* We have to initialize info->pending_input_region to an empty region explicitly,
* because None value is used to mean that the whole screen is an input region.
*/
+#ifndef HAVE_WAYLAND
info->pending_input_region = XFixesCreateRegion (xdisplay, NULL, 0);
+#else
+ /* Stage input region trickery isn't needed when we're running as a
+ * wayland compositor. */
+ info->pending_input_region = None;
+#endif
info->screen = screen;
@@ -515,6 +548,12 @@ meta_compositor_manage_screen (MetaCompositor *compositor,
meta_screen_set_cm_selection (screen);
+ /* We will have already created a stage if running as a wayland
+ * compositor... */
+#ifdef HAVE_WAYLAND
+ wayland_compositor = meta_wayland_compositor_get_default ();
+ info->stage = wayland_compositor->stage;
+#else
info->stage = clutter_stage_new ();
meta_screen_get_size (screen, &width, &height);
@@ -540,6 +579,8 @@ meta_compositor_manage_screen (MetaCompositor *compositor,
XSelectInput (xdisplay, xwin, event_mask);
+#endif /* HAVE_WAYLAND */
+
info->window_group = meta_window_group_new (screen);
info->background_actor = meta_background_actor_new_for_screen (screen);
info->overlay_group = clutter_group_new ();
@@ -561,6 +602,8 @@ meta_compositor_manage_screen (MetaCompositor *compositor,
meta_plugin_manager_get (screen);
meta_plugin_manager_initialize (info->plugin_mgr);
+#ifndef HAVE_WAYLAND
+
/*
* Delay the creation of the overlay window as long as we can, to avoid
* blanking out the screen. This means that during the plugin loading, the
@@ -588,6 +631,15 @@ meta_compositor_manage_screen (MetaCompositor *compositor,
info->pending_input_region = None;
}
+#else /* HAVE_WAYLAND */
+
+ /* NB: When running as a wayland compositor we don't need an X composite
+ * overlay window, and we don't need to play any input region tricks
+ * to redirect events into clutter. */
+ info->output = None;
+
+#endif /* HAVE_WAYLAND */
+
clutter_actor_show (info->overlay_group);
clutter_actor_show (info->stage);
}
@@ -596,6 +648,7 @@ void
meta_compositor_unmanage_screen (MetaCompositor *compositor,
MetaScreen *screen)
{
+#ifndef HAVE_WAYLAND
MetaDisplay *display = meta_screen_get_display (screen);
Display *xdisplay = meta_display_get_xdisplay (display);
Window xroot = meta_screen_get_xroot (screen);
@@ -604,8 +657,10 @@ meta_compositor_unmanage_screen (MetaCompositor *compositor,
* before giving up the window manager selection or the next
* window manager won't be able to redirect subwindows */
XCompositeUnredirectSubwindows (xdisplay, xroot, CompositeRedirectManual);
+#endif
}
+#ifndef HAVE_WAYLAND
/*
* Shapes the cow so that the given window is exposed,
* when metaWindow is NULL it clears the shape again
@@ -646,6 +701,7 @@ meta_shape_cow_for_window (MetaScreen *screen,
XFixesDestroyRegion (xdisplay, output_region);
}
}
+#endif
void
meta_compositor_add_window (MetaCompositor *compositor,
@@ -667,14 +723,17 @@ meta_compositor_remove_window (MetaCompositor *compositor,
MetaWindow *window)
{
MetaWindowActor *window_actor = NULL;
+#ifndef HAVE_WAYLAND
MetaScreen *screen;
MetaCompScreen *info;
+#endif
DEBUG_TRACE ("meta_compositor_remove_window\n");
window_actor = META_WINDOW_ACTOR (meta_window_get_compositor_private (window));
if (!window_actor)
return;
+#ifndef HAVE_WAYLAND
screen = meta_window_get_screen (window);
info = meta_screen_get_compositor_data (screen);
@@ -685,6 +744,7 @@ meta_compositor_remove_window (MetaCompositor *compositor,
NULL);
info->unredirected_window = NULL;
}
+#endif
meta_window_actor_destroy (window_actor);
}
@@ -788,6 +848,7 @@ meta_compositor_process_event (MetaCompositor *compositor,
break;
default:
+#ifndef HAVE_WAYLAND
if (event->type == meta_display_get_damage_event_base (compositor->display) + XDamageNotify)
{
/* Core code doesn't handle damage events, so we need to extract the MetaWindow
@@ -802,13 +863,16 @@ meta_compositor_process_event (MetaCompositor *compositor,
DEBUG_TRACE ("meta_compositor_process_event (process_damage)\n");
process_damage (compositor, (XDamageNotifyEvent *) event, window);
}
+#endif
break;
}
+#ifndef HAVE_WAYLAND
/* Clutter needs to know about MapNotify events otherwise it will
think the stage is invisible */
if (event->type == MapNotify)
clutter_x11_handle_event (event);
+#endif
/* The above handling is basically just "observing" the events, so we return
* FALSE to indicate that the event should not be filtered out; if we have
@@ -1141,6 +1205,7 @@ meta_compositor_sync_screen_size (MetaCompositor *compositor,
guint width,
guint height)
{
+#ifndef HAVE_WAYLAND
MetaDisplay *display = meta_screen_get_display (screen);
MetaCompScreen *info = meta_screen_get_compositor_data (screen);
Display *xdisplay;
@@ -1159,18 +1224,28 @@ meta_compositor_sync_screen_size (MetaCompositor *compositor,
meta_verbose ("Changed size for stage on screen %d to %dx%d\n",
meta_screen_get_screen_number (screen),
width, height);
+#else
+ /* It's not clear at the moment how we will be dealing with screen
+ * resizing as a Wayland compositor so for now just abort if we
+ * hit this code. */
+ g_critical ("Unexpected call to meta_compositor_sync_screen_size() "
+ "when running as a wayland compositor");
+#endif
}
static void
pre_paint_windows (MetaCompScreen *info)
{
GList *l;
+#ifndef HAVE_WAYLAND
MetaWindowActor *top_window;
MetaWindowActor *expected_unredirected_window = NULL;
+#endif
if (info->windows == NULL)
return;
+#ifndef HAVE_WAYLAND
top_window = g_list_last (info->windows)->data;
if (meta_window_actor_should_unredirect (top_window) &&
@@ -1195,6 +1270,7 @@ pre_paint_windows (MetaCompScreen *info)
info->unredirected_window = expected_unredirected_window;
}
+#endif
for (l = info->windows; l; l = l->next)
meta_window_actor_pre_paint (l->data);
@@ -1298,6 +1374,7 @@ meta_get_overlay_window (MetaScreen *screen)
return info->output;
}
+#ifndef HAVE_WAYLAND
/**
* meta_disable_unredirect_for_screen:
* @screen: a #MetaScreen
@@ -1328,6 +1405,7 @@ meta_enable_unredirect_for_screen (MetaScreen *screen)
if (info != NULL)
info->disable_unredirect_count = MAX(0, info->disable_unredirect_count - 1);
}
+#endif
#define FLASH_TIME_MS 50
diff --git a/src/compositor/meta-plugin-manager.c b/src/compositor/meta-plugin-manager.c
index bc4b9c7..9c3effb 100644
--- a/src/compositor/meta-plugin-manager.c
+++ b/src/compositor/meta-plugin-manager.c
@@ -477,8 +477,10 @@ meta_plugin_manager_xevent_filter (MetaPluginManager *plugin_mgr,
l = l->next;
}
+#ifndef HAVE_WAYLAND
if (!have_plugin_xevent_func)
return clutter_x11_handle_event (xev) != CLUTTER_X11_FILTER_CONTINUE;
+#endif
return FALSE;
}
diff --git a/src/compositor/meta-shaped-texture.c b/src/compositor/meta-shaped-texture.c
index 7aa8f9e..4e81b7a 100644
--- a/src/compositor/meta-shaped-texture.c
+++ b/src/compositor/meta-shaped-texture.c
@@ -28,29 +28,41 @@
#include "meta-shaped-texture.h"
#include "meta-texture-tower.h"
#include "meta-texture-rectangle.h"
+#include "meta-wayland-private.h"
#include <clutter/clutter.h>
+#ifdef HAVE_WAYLAND
+#include <clutter/wayland/clutter-wayland-surface.h>
+#endif
#include <cogl/cogl.h>
#include <string.h>
static void meta_shaped_texture_dispose (GObject *object);
+#ifndef HAVE_WAYLAND
static void meta_shaped_texture_notify (GObject *object,
GParamSpec *pspec);
+#endif
static void meta_shaped_texture_paint (ClutterActor *actor);
static void meta_shaped_texture_pick (ClutterActor *actor,
const ClutterColor *color);
+#ifndef HAVE_WAYLAND
static void meta_shaped_texture_update_area (ClutterX11TexturePixmap *texture,
int x,
int y,
int width,
int height);
+#endif
static void meta_shaped_texture_dirty_mask (MetaShapedTexture *stex);
G_DEFINE_TYPE (MetaShapedTexture, meta_shaped_texture,
+#ifdef HAVE_WAYLAND
+ CLUTTER_WAYLAND_TYPE_SURFACE);
+#else
CLUTTER_X11_TYPE_TEXTURE_PIXMAP);
+#endif
#define META_SHAPED_TEXTURE_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), META_TYPE_SHAPED_TEXTURE, \
@@ -81,15 +93,21 @@ meta_shaped_texture_class_init (MetaShapedTextureClass *klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
ClutterActorClass *actor_class = (ClutterActorClass *) klass;
+#ifndef HAVE_WAYLAND
ClutterX11TexturePixmapClass *x11_texture_class = (ClutterX11TexturePixmapClass *) klass;
+#endif
gobject_class->dispose = meta_shaped_texture_dispose;
+#ifndef HAVE_WAYLAND
gobject_class->notify = meta_shaped_texture_notify;
+#endif
actor_class->paint = meta_shaped_texture_paint;
actor_class->pick = meta_shaped_texture_pick;
+#ifndef HAVE_WAYLAND
x11_texture_class->update_area = meta_shaped_texture_update_area;
+#endif
g_type_class_add_private (klass, sizeof (MetaShapedTexturePrivate));
}
@@ -140,6 +158,7 @@ meta_shaped_texture_dispose (GObject *object)
G_OBJECT_CLASS (meta_shaped_texture_parent_class)->dispose (object);
}
+#ifndef HAVE_WAYLAND
static void
meta_shaped_texture_notify (GObject *object,
GParamSpec *pspec)
@@ -164,6 +183,7 @@ meta_shaped_texture_notify (GObject *object,
clutter_texture_get_cogl_texture (CLUTTER_TEXTURE (stex)));
}
}
+#endif
static void
meta_shaped_texture_dirty_mask (MetaShapedTexture *stex)
@@ -306,9 +326,15 @@ install_overlay_path (MetaShapedTexture *stex,
static void
meta_shaped_texture_ensure_mask (MetaShapedTexture *stex)
{
+#ifdef HAVE_WAYLAND
+#warning "FIXME: support meta_shaped_texture_ensure_mask()"
+ return;
+#else
+
MetaShapedTexturePrivate *priv = stex->priv;
- CoglHandle paint_tex;
guint tex_width, tex_height;
+#ifndef HAVE_WAYLAND
+ CoglHandle paint_tex;
paint_tex = clutter_texture_get_cogl_texture (CLUTTER_TEXTURE (stex));
@@ -317,6 +343,13 @@ meta_shaped_texture_ensure_mask (MetaShapedTexture *stex)
tex_width = cogl_texture_get_width (paint_tex);
tex_height = cogl_texture_get_height (paint_tex);
+#else
+ gfloat width, height;
+ clutter_actor_get_preferred_width (CLUTTER_ACTOR (stex), -1, NULL, &width);
+ clutter_actor_get_preferred_height (CLUTTER_ACTOR (stex), -1, NULL, &height);
+ tex_width = width;
+ tex_height = height;
+#endif
/* If the mask texture we have was created for a different size then
recreate it */
@@ -411,11 +444,13 @@ meta_shaped_texture_ensure_mask (MetaShapedTexture *stex)
priv->mask_width = tex_width;
priv->mask_height = tex_height;
}
+#endif /* HAVE_WAYLAND */
}
static void
meta_shaped_texture_paint (ClutterActor *actor)
{
+#ifndef HAVE_WAYLAND
MetaShapedTexture *stex = (MetaShapedTexture *) actor;
MetaShapedTexturePrivate *priv = stex->priv;
CoglHandle paint_tex;
@@ -555,12 +590,16 @@ meta_shaped_texture_paint (ClutterActor *actor)
cogl_rectangle (0, 0,
alloc.x2 - alloc.x1,
alloc.y2 - alloc.y1);
+#else /* HAVE_WAYLAND */
+ CLUTTER_ACTOR_CLASS (meta_shaped_texture_parent_class)->paint (actor);
+#endif /* HAVE_WAYLAND */
}
static void
meta_shaped_texture_pick (ClutterActor *actor,
const ClutterColor *color)
{
+#ifndef HAVE_WAYLAND
MetaShapedTexture *stex = (MetaShapedTexture *) actor;
MetaShapedTexturePrivate *priv = stex->priv;
@@ -599,8 +638,12 @@ meta_shaped_texture_pick (ClutterActor *actor,
alloc.y2 - alloc.y1,
0, 0, 1, 1);
}
+#else /* HAVE_WAYLAND */
+ CLUTTER_ACTOR_CLASS (meta_shaped_texture_parent_class)->pick (actor, color);
+#endif /* HAVE_WAYLAND */
}
+#ifndef HAVE_WAYLAND
static void
meta_shaped_texture_update_area (ClutterX11TexturePixmap *texture,
int x,
@@ -616,13 +659,39 @@ meta_shaped_texture_update_area (ClutterX11TexturePixmap *texture,
meta_texture_tower_update_area (priv->paint_tower, x, y, width, height);
}
+#endif
ClutterActor *
-meta_shaped_texture_new (void)
+meta_shaped_texture_new (Window xwindow)
{
- ClutterActor *self = g_object_new (META_TYPE_SHAPED_TEXTURE, NULL);
+ MetaWaylandSurface *surface = meta_wayland_lookup_surface_for_xid (xwindow);
- return self;
+ if (surface)
+ {
+ ClutterWaylandSurface *wayland_surface;
+ ClutterActor *actor =
+ g_object_new (META_TYPE_SHAPED_TEXTURE,
+ "surface", surface->wayland_surface, NULL);
+
+ /* XXX: This is a bit messy but meta-wayland.c wants a reference
+ * to the corresponding surface actor so that it can pass on
+ * damage events.
+ *
+ * TODO: find a neater way.
+ */
+ surface->actor = actor;
+
+ if (surface->buffer)
+ {
+ wayland_surface = CLUTTER_WAYLAND_SURFACE (surface->actor);
+ clutter_wayland_surface_attach_buffer (wayland_surface,
+ surface->buffer->wayland_buffer,
+ NULL);
+ }
+ return actor;
+ }
+ else
+ return g_object_new (META_TYPE_SHAPED_TEXTURE, NULL);
}
void
diff --git a/src/compositor/meta-shaped-texture.h b/src/compositor/meta-shaped-texture.h
index a36fe6b..2df479d 100644
--- a/src/compositor/meta-shaped-texture.h
+++ b/src/compositor/meta-shaped-texture.h
@@ -31,6 +31,10 @@
#include <clutter/clutter.h>
#include <clutter/x11/clutter-x11.h>
+#ifdef HAVE_WAYLAND
+#include <wayland-server.h>
+#endif
+
G_BEGIN_DECLS
#define META_TYPE_SHAPED_TEXTURE (meta_shaped_texture_get_type())
@@ -58,7 +62,7 @@ struct _MetaShapedTexture
GType meta_shaped_texture_get_type (void) G_GNUC_CONST;
-ClutterActor *meta_shaped_texture_new (void);
+ClutterActor *meta_shaped_texture_new (Window xwindow);
void meta_shaped_texture_set_create_mipmaps (MetaShapedTexture *stex,
gboolean create_mipmaps);
@@ -78,6 +82,11 @@ void meta_shaped_texture_set_overlay_path (MetaShapedTexture *stex,
void meta_shaped_texture_set_clip_region (MetaShapedTexture *stex,
cairo_region_t *clip_region);
+#ifdef HAVE_WAYLAND
+void meta_shaped_texture_set_wayland_surface (MetaShapedTexture *stex,
+ struct wl_surface *surface);
+#endif
+
G_END_DECLS
#endif /* __META_SHAPED_TEXTURE_H__ */
diff --git a/src/compositor/meta-window-actor-private.h b/src/compositor/meta-window-actor-private.h
index 97e3140..9343722 100644
--- a/src/compositor/meta-window-actor-private.h
+++ b/src/compositor/meta-window-actor-private.h
@@ -8,6 +8,10 @@
#include <X11/extensions/Xdamage.h>
#include <meta/compositor-mutter.h>
+#ifdef HAVE_WAYLAND
+#include <wayland-server.h>
+#endif
+
MetaWindowActor *meta_window_actor_new (MetaWindow *window);
void meta_window_actor_destroy (MetaWindowActor *self);
@@ -24,14 +28,18 @@ void meta_window_actor_unmaximize (MetaWindowActor *self,
MetaRectangle *old_rect,
MetaRectangle *new_rect);
+#ifndef HAVE_WAYLAND
void meta_window_actor_process_damage (MetaWindowActor *self,
XDamageNotifyEvent *event);
+#endif
void meta_window_actor_pre_paint (MetaWindowActor *self);
void meta_window_actor_invalidate_shadow (MetaWindowActor *self);
+#ifndef HAVE_WAYLAND
void meta_window_actor_set_redirected (MetaWindowActor *self, gboolean state);
+#endif
gboolean meta_window_actor_should_unredirect (MetaWindowActor *self);
@@ -57,4 +65,8 @@ void meta_window_actor_reset_visible_regions (MetaWindowActor *self);
void meta_window_actor_effect_completed (MetaWindowActor *actor,
gulong event);
+#ifdef HAVE_WAYLAND
+ClutterActor *meta_window_actor_get_shaped_texture (MetaWindowActor *self);
+#endif
+
#endif /* META_WINDOW_ACTOR_PRIVATE_H */
diff --git a/src/compositor/meta-window-actor.c b/src/compositor/meta-window-actor.c
index 16d136f..66bbb29 100644
--- a/src/compositor/meta-window-actor.c
+++ b/src/compositor/meta-window-actor.c
@@ -24,6 +24,10 @@
#include "meta-shadow-factory-private.h"
#include "meta-shaped-texture.h"
#include "meta-window-actor-private.h"
+#ifdef HAVE_WAYLAND
+#include "meta-wayland-private.h"
+#include <clutter/wayland/clutter-wayland-surface.h>
+#endif
enum {
POSITION_CHANGED,
@@ -56,9 +60,11 @@ struct _MetaWindowActorPrivate
MetaShadow *focused_shadow;
MetaShadow *unfocused_shadow;
+#ifndef HAVE_WAYLAND
Pixmap back_pixmap;
Damage damage;
+#endif
guint8 opacity;
guint8 shadow_opacity;
@@ -75,8 +81,10 @@ struct _MetaWindowActorPrivate
/* Extracted size-invariant shape used for shadows */
MetaWindowShape *shadow_shape;
+#ifndef HAVE_WAYLAND
gint last_width;
gint last_height;
+#endif
MetaFrameBorders last_borders;
gint freeze_count;
@@ -100,22 +108,27 @@ struct _MetaWindowActorPrivate
guint disposed : 1;
guint redecorating : 1;
+#ifndef HAVE_WAYLAND
guint needs_damage_all : 1;
guint received_damage : 1;
guint needs_pixmap : 1;
+#endif
guint needs_reshape : 1;
guint recompute_focused_shadow : 1;
guint recompute_unfocused_shadow : 1;
+#ifndef HAVE_WAYLAND
guint size_changed : 1;
+#endif
guint needs_destroy : 1;
guint no_shadow : 1;
guint no_more_x_calls : 1;
-
+#ifndef HAVE_WAYLAND
guint unredirected : 1;
+#endif
};
enum
@@ -150,7 +163,9 @@ static gboolean meta_window_actor_get_paint_volume (ClutterActor *actor,
ClutterPaintVolume *volume);
+#ifndef HAVE_WAYLAND
static void meta_window_actor_detach (MetaWindowActor *self);
+#endif
static gboolean meta_window_actor_has_shadow (MetaWindowActor *self);
static void meta_window_actor_clear_shape_region (MetaWindowActor *self);
@@ -262,9 +277,11 @@ window_decorated_notify (MetaWindow *mw,
MetaWindowActor *self = META_WINDOW_ACTOR (data);
MetaWindowActorPrivate *priv = self->priv;
MetaFrame *frame = meta_window_get_frame (mw);
+#ifndef HAVE_WAYLAND
MetaScreen *screen = priv->screen;
MetaDisplay *display = meta_screen_get_display (screen);
Display *xdisplay = meta_display_get_xdisplay (display);
+#endif
Window new_xwindow;
/*
@@ -278,6 +295,7 @@ window_decorated_notify (MetaWindow *mw,
else
new_xwindow = meta_window_get_xwindow (mw);
+#ifndef HAVE_WAYLAND
meta_window_actor_detach (self);
/*
@@ -291,6 +309,7 @@ window_decorated_notify (MetaWindow *mw,
meta_error_trap_pop (display);
priv->damage = None;
}
+#endif
g_free (priv->desc);
priv->desc = NULL;
@@ -323,8 +342,10 @@ meta_window_actor_constructed (GObject *object)
Display *xdisplay = meta_display_get_xdisplay (display);
XRenderPictFormat *format;
+#ifndef HAVE_WAYLAND
priv->damage = XDamageCreate (xdisplay, xwindow,
XDamageReportBoundingBox);
+#endif
format = XRenderFindVisualFormat (xdisplay, window->xvisual);
@@ -333,7 +354,7 @@ meta_window_actor_constructed (GObject *object)
if (!priv->actor)
{
- priv->actor = meta_shaped_texture_new ();
+ priv->actor = meta_shaped_texture_new (xwindow);
clutter_container_add_actor (CLUTTER_CONTAINER (self), priv->actor);
@@ -370,8 +391,10 @@ meta_window_actor_dispose (GObject *object)
MetaWindowActor *self = META_WINDOW_ACTOR (object);
MetaWindowActorPrivate *priv = self->priv;
MetaScreen *screen;
+#ifndef HAVE_WAYLAND
MetaDisplay *display;
Display *xdisplay;
+#endif
MetaCompScreen *info;
if (priv->disposed)
@@ -380,11 +403,13 @@ meta_window_actor_dispose (GObject *object)
priv->disposed = TRUE;
screen = priv->screen;
+ info = meta_screen_get_compositor_data (screen);
+#ifndef HAVE_WAYLAND
display = meta_screen_get_display (screen);
xdisplay = meta_display_get_xdisplay (display);
- info = meta_screen_get_compositor_data (screen);
meta_window_actor_detach (self);
+#endif
meta_window_actor_clear_shape_region (self);
meta_window_actor_clear_bounding_region (self);
@@ -414,6 +439,7 @@ meta_window_actor_dispose (GObject *object)
priv->shadow_shape = NULL;
}
+#ifndef HAVE_WAYLAND
if (priv->damage != None)
{
meta_error_trap_push (display);
@@ -422,6 +448,7 @@ meta_window_actor_dispose (GObject *object)
priv->damage = None;
}
+#endif
info->windows = g_list_remove (info->windows, (gconstpointer) self);
@@ -940,6 +967,7 @@ meta_window_actor_freeze (MetaWindowActor *self)
self->priv->freeze_count++;
}
+#ifndef HAVE_WAYLAND
static void
meta_window_actor_damage_all (MetaWindowActor *self)
{
@@ -967,6 +995,7 @@ meta_window_actor_damage_all (MetaWindowActor *self)
priv->needs_damage_all = FALSE;
}
+#endif
static void
meta_window_actor_thaw (MetaWindowActor *self)
@@ -983,12 +1012,14 @@ meta_window_actor_thaw (MetaWindowActor *self)
if (self->priv->freeze_count)
return;
+#ifndef HAVE_WAYLAND
/* Since we ignore damage events while a window is frozen for certain effects
* we may need to issue an update_area() covering the whole pixmap if we
* don't know what real damage has happened. */
if (self->priv->needs_damage_all)
meta_window_actor_damage_all (self);
+#endif
}
gboolean
@@ -1001,6 +1032,7 @@ meta_window_actor_effect_in_progress (MetaWindowActor *self)
self->priv->destroy_in_progress);
}
+#ifndef HAVE_WAYLAND
static void
meta_window_actor_queue_create_pixmap (MetaWindowActor *self)
{
@@ -1021,6 +1053,7 @@ meta_window_actor_queue_create_pixmap (MetaWindowActor *self)
*/
clutter_actor_queue_redraw (priv->actor);
}
+#endif
static gboolean
is_freeze_thaw_effect (gulong event)
@@ -1103,11 +1136,13 @@ meta_window_actor_after_effects (MetaWindowActor *self)
meta_window_actor_sync_visibility (self);
meta_window_actor_sync_actor_position (self);
+#ifndef HAVE_WAYLAND
if (!meta_window_is_mapped (priv->window))
meta_window_actor_detach (self);
if (priv->needs_pixmap)
clutter_actor_queue_redraw (priv->actor);
+#endif
}
void
@@ -1182,6 +1217,7 @@ meta_window_actor_effect_completed (MetaWindowActor *self,
meta_window_actor_after_effects (self);
}
+#ifndef HAVE_WAYLAND
/* Called to drop our reference to a window backing pixmap that we
* previously obtained with XCompositeNameWindowPixmap. We do this
* when the window is unmapped or when we want to update to a new
@@ -1212,6 +1248,7 @@ meta_window_actor_detach (MetaWindowActor *self)
meta_window_actor_queue_create_pixmap (self);
}
+#endif /* HAVE_WAYLAND */
gboolean
meta_window_actor_should_unredirect (MetaWindowActor *self)
@@ -1249,6 +1286,7 @@ meta_window_actor_should_unredirect (MetaWindowActor *self)
return FALSE;
}
+#ifndef HAVE_WAYLAND
void
meta_window_actor_set_redirected (MetaWindowActor *self, gboolean state)
{
@@ -1274,6 +1312,7 @@ meta_window_actor_set_redirected (MetaWindowActor *self, gboolean state)
self->priv->unredirected = TRUE;
}
}
+#endif
void
meta_window_actor_destroy (MetaWindowActor *self)
@@ -1333,6 +1372,7 @@ meta_window_actor_sync_actor_position (MetaWindowActor *self)
meta_window_get_input_rect (priv->window, &window_rect);
+#ifndef HAVE_WAYLAND
if (priv->last_width != window_rect.width ||
priv->last_height != window_rect.height)
{
@@ -1342,6 +1382,7 @@ meta_window_actor_sync_actor_position (MetaWindowActor *self)
priv->last_width = window_rect.width;
priv->last_height = window_rect.height;
}
+#endif
if (meta_window_actor_effect_in_progress (self))
return;
@@ -1502,7 +1543,9 @@ meta_window_actor_new (MetaWindow *window)
MetaScreen *screen = meta_window_get_screen (window);
MetaCompScreen *info = meta_screen_get_compositor_data (screen);
MetaWindowActor *self;
+#ifndef HAVE_WAYLAND
MetaWindowActorPrivate *priv;
+#endif
MetaFrame *frame;
Window top_window;
@@ -1520,6 +1563,7 @@ meta_window_actor_new (MetaWindow *window)
"meta-screen", screen,
NULL);
+#ifndef HAVE_WAYLAND
priv = self->priv;
priv->last_width = -1;
@@ -1528,6 +1572,7 @@ meta_window_actor_new (MetaWindow *window)
priv->mapped = meta_window_toplevel_is_mapped (priv->window);
if (priv->mapped)
meta_window_actor_queue_create_pixmap (self);
+#endif
meta_window_actor_sync_actor_position (self);
@@ -1555,7 +1600,9 @@ meta_window_actor_mapped (MetaWindowActor *self)
priv->mapped = TRUE;
+#ifndef HAVE_WAYLAND
meta_window_actor_queue_create_pixmap (self);
+#endif
}
void
@@ -1570,8 +1617,10 @@ meta_window_actor_unmapped (MetaWindowActor *self)
if (meta_window_actor_effect_in_progress (self))
return;
+#ifndef HAVE_WAYLAND
meta_window_actor_detach (self);
priv->needs_pixmap = FALSE;
+#endif
}
static void
@@ -1610,6 +1659,7 @@ meta_window_actor_clear_shadow_clip (MetaWindowActor *self)
}
}
+#ifndef HAVE_WAYLAND
static void
meta_window_actor_update_bounding_region_and_borders (MetaWindowActor *self,
int width,
@@ -1666,6 +1716,7 @@ meta_window_actor_update_bounding_region_and_borders (MetaWindowActor *self,
g_signal_emit (self, signals[SIZE_CHANGED], 0);
}
+#endif
static void
meta_window_actor_update_shape_region (MetaWindowActor *self,
@@ -1709,7 +1760,12 @@ meta_window_actor_get_obscured_region (MetaWindowActor *self)
{
MetaWindowActorPrivate *priv = self->priv;
- if (!priv->argb32 && priv->opacity == 0xff && priv->back_pixmap)
+ if (!priv->argb32 && priv->opacity == 0xff
+#ifndef HAVE_WAYLAND
+ && priv->back_pixmap)
+#else
+ )
+#endif
{
if (priv->shape_region)
return priv->shape_region;
@@ -1823,6 +1879,7 @@ meta_window_actor_reset_visible_regions (MetaWindowActor *self)
meta_window_actor_clear_shadow_clip (self);
}
+#ifndef HAVE_WAYLAND
static gboolean
texture_pixmap_using_extension (ClutterX11TexturePixmap *texture)
{
@@ -1835,7 +1892,12 @@ texture_pixmap_using_extension (ClutterX11TexturePixmap *texture)
cogl_is_texture_pixmap_x11 (handle) &&
cogl_texture_pixmap_x11_is_using_tfp_extension (handle);
}
+#endif
+/* When running as a wayland compositor we don't make requests for
+ * replacement pixmaps when resizing windows, we will instead be
+ * asked to attach replacement buffers by the clients. */
+#ifndef HAVE_WAYLAND
static void
check_needs_pixmap (MetaWindowActor *self)
{
@@ -1922,6 +1984,7 @@ check_needs_pixmap (MetaWindowActor *self)
out:
meta_error_trap_pop (display);
}
+#endif
static void
check_needs_shadow (MetaWindowActor *self)
@@ -2002,6 +2065,7 @@ is_frozen (MetaWindowActor *self)
return self->priv->freeze_count ? TRUE : FALSE;
}
+#ifndef HAVE_WAYLAND
void
meta_window_actor_process_damage (MetaWindowActor *self,
XDamageNotifyEvent *event)
@@ -2044,6 +2108,7 @@ meta_window_actor_process_damage (MetaWindowActor *self,
event->area.width,
event->area.height);
}
+#endif
void
meta_window_actor_sync_visibility (MetaWindowActor *self)
@@ -2173,7 +2238,6 @@ update_corners (MetaWindowActor *self,
corner_region, corner_path);
cairo_region_destroy (corner_region);
-
}
static void
@@ -2290,10 +2354,12 @@ meta_window_actor_update_shape (MetaWindowActor *self)
void
meta_window_actor_pre_paint (MetaWindowActor *self)
{
+#ifndef HAVE_WAYLAND
MetaWindowActorPrivate *priv = self->priv;
MetaScreen *screen = priv->screen;
MetaDisplay *display = meta_screen_get_display (screen);
Display *xdisplay = meta_display_get_xdisplay (display);
+#endif
if (is_frozen (self))
{
@@ -2302,6 +2368,7 @@ meta_window_actor_pre_paint (MetaWindowActor *self)
return;
}
+#ifndef HAVE_WAYLAND
if (priv->unredirected)
{
/* Nothing to do here until/if the window gets redirected again */
@@ -2338,6 +2405,7 @@ meta_window_actor_pre_paint (MetaWindowActor *self)
}
check_needs_pixmap (self);
+#endif
check_needs_reshape (self);
check_needs_shadow (self);
}
@@ -2374,3 +2442,11 @@ meta_window_actor_update_opacity (MetaWindowActor *self)
self->priv->opacity = opacity;
clutter_actor_set_opacity (self->priv->actor, opacity);
}
+
+#ifdef HAVE_WAYLAND
+ClutterActor *
+meta_window_actor_get_shaped_texture (MetaWindowActor *self)
+{
+ return self->priv->actor;
+}
+#endif
diff --git a/src/compositor/meta-window-group.c b/src/compositor/meta-window-group.c
index 491afe9..ca51bf1 100644
--- a/src/compositor/meta-window-group.c
+++ b/src/compositor/meta-window-group.c
@@ -126,8 +126,10 @@ meta_window_group_paint (ClutterActor *actor)
cairo_region_t *visible_region;
cairo_region_t *unredirected_window_region = NULL;
ClutterActor *stage;
- cairo_rectangle_int_t visible_rect, unredirected_rect;
+ cairo_rectangle_int_t visible_rect;
GList *children, *l;
+#ifndef HAVE_WAYLAND
+ cairo_rectangle_int_t unredirected_rect;
MetaWindowGroup *window_group = META_WINDOW_GROUP (actor);
MetaCompScreen *info = meta_screen_get_compositor_data (window_group->screen);
@@ -136,6 +138,7 @@ meta_window_group_paint (ClutterActor *actor)
meta_window_actor_get_shape_bounds (META_WINDOW_ACTOR (info->unredirected_window), &unredirected_rect);
unredirected_window_region = cairo_region_create_rectangle (&unredirected_rect);
}
+#endif
/* We walk the list from top to bottom (opposite of painting order),
* and subtract the opaque area of each window out of the visible
diff --git a/src/core/display.c b/src/core/display.c
index 1e7b48a..b73c053 100644
--- a/src/core/display.c
+++ b/src/core/display.c
@@ -378,8 +378,14 @@ enable_compositor (MetaDisplay *display,
meta_compositor_manage_screen (screen->display->compositor,
screen);
+ /* Instead of explicitly enumerating all windows during
+ * initialization, when we run as a wayland compositor we can
+ * rely on xwayland notifying us of all top level windows so
+ * we start compositing them when we get those notifications. */
+#ifndef HAVE_WAYLAND
if (composite_windows)
meta_screen_composite_all_windows (screen);
+#endif
}
}
@@ -820,14 +826,29 @@ meta_display_open (void)
enable_compositor (the_display, FALSE);
meta_display_grab (the_display);
-
+
/* Now manage all existing windows */
tmp = the_display->screens;
while (tmp != NULL)
{
MetaScreen *screen = tmp->data;
-
+
+#ifndef HAVE_WAYLAND
meta_screen_manage_all_windows (screen);
+#else
+ /* Instead of explicitly enumerating all windows during
+ * initialization, when we run as a wayland compositor we can rely on
+ * xwayland notifying us of all top level windows so we create
+ * MetaWindows when we get those notifications.
+ *
+ * We still want a guard window so we can avoid
+ * unmapping/withdrawing minimized windows for live
+ * thumbnails...
+ */
+ if (screen->guard_window == None)
+ screen->guard_window =
+ meta_screen_create_guard_window (screen->display->xdisplay, screen);
+#endif
tmp = tmp->next;
}
@@ -2327,9 +2348,11 @@ event_callback (XEvent *event,
screen->xscreen->height = event->xconfigure.height;
#endif
+#ifndef HAVE_WAYLAND
meta_screen_resize (screen,
event->xconfigure.width,
event->xconfigure.height);
+#endif
}
}
break;
diff --git a/src/core/main.c b/src/core/main.c
index 2c00e8b..040840f 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -53,6 +53,7 @@
#include "session.h"
#include <meta/prefs.h>
#include <meta/compositor.h>
+#include "meta-wayland-private.h"
#include <glib-object.h>
#include <gdk/gdkx.h>
@@ -264,6 +265,8 @@ meta_get_option_context (void)
return ctx;
}
+#ifndef HAVE_WAYLAND
+
/* Mutter is responsible for pulling events off the X queue, so Clutter
* doesn't need (and shouldn't) run its normal event source which polls
* the X fd, but we do have to deal with dispatching events that accumulate
@@ -344,6 +347,7 @@ meta_select_display (gchar *display_name)
/* DO NOT FREE envVar, putenv() sucks */
putenv (envVar);
}
+#endif
static void
meta_finalize (void)
@@ -355,26 +359,65 @@ meta_finalize (void)
CurrentTime); /* I doubt correct timestamps matter here */
}
-static int sigterm_pipe_fds[2] = { -1, -1 };
+static int signal_pipe_fds[2] = { -1, -1 };
static void
-sigterm_handler (int signum)
+signal_handler (int signum)
{
- if (sigterm_pipe_fds[1] >= 0)
+ if (signal_pipe_fds[1] >= 0)
{
- int G_GNUC_UNUSED dummy;
-
- dummy = write (sigterm_pipe_fds[1], "", 1);
- close (sigterm_pipe_fds[1]);
- sigterm_pipe_fds[1] = -1;
+ switch (signum)
+ {
+ case SIGTERM:
+ write (signal_pipe_fds[1], "T", 1);
+ break;
+ case SIGCHLD:
+ write (signal_pipe_fds[1], "C", 1);
+ break;
+ default:
+ break;
+ }
}
}
static gboolean
-on_sigterm (void)
+on_signal (GIOChannel *source,
+ GIOCondition condition,
+ void *data)
{
- meta_quit (META_EXIT_SUCCESS);
- return FALSE;
+ char signal;
+ int count;
+
+ for (;;)
+ {
+ count = read (signal_pipe_fds[0], &signal, 1);
+ if (count == EINTR)
+ continue;
+ if (count < 0)
+ {
+ const char *msg = strerror (errno);
+ g_warning ("Error handling signal: %s", msg);
+ }
+ if (count != 1)
+ {
+ g_warning ("Unexpectedly failed to read byte from signal pipe\n");
+ return TRUE;
+ }
+ break;
+ }
+ switch (signal)
+ {
+ case 'T': /* SIGTERM */
+ meta_quit (META_EXIT_SUCCESS);
+ break;
+ case 'C': /* SIGCHLD */
+ meta_wayland_handle_sig_child ();
+ break;
+ default:
+ g_warning ("Spurious character '%c' read from signal pipe", signal);
+ }
+
+ return TRUE;
}
/**
@@ -405,20 +448,25 @@ meta_init (void)
g_strerror (errno));
#endif
- if (pipe (sigterm_pipe_fds) != 0)
- g_printerr ("Failed to create SIGTERM pipe: %s\n",
+ if (pipe (signal_pipe_fds) != 0)
+ g_printerr ("Failed to create signal pipe: %s\n",
g_strerror (errno));
- channel = g_io_channel_unix_new (sigterm_pipe_fds[0]);
+ channel = g_io_channel_unix_new (signal_pipe_fds[0]);
g_io_channel_set_flags (channel, G_IO_FLAG_NONBLOCK, NULL);
- g_io_add_watch (channel, G_IO_IN, (GIOFunc) on_sigterm, NULL);
+ g_io_add_watch (channel, G_IO_IN, (GIOFunc) on_signal, NULL);
g_io_channel_set_close_on_unref (channel, TRUE);
g_io_channel_unref (channel);
- act.sa_handler = &sigterm_handler;
+ act.sa_handler = &signal_handler;
if (sigaction (SIGTERM, &act, NULL) < 0)
g_printerr ("Failed to register SIGTERM handler: %s\n",
g_strerror (errno));
+#ifdef HAVE_WAYLAND
+ if (sigaction (SIGCHLD, &act, NULL) < 0)
+ g_printerr ("Failed to register SIGCHLD handler: %s\n",
+ g_strerror (errno));
+#endif
if (g_getenv ("MUTTER_VERBOSE"))
meta_set_verbose (TRUE);
@@ -436,9 +484,15 @@ meta_init (void)
g_irepository_prepend_search_path (MUTTER_PKGLIBDIR);
#endif
- meta_set_syncing (opt_sync || (g_getenv ("MUTTER_SYNC") != NULL));
-
+#ifdef HAVE_WAYLAND
+ /* NB: When running as a hybrid wayland compositor we run our own headless X
+ * server so the user can't control the X display to connect too. */
+ meta_wayland_init ();
+#else
meta_select_display (opt_display_name);
+#endif
+
+ meta_set_syncing (opt_sync || (g_getenv ("MUTTER_SYNC") != NULL));
if (opt_replace_wm)
meta_set_replace_current_wm (TRUE);
@@ -450,10 +504,16 @@ meta_init (void)
meta_ui_init ();
+ /* If we are running with wayland then we don't wait until we have
+ * an X connection before initializing clutter we instead initialize
+ * it earlier since we need to initialize the GL driver so the driver
+ * can register any needed wayland extensions. */
+#ifndef HAVE_WAYLAND
/*
* Clutter can only be initialized after the UI.
*/
meta_clutter_init ();
+#endif
}
/**
diff --git a/src/core/screen-private.h b/src/core/screen-private.h
index 866f5fb..0f0c484 100644
--- a/src/core/screen-private.h
+++ b/src/core/screen-private.h
@@ -254,4 +254,6 @@ void meta_screen_workspace_switched (MetaScreen *screen,
void meta_screen_set_active_workspace_hint (MetaScreen *screen);
+Window meta_screen_create_guard_window (Display *xdisplay, MetaScreen *screen);
+
#endif
diff --git a/src/core/screen.c b/src/core/screen.c
index 14d40b1..a1c5cb7 100644
--- a/src/core/screen.c
+++ b/src/core/screen.c
@@ -40,6 +40,9 @@
#include "xprops.h"
#include <meta/compositor.h>
#include "mutter-enum-types.h"
+#ifdef HAVE_WAYLAND
+#include "meta-wayland-private.h"
+#endif
#ifdef HAVE_SOLARIS_XINERAMA
#include <X11/extensions/xinerama.h>
@@ -652,8 +655,8 @@ reload_monitor_infos (MetaScreen *screen)
* that compositor code may provide live previews of them.
* Instead of being unmapped/withdrawn, they get pushed underneath
* the guard window. */
-static Window
-create_guard_window (Display *xdisplay, MetaScreen *screen)
+Window
+meta_screen_create_guard_window (Display *xdisplay, MetaScreen *screen)
{
XSetWindowAttributes attributes;
Window guard_window;
@@ -707,6 +710,9 @@ meta_screen_new (MetaDisplay *display,
char buf[128];
guint32 manager_timestamp;
gulong current_workspace;
+#ifdef HAVE_WAYLAND
+ MetaWaylandCompositor *compositor;
+#endif
replace_current_wm = meta_get_replace_current_wm ();
@@ -842,8 +848,14 @@ meta_screen_new (MetaDisplay *display,
screen->xscreen = ScreenOfDisplay (xdisplay, number);
screen->xroot = xroot;
screen->rect.x = screen->rect.y = 0;
+#ifdef HAVE_WAYLAND
+ compositor = meta_wayland_compositor_get_default ();
+ screen->rect.width = clutter_actor_get_width (compositor->stage);
+ screen->rect.height = clutter_actor_get_height (compositor->stage);
+#else
screen->rect.width = WidthOfScreen (screen->xscreen);
screen->rect.height = HeightOfScreen (screen->xscreen);
+#endif
screen->current_cursor = -1; /* invalid/unset */
screen->default_xvisual = DefaultVisualOfScreen (screen->xscreen);
screen->default_depth = DefaultDepthOfScreen (screen->xscreen);
@@ -1086,6 +1098,11 @@ list_windows (MetaScreen *screen)
return g_list_reverse (result);
}
+/* Instead of explicitly enumerating all windows during
+ * initialization, when we run as a wayland compositor we can rely on
+ * xwayland notifying us of all top level windows so we create
+ * MetaWindows when we get those notifications. */
+#ifndef HAVE_WAYLAND
void
meta_screen_manage_all_windows (MetaScreen *screen)
{
@@ -1095,8 +1112,8 @@ meta_screen_manage_all_windows (MetaScreen *screen)
meta_display_grab (screen->display);
if (screen->guard_window == None)
- screen->guard_window = create_guard_window (screen->display->xdisplay,
- screen);
+ screen->guard_window =
+ meta_screen_create_guard_window (screen->display->xdisplay, screen);
windows = list_windows (screen);
@@ -1144,6 +1161,7 @@ meta_screen_composite_all_windows (MetaScreen *screen)
/* initialize the compositor's view of the stacking order */
meta_stack_tracker_sync_stack (screen->stack_tracker);
}
+#endif /* HAVE_WAYLAND */
/**
* meta_screen_for_x_screen:
diff --git a/src/meta/compositor-mutter.h b/src/meta/compositor-mutter.h
index 5be1f0e..9941ce8 100644
--- a/src/meta/compositor-mutter.h
+++ b/src/meta/compositor-mutter.h
@@ -39,8 +39,10 @@ Window meta_get_overlay_window (MetaScreen *screen);
GList *meta_get_window_actors (MetaScreen *screen);
ClutterActor *meta_get_window_group_for_screen (MetaScreen *screen);
+#ifndef HAVE_WAYLAND
void meta_disable_unredirect_for_screen (MetaScreen *screen);
void meta_enable_unredirect_for_screen (MetaScreen *screen);
+#endif
ClutterActor *meta_get_background_actor_for_screen (MetaScreen *screen);
diff --git a/src/wayland/meta-wayland-private.h b/src/wayland/meta-wayland-private.h
new file mode 100644
index 0000000..4e7f7fa
--- /dev/null
+++ b/src/wayland/meta-wayland-private.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2012 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef META_WAYLAND_PRIVATE_H
+#define META_WAYLAND_PRIVATE_H
+
+#include <wayland-server.h>
+
+#include <clutter/clutter.h>
+
+#include <glib.h>
+
+typedef struct _MetaWaylandCompositor MetaWaylandCompositor;
+
+typedef struct
+{
+ struct wl_buffer *wayland_buffer;
+ GList *surfaces_attached_to;
+ struct wl_listener buffer_destroy_listener;
+} MetaWaylandBuffer;
+
+typedef struct
+{
+ MetaWaylandCompositor *compositor;
+ struct wl_surface wayland_surface;
+ guint32 xid;
+ int x;
+ int y;
+ MetaWaylandBuffer *buffer;
+ ClutterActor *actor;
+ gboolean has_shell_surface;
+ struct wl_listener surface_destroy_listener;
+} MetaWaylandSurface;
+
+typedef struct
+{
+ MetaWaylandSurface *surface;
+ struct wl_resource resource;
+ struct wl_listener surface_destroy_listener;
+} MetaWaylandShellSurface;
+
+typedef struct
+{
+ guint32 flags;
+ int width;
+ int height;
+ int refresh;
+} MetaWaylandMode;
+
+typedef struct
+{
+ struct wl_object wayland_output;
+ int x;
+ int y;
+ int width_mm;
+ int height_mm;
+ /* XXX: with sliced stages we'd reference a CoglFramebuffer here. */
+
+ GList *modes;
+} MetaWaylandOutput;
+
+typedef struct
+{
+ GSource source;
+ GPollFD pfd;
+ struct wl_event_loop *loop;
+} WaylandEventSource;
+
+typedef struct
+{
+ /* GList node used as an embedded list */
+ GList node;
+
+ /* Pointer back to the compositor */
+ MetaWaylandCompositor *compositor;
+
+ struct wl_resource resource;
+} MetaWaylandFrameCallback;
+
+struct _MetaWaylandCompositor
+{
+ struct wl_display *wayland_display;
+ struct wl_shm *wayland_shm;
+ struct wl_event_loop *wayland_loop;
+ GMainLoop *init_loop;
+ ClutterActor *stage;
+ GList *outputs;
+ GSource *wayland_event_source;
+ GList *surfaces;
+ GQueue 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;
+ GHashTable *window_surfaces;
+};
+
+void meta_wayland_init (void);
+
+/* We maintain a singleton MetaWaylandCompositor which can be got at via this
+ * API after meta_wayland_init() has been called. */
+MetaWaylandCompositor *meta_wayland_compositor_get_default (void);
+
+void meta_wayland_handle_sig_child (void);
+
+MetaWaylandSurface *meta_wayland_lookup_surface_for_xid (guint32 xid);
+
+#endif /* META_WAYLAND_PRIVATE_H */
diff --git a/src/wayland/meta-wayland.c b/src/wayland/meta-wayland.c
new file mode 100644
index 0000000..b951dfb
--- /dev/null
+++ b/src/wayland/meta-wayland.c
@@ -0,0 +1,1098 @@
+/*
+ * Wayland Support
+ *
+ * Copyright (C) 2012 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#define COGL_ENABLE_EXPERIMENTAL_2_0_API
+#include <clutter/clutter.h>
+#include <clutter/wayland/clutter-wayland-compositor.h>
+#include <clutter/wayland/clutter-wayland-surface.h>
+
+#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 <sys/wait.h>
+
+#include <wayland-server.h>
+
+#include "xserver-server-protocol.h"
+
+#include "meta-wayland-private.h"
+#include "meta-window-actor-private.h"
+#include "display-private.h"
+#include "window-private.h"
+#include <meta/types.h>
+#include "frame.h"
+
+static MetaWaylandCompositor _meta_wayland_compositor;
+
+MetaWaylandCompositor *
+meta_wayland_compositor_get_default (void)
+{
+ return &_meta_wayland_compositor;
+}
+
+static guint32
+get_time (void)
+{
+ struct timeval tv;
+ gettimeofday (&tv, NULL);
+ return tv.tv_sec * 1000 + tv.tv_usec / 1000;
+}
+
+static gboolean
+wayland_event_source_prepare (GSource *base, int *timeout)
+{
+ *timeout = -1;
+ return FALSE;
+}
+
+static gboolean
+wayland_event_source_check (GSource *base)
+{
+ WaylandEventSource *source = (WaylandEventSource *)base;
+ return source->pfd.revents;
+}
+
+static gboolean
+wayland_event_source_dispatch (GSource *base,
+ GSourceFunc callback,
+ void *data)
+{
+ WaylandEventSource *source = (WaylandEventSource *)base;
+ wl_event_loop_dispatch (source->loop, 0);
+ return TRUE;
+}
+
+static GSourceFuncs wayland_event_source_funcs =
+{
+ wayland_event_source_prepare,
+ wayland_event_source_check,
+ wayland_event_source_dispatch,
+ NULL
+};
+
+static GSource *
+wayland_event_source_new (struct wl_event_loop *loop)
+{
+ WaylandEventSource *source;
+
+ source = (WaylandEventSource *) g_source_new (&wayland_event_source_funcs,
+ sizeof (WaylandEventSource));
+ source->loop = loop;
+ source->pfd.fd = wl_event_loop_get_fd (loop);
+ source->pfd.events = G_IO_IN | G_IO_ERR;
+ g_source_add_poll (&source->source, &source->pfd);
+
+ return &source->source;
+}
+
+static void
+buffer_destroy_callback (struct wl_listener *listener,
+ struct wl_resource *resource,
+ guint32 time)
+{
+ g_warning ("Buffer destroy callback");
+}
+
+static MetaWaylandBuffer *
+meta_wayland_buffer_new (struct wl_buffer *wayland_buffer)
+{
+ MetaWaylandBuffer *buffer = g_slice_new0 (MetaWaylandBuffer);
+
+ buffer->wayland_buffer = wayland_buffer;
+ buffer->surfaces_attached_to = NULL;
+
+ buffer->buffer_destroy_listener.func = buffer_destroy_callback;
+ wl_list_insert (wayland_buffer->resource.destroy_listener_list.prev,
+ &buffer->buffer_destroy_listener.link);
+
+ return buffer;
+}
+
+static void
+meta_wayland_buffer_free (MetaWaylandBuffer *buffer)
+{
+ GList *l;
+
+ buffer->wayland_buffer->user_data = NULL;
+
+ for (l = buffer->surfaces_attached_to; l; l = l->next)
+ {
+ MetaWaylandSurface *surface = l->data;
+ surface->buffer = NULL;
+ }
+
+ g_list_free (buffer->surfaces_attached_to);
+ g_slice_free (MetaWaylandBuffer, buffer);
+}
+
+static void
+shm_buffer_created (struct wl_buffer *wayland_buffer)
+{
+ /* We ignore the buffer until it is attached to a surface */
+ wayland_buffer->user_data = NULL;
+}
+
+static void
+shm_buffer_damaged (struct wl_buffer *wayland_buffer,
+ gint32 x,
+ gint32 y,
+ gint32 width,
+ gint32 height)
+{
+ MetaWaylandBuffer *buffer = wayland_buffer->user_data;
+ GList *l;
+
+ /* We only have an associated MetaWaylandBuffer once the wayland buffer has
+ * been attached to a surface. */
+ if (!buffer)
+ return;
+
+ for (l = buffer->surfaces_attached_to; l; l = l->next)
+ {
+ MetaWaylandSurface *surface = l->data;
+ ClutterWaylandSurface *surface_actor =
+ CLUTTER_WAYLAND_SURFACE (surface->actor);
+ clutter_wayland_surface_damage_buffer (surface_actor,
+ wayland_buffer,
+ x, y, width, height);
+ }
+}
+
+static void
+shm_buffer_destroyed (struct wl_buffer *wayland_buffer)
+{
+ /* We only have an associated MetaWaylandBuffer once the wayland buffer has
+ * been attached to a surface. */
+ if (wayland_buffer->user_data)
+ meta_wayland_buffer_free ((MetaWaylandBuffer *)wayland_buffer->user_data);
+}
+
+const static struct wl_shm_callbacks shm_callbacks = {
+ shm_buffer_created,
+ shm_buffer_damaged,
+ shm_buffer_destroyed
+};
+
+static void
+meta_wayland_surface_destroy (struct wl_client *wayland_client,
+ struct wl_resource *wayland_resource)
+{
+ wl_resource_destroy (wayland_resource, get_time ());
+}
+
+static void
+meta_wayland_surface_detach_buffer (MetaWaylandSurface *surface)
+{
+ MetaWaylandBuffer *buffer = surface->buffer;
+
+ if (buffer)
+ {
+ buffer->surfaces_attached_to =
+ g_list_remove (buffer->surfaces_attached_to, surface);
+ if (buffer->surfaces_attached_to == NULL)
+ meta_wayland_buffer_free (buffer);
+ surface->buffer = NULL;
+ }
+}
+
+static void
+meta_wayland_surface_attach_buffer (struct wl_client *wayland_client,
+ struct wl_resource *wayland_surface_resource,
+ struct wl_resource *wayland_buffer_resource,
+ gint32 dx, gint32 dy)
+{
+ struct wl_buffer *wayland_buffer = wayland_buffer_resource->data;
+ MetaWaylandBuffer *buffer = wayland_buffer->user_data;
+ MetaWaylandSurface *surface = wayland_surface_resource->data;
+ MetaWaylandCompositor *compositor = surface->compositor;
+ ClutterWaylandSurface *surface_actor;
+
+ /* XXX: in the case where we are reattaching the same buffer we can
+ * simply bail out. Note this is important because if we don't bail
+ * out then the _detach_buffer will actually end up destroying the
+ * buffer we're trying to attach. */
+ if (buffer && surface->buffer == buffer)
+ return;
+
+ meta_wayland_surface_detach_buffer (surface);
+
+ if (!buffer)
+ {
+ buffer = meta_wayland_buffer_new (wayland_buffer);
+ wayland_buffer->user_data = buffer;
+ }
+
+ g_return_if_fail (g_list_find (buffer->surfaces_attached_to, surface) == NULL);
+
+ buffer->surfaces_attached_to = g_list_prepend (buffer->surfaces_attached_to,
+ surface);
+
+ /* NB: Surfaces from xwayland are dealt with via MetaShapedTexture
+ * actors */
+ if (wayland_client != compositor->xwayland_client)
+ {
+ if (!surface->actor)
+ {
+ surface->actor = clutter_wayland_surface_new (&surface->wayland_surface);
+ clutter_container_add_actor (CLUTTER_CONTAINER (compositor->stage),
+ surface->actor);
+ }
+ }
+
+ /* XXX: Its a bit messy but even though xwayland surfaces are
+ * handled separately we still set surface->actor to the
+ * MetaShapedTexture actor thats created for a MetaWindowActor.
+ * This means we can consistently deal with damage and attaching
+ * buffers to surfaces.
+ */
+ if (surface->actor)
+ {
+ surface_actor = CLUTTER_WAYLAND_SURFACE (surface->actor);
+ if (!clutter_wayland_surface_attach_buffer (surface_actor, wayland_buffer,
+ NULL))
+ g_warning ("Failed to attach buffer to ClutterWaylandSurface");
+ }
+
+ surface->buffer = buffer;
+}
+
+static void
+meta_wayland_surface_damage (struct wl_client *client,
+ struct wl_resource *surface_resource,
+ gint32 x,
+ gint32 y,
+ gint32 width,
+ gint32 height)
+{
+ MetaWaylandSurface *surface = surface_resource->data;
+ if (surface->buffer && surface->actor)
+ {
+ clutter_wayland_surface_damage_buffer (CLUTTER_WAYLAND_SURFACE (surface->actor),
+ surface->buffer->wayland_buffer,
+ x, y, width, height);
+ }
+}
+
+static void
+destroy_frame_callback (struct wl_resource *callback_resource)
+{
+ MetaWaylandFrameCallback *callback = callback_resource->data;
+
+ g_queue_unlink (&callback->compositor->frame_callbacks,
+ &callback->node);
+
+ g_slice_free (MetaWaylandFrameCallback, callback);
+}
+
+static void
+meta_wayland_surface_frame (struct wl_client *client,
+ struct wl_resource *surface_resource,
+ guint32 callback_id)
+{
+ MetaWaylandFrameCallback *callback;
+ MetaWaylandSurface *surface = surface_resource->data;
+
+ callback = g_slice_new0 (MetaWaylandFrameCallback);
+ callback->compositor = surface->compositor;
+ callback->node.data = callback;
+ callback->resource.object.interface = &wl_callback_interface;
+ callback->resource.object.id = callback_id;
+ callback->resource.destroy = destroy_frame_callback;
+ callback->resource.data = callback;
+
+ wl_client_add_resource (client, &callback->resource);
+
+ g_queue_push_tail_link (&surface->compositor->frame_callbacks,
+ &callback->node);
+}
+
+const struct wl_surface_interface meta_wayland_surface_interface = {
+ meta_wayland_surface_destroy,
+ meta_wayland_surface_attach_buffer,
+ meta_wayland_surface_damage,
+ meta_wayland_surface_frame
+};
+
+static void
+meta_wayland_surface_free (MetaWaylandSurface *surface)
+{
+ MetaWaylandCompositor *compositor = surface->compositor;
+ compositor->surfaces = g_list_remove (compositor->surfaces, surface);
+ meta_wayland_surface_detach_buffer (surface);
+
+ /* XXX: This is a bit messy, but we only own surface->actor if
+ * its a ClutterWaylandSurface. Surfaces that correspond to X
+ * applications will have MetaShapedTexture actors and those are
+ * owned by a MetaWindowActor.
+ *
+ * TODO: find a neater way of separating things.
+ */
+ if (surface->actor && CLUTTER_WAYLAND_IS_SURFACE (surface->actor))
+ {
+ clutter_actor_destroy (surface->actor);
+ }
+
+ g_slice_free (MetaWaylandSurface, surface);
+}
+
+static void
+meta_wayland_surface_resource_destroy_cb (struct wl_resource *wayland_surface_resource)
+{
+ MetaWaylandSurface *surface = wayland_surface_resource->data;
+ meta_wayland_surface_free (surface);
+}
+
+static void
+surface_destroy_callback (struct wl_listener *listener,
+ struct wl_resource *resource,
+ guint32 time)
+{
+ g_warning ("Surface destroy callback");
+}
+
+static void
+meta_wayland_compositor_create_surface (struct wl_client *wayland_client,
+ struct wl_resource *wayland_compositor_resource,
+ guint32 id)
+{
+ MetaWaylandCompositor *compositor = wayland_compositor_resource->data;
+ MetaWaylandSurface *surface = g_slice_new0 (MetaWaylandSurface);
+
+ surface->compositor = compositor;
+
+ surface->wayland_surface.resource.destroy =
+ meta_wayland_surface_resource_destroy_cb;
+ surface->wayland_surface.resource.object.id = id;
+ surface->wayland_surface.resource.object.interface = &wl_surface_interface;
+ surface->wayland_surface.resource.object.implementation =
+ (void (**)(void)) &meta_wayland_surface_interface;
+ surface->wayland_surface.resource.data = surface;
+
+ wl_client_add_resource (wayland_client, &surface->wayland_surface.resource);
+
+ surface->surface_destroy_listener.func = surface_destroy_callback;
+ wl_list_insert (surface->wayland_surface.resource.destroy_listener_list.prev,
+ &surface->surface_destroy_listener.link);
+
+ compositor->surfaces = g_list_prepend (compositor->surfaces, surface);
+}
+
+static void
+bind_output (struct wl_client *client,
+ void *data,
+ guint32 version,
+ guint32 id)
+{
+ MetaWaylandOutput *output = data;
+ struct wl_resource *resource =
+ wl_client_add_object (client, &wl_output_interface, NULL, id, data);
+ GList *l;
+
+ wl_resource_post_event (resource,
+ WL_OUTPUT_GEOMETRY,
+ output->x, output->y,
+ output->width_mm,
+ output->height_mm,
+ 0, /* subpixel: unknown */
+ "unknown", /* make */
+ "unknown"); /* model */
+
+ for (l = output->modes; l; l = l->next)
+ {
+ MetaWaylandMode *mode = l->data;
+ wl_resource_post_event (resource,
+ WL_OUTPUT_MODE,
+ mode->flags,
+ mode->width,
+ mode->height,
+ mode->refresh);
+ }
+}
+
+static void
+meta_wayland_compositor_create_output (MetaWaylandCompositor *compositor,
+ int x,
+ int y,
+ int width_mm,
+ int height_mm)
+{
+ MetaWaylandOutput *output = g_slice_new0 (MetaWaylandOutput);
+
+ output->wayland_output.interface = &wl_output_interface;
+
+ output->x = x;
+ output->y = y;
+ output->width_mm = width_mm;
+ output->height_mm = height_mm;
+
+ wl_display_add_global (compositor->wayland_display,
+ &wl_output_interface,
+ output,
+ bind_output);
+
+ /* XXX: eventually we will support sliced stages and an output should
+ * correspond to a slice/CoglFramebuffer, but for now we only support
+ * one output so we make sure it always matches the size of the stage
+ */
+ clutter_actor_set_size (compositor->stage, width_mm, height_mm);
+
+ compositor->outputs = g_list_prepend (compositor->outputs, output);
+}
+
+const static struct wl_compositor_interface meta_wayland_compositor_interface = {
+ meta_wayland_compositor_create_surface,
+};
+
+static void
+paint_finished_cb (ClutterActor *self, void *user_data)
+{
+ MetaWaylandCompositor *compositor = user_data;
+
+ while (!g_queue_is_empty (&compositor->frame_callbacks))
+ {
+ MetaWaylandFrameCallback *callback =
+ g_queue_peek_head (&compositor->frame_callbacks);
+
+ wl_resource_post_event (&callback->resource,
+ WL_CALLBACK_DONE, get_time ());
+ wl_resource_destroy (&callback->resource, 0);
+ }
+}
+
+static void
+compositor_bind (struct wl_client *client,
+ void *data,
+ guint32 version,
+ guint32 id)
+{
+ MetaWaylandCompositor *compositor = data;
+
+ wl_client_add_object (client, &wl_compositor_interface,
+ &meta_wayland_compositor_interface, id, compositor);
+}
+
+static void
+shell_surface_move(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *input_resource,
+ guint32 time)
+{
+}
+
+static void
+shell_surface_resize (struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *input_resource,
+ guint32 time,
+ guint32 edges)
+{
+}
+
+static void
+shell_surface_set_toplevel (struct wl_client *client,
+ struct wl_resource *resource)
+{
+}
+
+static void
+shell_surface_set_transient (struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *parent_resource,
+ int x,
+ int y,
+ guint32 flags)
+{
+}
+
+static void
+shell_surface_set_fullscreen (struct wl_client *client,
+ struct wl_resource *resource)
+{
+}
+
+static const struct wl_shell_surface_interface meta_wayland_shell_surface_interface =
+{
+ shell_surface_move,
+ shell_surface_resize,
+ shell_surface_set_toplevel,
+ shell_surface_set_transient,
+ shell_surface_set_fullscreen
+};
+
+static void
+shell_handle_surface_destroy (struct wl_listener *listener,
+ struct wl_resource *resource,
+ guint32 time)
+{
+ MetaWaylandShellSurface *shell_surface = container_of (listener,
+ MetaWaylandShellSurface,
+ surface_destroy_listener);
+
+ shell_surface->surface->has_shell_surface = FALSE;
+ shell_surface->surface = NULL;
+ wl_resource_destroy (&shell_surface->resource, time);
+}
+
+static void
+destroy_shell_surface (struct wl_resource *resource)
+{
+ MetaWaylandShellSurface *shell_surface = resource->data;
+
+ /* In case cleaning up a dead client destroys shell_surface first */
+ if (shell_surface->surface)
+ {
+ wl_list_remove (&shell_surface->surface_destroy_listener.link);
+ shell_surface->surface->has_shell_surface = FALSE;
+ }
+
+ g_free (shell_surface);
+}
+
+static void
+get_shell_surface (struct wl_client *client,
+ struct wl_resource *resource,
+ guint32 id,
+ struct wl_resource *surface_resource)
+{
+ MetaWaylandSurface *surface = surface_resource->data;
+ MetaWaylandShellSurface *shell_surface;
+
+ if (surface->has_shell_surface)
+ {
+ wl_resource_post_error (surface_resource,
+ WL_DISPLAY_ERROR_INVALID_OBJECT,
+ "wl_shell::get_shell_surface already requested");
+ return;
+ }
+
+ shell_surface = g_new0 (MetaWaylandShellSurface, 1);
+ shell_surface->resource.destroy = destroy_shell_surface;
+ shell_surface->resource.object.id = id;
+ shell_surface->resource.object.interface = &wl_shell_surface_interface;
+ shell_surface->resource.object.implementation =
+ (void (**) (void)) &meta_wayland_shell_surface_interface;
+ shell_surface->resource.data = shell_surface;
+
+ shell_surface->surface = surface;
+ shell_surface->surface_destroy_listener.func = shell_handle_surface_destroy;
+ wl_list_insert (surface->wayland_surface.resource.destroy_listener_list.prev,
+ &shell_surface->surface_destroy_listener.link);
+
+ surface->has_shell_surface = TRUE;
+
+ wl_client_add_resource (client, &shell_surface->resource);
+}
+
+static const struct wl_shell_interface meta_wayland_shell_interface =
+{
+ get_shell_surface
+};
+
+static void
+bind_shell (struct wl_client *client,
+ void *data,
+ guint32 version,
+ guint32 id)
+{
+ wl_client_add_object (client, &wl_shell_interface,
+ &meta_wayland_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 (MetaWaylandCompositor *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]);
+
+ compositor->xwayland_pid = pid;
+ break;
+
+ case -1:
+ g_error ("Failed to fork for xwayland server");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+stop_xwayland (MetaWaylandCompositor *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 xid)
+{
+ MetaWaylandCompositor *compositor = compositor_resource->data;
+ MetaWaylandSurface *surface = surface_resource->data;
+ MetaDisplay *display = meta_get_display ();
+ MetaWindow *window;
+
+ g_return_if_fail (surface->xid == None);
+
+ surface->xid = xid;
+
+ g_hash_table_insert (compositor->window_surfaces, &xid, surface);
+
+ window = meta_display_lookup_x_window (display, xid);
+ if (window)
+ {
+ MetaWindowActor *window_actor =
+ META_WINDOW_ACTOR (meta_window_get_compositor_private (window));
+ ClutterWaylandSurface *surface_actor = CLUTTER_WAYLAND_SURFACE (
+ meta_window_actor_get_shaped_texture (window_actor));
+
+ clutter_wayland_surface_set_surface (surface_actor,
+ &surface->wayland_surface);
+ if (surface->buffer)
+ clutter_wayland_surface_attach_buffer (surface_actor,
+ surface->buffer->wayland_buffer,
+ NULL);
+
+ surface->actor = surface_actor;
+
+#if 0
+ if (window->visible_to_compositor)
+ meta_compositor_show_window (display->compositor, window,
+ META_COMP_EFFECT_NONE);
+#endif
+ }
+
+#warning "FIXME: Handle surface destroy and remove window_surfaces mapping"
+}
+
+MetaWaylandSurface *
+meta_wayland_lookup_surface_for_xid (guint32 xid)
+{
+ return g_hash_table_lookup (_meta_wayland_compositor.window_surfaces, &xid);
+}
+
+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)
+{
+ MetaWaylandCompositor *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);
+
+ 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");
+
+ /* Make sure xwayland will recieve the above sockets in a finite
+ * time before unblocking the initialization mainloop since we are
+ * then going to immediately try and connect to those as the window
+ * manager. */
+ wl_client_flush (client);
+
+ /* At this point xwayland is all setup to start accepting
+ * connections so we can quit the transient initialization mainloop
+ * and unblock meta_wayland_init() to continue initializing mutter.
+ * */
+ g_main_loop_quit (compositor->init_loop);
+ compositor->init_loop = NULL;
+}
+
+void
+meta_wayland_init (void)
+{
+ MetaWaylandCompositor *compositor = &_meta_wayland_compositor;
+ ClutterColor stage_color;
+
+ memset (compositor, 0, sizeof (MetaWaylandCompositor));
+
+ compositor->wayland_display = wl_display_create ();
+ if (compositor->wayland_display == NULL)
+ g_error ("failed to create wayland display");
+
+ g_queue_init (&compositor->frame_callbacks);
+
+ if (!wl_display_add_global (compositor->wayland_display,
+ &wl_compositor_interface,
+ compositor,
+ compositor_bind))
+ g_error ("Failed to register wayland compositor object");
+
+ compositor->wayland_shm = wl_shm_init (compositor->wayland_display,
+ &shm_callbacks);
+ if (!compositor->wayland_shm)
+ g_error ("Failed to allocate setup wayland shm callbacks");
+
+ compositor->wayland_loop =
+ wl_display_get_event_loop (compositor->wayland_display);
+ compositor->wayland_event_source =
+ wayland_event_source_new (compositor->wayland_loop);
+ g_source_attach (compositor->wayland_event_source, NULL);
+
+ clutter_wayland_set_compositor_display (compositor->wayland_display);
+
+ if (clutter_init (NULL, NULL) != CLUTTER_INIT_SUCCESS)
+ g_error ("Failed to initialize Clutter");
+
+ compositor->stage = clutter_stage_new ();
+ stage_color.red = 0xff;
+ stage_color.green = 0;
+ stage_color.blue = 0;
+ stage_color.alpha = 0xff;
+ clutter_stage_set_color (compositor->stage, &stage_color);
+ clutter_stage_set_user_resizable (CLUTTER_STAGE (compositor->stage), FALSE);
+ g_signal_connect_after (compositor->stage, "paint",
+ G_CALLBACK (paint_finished_cb), compositor);
+
+ meta_wayland_compositor_create_output (compositor, 0, 0, 800, 600);
+
+ if (wl_display_add_global (compositor->wayland_display, &wl_shell_interface,
+ compositor, bind_shell) == NULL)
+ 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");
+
+ wl_display_add_global (compositor->wayland_display,
+ &xserver_interface,
+ compositor,
+ bind_xserver);
+
+ /* We need a mapping from xids to wayland surfaces... */
+ compositor->window_surfaces = g_hash_table_new (g_int_hash, g_int_equal);
+
+ /* 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))
+ g_error ("Failed to start X Wayland");
+
+ putenv (g_strdup_printf ("DISPLAY=:%d", compositor->xwayland_display_index));
+
+ /* We need to run a mainloop until we know xwayland has a binding
+ * for our xserver interface at which point we can assume it's
+ * ready to start accepting connections. */
+ compositor->init_loop = g_main_loop_new (NULL, FALSE);
+
+ g_main_loop_run (compositor->init_loop);
+}
+
+void
+meta_wayland_handle_sig_child (void)
+{
+ int status;
+ pid_t pid = waitpid (-1, &status, WNOHANG);
+ MetaWaylandCompositor *compositor = &_meta_wayland_compositor;
+
+ /* The simplest measure to avoid infinitely re-spawning a crashing
+ * X server */
+ if (pid == compositor->xwayland_pid)
+ {
+ if (!WIFEXITED (status))
+ g_critical ("X Wayland crashed; aborting");
+ else
+ {
+ /* For now we simply abort if we see the server exit.
+ *
+ * In the future X will only be loaded lazily for legacy X support
+ * but for now it's a hard requirement. */
+ g_critical ("Spurious exit of X Wayland server");
+ }
+ }
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]