[mutter/wip/wayland: 6/8] wayland: Adds basic hybrid X + Wayland support
- From: Robert Bragg <rbragg src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [mutter/wip/wayland: 6/8] wayland: Adds basic hybrid X + Wayland support
- Date: Fri, 17 May 2013 13:40:06 +0000 (UTC)
commit db426cfe5c8dbb3f87c65670dc2e1b69cb530d8c
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.
This aims to not break Mutter's existing support for the traditional X
compositing model which means a single build of Mutter can be
distributed supporting the traditional model and the new Wayland based
compositing model.
TODO: although building with --disable-wayland has at least been tested,
I still haven't actually verified that running as a traditional
compositor isn't broken currently.
At this point no input is supported
Note: This patch is still a work in progress
Additional authors:
Neil Roberts <neil linux intel com>
Rico Tzschichholz.
configure.ac | 2 +-
src/Makefile.am | 23 +
src/compositor/compositor.c | 341 +++++---
src/compositor/meta-plugin-manager.c | 3 +
src/compositor/meta-shaped-texture.c | 336 ++++++--
src/compositor/meta-window-actor-private.h | 23 +-
src/compositor/meta-window-actor.c | 419 ++++++---
src/compositor/meta-window-group.c | 16 +-
src/core/display.c | 35 +-
src/core/main.c | 114 ++-
src/core/screen-private.h | 2 +
src/core/screen.c | 31 +-
src/core/window-private.h | 24 +
src/core/window.c | 338 +++++---
src/meta/compositor.h | 4 +-
src/meta/meta-shaped-texture.h | 22 +-
src/wayland/meta-wayland-private.h | 151 +++
src/wayland/meta-wayland.c | 1369 ++++++++++++++++++++++++++++
18 files changed, 2748 insertions(+), 505 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index c7dbf30..5ee5384 100644
--- a/configure.ac
+++ b/configure.ac
@@ -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 d8edb49..b0eb79f 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -14,6 +14,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\" \
@@ -39,6 +40,13 @@ 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 \
+ wayland/xserver-client-protocol.h
+endif
+
libmutter_la_SOURCES = \
core/async-getprop.c \
core/async-getprop.h \
@@ -167,6 +175,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)
@@ -348,3 +362,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.c b/src/compositor/compositor.c
index dbaaca2..a7e07b9 100644
--- a/src/compositor/compositor.c
+++ b/src/compositor/compositor.c
@@ -86,6 +86,9 @@
#include "meta-window-group.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>
@@ -174,7 +177,7 @@ process_damage (MetaCompositor *compositor,
if (window_actor == NULL)
return;
- meta_window_actor_process_damage (window_actor, event);
+ meta_window_actor_process_x11_damage (window_actor, event);
}
static void
@@ -346,29 +349,37 @@ void
meta_set_stage_input_region (MetaScreen *screen,
XserverRegion region)
{
- MetaCompScreen *info = meta_screen_get_compositor_data (screen);
- MetaDisplay *display = meta_screen_get_display (screen);
- Display *xdpy = meta_display_get_xdisplay (display);
-
- if (info->stage && info->output)
- {
- do_set_stage_input_region (screen, region);
- }
- else
+ /* 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.
+ */
+ if (!meta_is_display_server ())
{
- /* Reset info->pending_input_region if one existed before and set the new
- * one to use it later. */
- if (info->pending_input_region)
+ MetaCompScreen *info = meta_screen_get_compositor_data (screen);
+ MetaDisplay *display = meta_screen_get_display (screen);
+ Display *xdpy = meta_display_get_xdisplay (display);
+
+ if (info->stage && info->output)
{
- XFixesDestroyRegion (xdpy, info->pending_input_region);
- info->pending_input_region = None;
+ do_set_stage_input_region (screen, region);
}
- if (region != None)
+ else
{
- info->pending_input_region = XFixesCreateRegion (xdpy, NULL, 0);
- XFixesCopyRegion (xdpy, info->pending_input_region, region);
- }
- }
+ /* Reset info->pending_input_region if one existed before and set the new
+ * one to use it later. */
+ if (info->pending_input_region)
+ {
+ XFixesDestroyRegion (xdpy, info->pending_input_region);
+ info->pending_input_region = None;
+ }
+ if (region != None)
+ {
+ info->pending_input_region = XFixesCreateRegion (xdpy, NULL, 0);
+ XFixesCopyRegion (xdpy, info->pending_input_region, region);
+ }
+ }
+ }
}
void
@@ -541,6 +552,11 @@ redirect_windows (MetaCompositor *compositor,
guint n_retries;
guint max_retries;
+ /* 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() */
+
if (meta_get_replace_current_wm ())
max_retries = 5;
else
@@ -583,6 +599,9 @@ meta_compositor_manage_screen (MetaCompositor *compositor,
Display *xdisplay = meta_display_get_xdisplay (display);
Window xwin;
gint width, height;
+#ifdef HAVE_WAYLAND
+ MetaWaylandCompositor *wayland_compositor;
+#endif
/* Check if the screen is already managed */
if (meta_screen_get_compositor_data (screen))
@@ -595,7 +614,14 @@ 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.
*/
- info->pending_input_region = XFixesCreateRegion (xdisplay, NULL, 0);
+ if (!meta_is_display_server ())
+ 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;
+ }
info->screen = screen;
@@ -606,46 +632,58 @@ meta_compositor_manage_screen (MetaCompositor *compositor,
meta_screen_set_cm_selection (screen);
- info->stage = clutter_stage_new ();
-
- clutter_threads_add_repaint_func_full (CLUTTER_REPAINT_FLAGS_POST_PAINT,
- after_stage_paint,
- info, NULL);
-
- clutter_stage_set_sync_delay (CLUTTER_STAGE (info->stage), META_SYNC_DELAY);
-
- meta_screen_get_size (screen, &width, &height);
- clutter_actor_realize (info->stage);
-
- xwin = clutter_x11_get_stage_window (CLUTTER_STAGE (info->stage));
+ /* We will have already created a stage if running as a wayland
+ * compositor... */
+#ifdef HAVE_WAYLAND
+ if (meta_is_display_server ())
+ {
+ wayland_compositor = meta_wayland_compositor_get_default ();
+ info->stage = wayland_compositor->stage;
+ }
+ else
+#endif /* HAVE_WAYLAND */
+ {
+ info->stage = clutter_stage_new ();
- XResizeWindow (xdisplay, xwin, width, height);
+ clutter_threads_add_repaint_func_full (CLUTTER_REPAINT_FLAGS_POST_PAINT,
+ after_stage_paint,
+ info, NULL);
- {
- long event_mask;
- unsigned char mask_bits[XIMaskLen (XI_LASTEVENT)] = { 0 };
- XIEventMask mask = { XIAllMasterDevices, sizeof (mask_bits), mask_bits };
- XWindowAttributes attr;
+ clutter_stage_set_sync_delay (CLUTTER_STAGE (info->stage), META_SYNC_DELAY);
- meta_core_add_old_event_mask (xdisplay, xwin, &mask);
+ meta_screen_get_size (screen, &width, &height);
+ clutter_actor_realize (info->stage);
- XISetMask (mask.mask, XI_KeyPress);
- XISetMask (mask.mask, XI_KeyRelease);
- XISetMask (mask.mask, XI_ButtonPress);
- XISetMask (mask.mask, XI_ButtonRelease);
- XISetMask (mask.mask, XI_Enter);
- XISetMask (mask.mask, XI_Leave);
- XISetMask (mask.mask, XI_FocusIn);
- XISetMask (mask.mask, XI_FocusOut);
- XISetMask (mask.mask, XI_Motion);
- XISelectEvents (xdisplay, xwin, &mask, 1);
+ xwin = clutter_x11_get_stage_window (CLUTTER_STAGE (info->stage));
- event_mask = ExposureMask | PropertyChangeMask | StructureNotifyMask;
- if (XGetWindowAttributes (xdisplay, xwin, &attr))
- event_mask |= attr.your_event_mask;
+ XResizeWindow (xdisplay, xwin, width, height);
- XSelectInput (xdisplay, xwin, event_mask);
- }
+ {
+ long event_mask;
+ unsigned char mask_bits[XIMaskLen (XI_LASTEVENT)] = { 0 };
+ XIEventMask mask = { XIAllMasterDevices, sizeof (mask_bits), mask_bits };
+ XWindowAttributes attr;
+
+ meta_core_add_old_event_mask (xdisplay, xwin, &mask);
+
+ XISetMask (mask.mask, XI_KeyPress);
+ XISetMask (mask.mask, XI_KeyRelease);
+ XISetMask (mask.mask, XI_ButtonPress);
+ XISetMask (mask.mask, XI_ButtonRelease);
+ XISetMask (mask.mask, XI_Enter);
+ XISetMask (mask.mask, XI_Leave);
+ XISetMask (mask.mask, XI_FocusIn);
+ XISetMask (mask.mask, XI_FocusOut);
+ XISetMask (mask.mask, XI_Motion);
+ XISelectEvents (xdisplay, xwin, &mask, 1);
+
+ event_mask = ExposureMask | PropertyChangeMask | StructureNotifyMask;
+ if (XGetWindowAttributes (xdisplay, xwin, &attr))
+ event_mask |= attr.your_event_mask;
+
+ XSelectInput (xdisplay, xwin, event_mask);
+ }
+ }
info->window_group = meta_window_group_new (screen);
info->top_window_group = meta_window_group_new (screen);
@@ -657,55 +695,68 @@ meta_compositor_manage_screen (MetaCompositor *compositor,
info->plugin_mgr = meta_plugin_manager_new (screen);
- /*
- * 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
- * overlay window is not accessible; if the plugin needs to access it
- * directly, it should hook into the "show" signal on stage, and do
- * its stuff there.
- */
- info->output = get_output_window (screen);
- XReparentWindow (xdisplay, xwin, info->output, 0, 0);
-
- /* Make sure there isn't any left-over output shape on the
- * overlay window by setting the whole screen to be an
- * output region.
- *
- * Note: there doesn't seem to be any real chance of that
- * because the X server will destroy the overlay window
- * when the last client using it exits.
- */
- XFixesSetWindowShapeRegion (xdisplay, info->output, ShapeBounding, 0, 0, None);
-
- do_set_stage_input_region (screen, info->pending_input_region);
- if (info->pending_input_region != None)
+ if (meta_is_display_server ())
{
- XFixesDestroyRegion (xdisplay, info->pending_input_region);
- info->pending_input_region = None;
+ /* 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;
}
+ else
+ {
+ /*
+ * 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
+ * overlay window is not accessible; if the plugin needs to access it
+ * directly, it should hook into the "show" signal on stage, and do
+ * its stuff there.
+ */
+ info->output = get_output_window (screen);
+ XReparentWindow (xdisplay, xwin, info->output, 0, 0);
+
+ /* Make sure there isn't any left-over output shape on the
+ * overlay window by setting the whole screen to be an
+ * output region.
+ *
+ * Note: there doesn't seem to be any real chance of that
+ * because the X server will destroy the overlay window
+ * when the last client using it exits.
+ */
+ XFixesSetWindowShapeRegion (xdisplay, info->output, ShapeBounding, 0, 0, None);
- clutter_actor_show (info->overlay_group);
+ do_set_stage_input_region (screen, info->pending_input_region);
+ if (info->pending_input_region != None)
+ {
+ XFixesDestroyRegion (xdisplay, info->pending_input_region);
+ info->pending_input_region = None;
+ }
- /* Map overlay window before redirecting windows offscreen so we catch their
- * contents until we show the stage.
- */
- XMapWindow (xdisplay, info->output);
+ /* Map overlay window before redirecting windows offscreen so we catch their
+ * contents until we show the stage.
+ */
+ XMapWindow (xdisplay, info->output);
- redirect_windows (compositor, screen);
+ redirect_windows (compositor, screen);
+ }
+
+ clutter_actor_show (info->overlay_group);
}
void
meta_compositor_unmanage_screen (MetaCompositor *compositor,
MetaScreen *screen)
{
- MetaDisplay *display = meta_screen_get_display (screen);
- Display *xdisplay = meta_display_get_xdisplay (display);
- Window xroot = meta_screen_get_xroot (screen);
-
- /* This is the most important part of cleanup - we have to do this
- * before giving up the window manager selection or the next
- * window manager won't be able to redirect subwindows */
- XCompositeUnredirectSubwindows (xdisplay, xroot, CompositeRedirectManual);
+ if (!meta_is_display_server ())
+ {
+ MetaDisplay *display = meta_screen_get_display (screen);
+ Display *xdisplay = meta_display_get_xdisplay (display);
+ Window xroot = meta_screen_get_xroot (screen);
+
+ /* This is the most important part of cleanup - we have to do this
+ * before giving up the window manager selection or the next
+ * window manager won't be able to redirect subwindows */
+ XCompositeUnredirectSubwindows (xdisplay, xroot, CompositeRedirectManual);
+ }
}
/*
@@ -777,15 +828,18 @@ meta_compositor_remove_window (MetaCompositor *compositor,
if (!window_actor)
return;
- screen = meta_window_get_screen (window);
- info = meta_screen_get_compositor_data (screen);
-
- if (window_actor == info->unredirected_window)
+ if (!meta_is_display_server ())
{
- meta_window_actor_set_redirected (window_actor, TRUE);
- meta_shape_cow_for_window (meta_window_get_screen (meta_window_actor_get_meta_window
(info->unredirected_window)),
- NULL);
- info->unredirected_window = NULL;
+ screen = meta_window_get_screen (window);
+ info = meta_screen_get_compositor_data (screen);
+
+ if (window_actor == info->unredirected_window)
+ {
+ meta_window_actor_set_redirected (window_actor, TRUE);
+ meta_shape_cow_for_window (meta_window_get_screen (meta_window_actor_get_meta_window
(info->unredirected_window)),
+ NULL);
+ info->unredirected_window = NULL;
+ }
}
meta_window_actor_destroy (window_actor);
@@ -845,15 +899,15 @@ is_grabbed_event (MetaDisplay *display,
}
void
-meta_compositor_window_shape_changed (MetaCompositor *compositor,
- MetaWindow *window)
+meta_compositor_window_x11_shape_changed (MetaCompositor *compositor,
+ MetaWindow *window)
{
MetaWindowActor *window_actor;
window_actor = META_WINDOW_ACTOR (meta_window_get_compositor_private (window));
if (!window_actor)
return;
- meta_window_actor_update_shape (window_actor);
+ meta_window_actor_update_x11_shape (window_actor);
}
/* Clutter makes the assumption that there is only one X window
@@ -972,7 +1026,8 @@ meta_compositor_process_event (MetaCompositor *compositor,
break;
default:
- if (event->type == meta_display_get_damage_event_base (compositor->display) + XDamageNotify)
+ if (!meta_is_display_server () &&
+ 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
* ourselves
@@ -991,7 +1046,7 @@ meta_compositor_process_event (MetaCompositor *compositor,
/* Clutter needs to know about MapNotify events otherwise it will
think the stage is invisible */
- if (event->type == MapNotify)
+ if (!meta_is_display_server () && event->type == MapNotify)
clutter_x11_handle_event (event);
/* The above handling is basically just "observing" the events, so we return
@@ -1333,22 +1388,33 @@ meta_compositor_sync_screen_size (MetaCompositor *compositor,
guint width,
guint height)
{
- MetaDisplay *display = meta_screen_get_display (screen);
- MetaCompScreen *info = meta_screen_get_compositor_data (screen);
- Display *xdisplay;
- Window xwin;
+ if (meta_is_display_server ())
+ {
+ /* 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");
+ }
+ else
+ {
+ MetaDisplay *display = meta_screen_get_display (screen);
+ MetaCompScreen *info = meta_screen_get_compositor_data (screen);
+ Display *xdisplay;
+ Window xwin;
- DEBUG_TRACE ("meta_compositor_sync_screen_size\n");
- g_return_if_fail (info);
+ DEBUG_TRACE ("meta_compositor_sync_screen_size\n");
+ g_return_if_fail (info);
- xdisplay = meta_display_get_xdisplay (display);
- xwin = clutter_x11_get_stage_window (CLUTTER_STAGE (info->stage));
+ xdisplay = meta_display_get_xdisplay (display);
+ xwin = clutter_x11_get_stage_window (CLUTTER_STAGE (info->stage));
- XResizeWindow (xdisplay, xwin, width, height);
+ XResizeWindow (xdisplay, xwin, width, height);
- meta_verbose ("Changed size for stage on screen %d to %dx%d\n",
- meta_screen_get_screen_number (screen),
- width, height);
+ meta_verbose ("Changed size for stage on screen %d to %dx%d\n",
+ meta_screen_get_screen_number (screen),
+ width, height);
+ }
}
static void
@@ -1412,29 +1478,32 @@ pre_paint_windows (MetaCompScreen *info)
if (info->windows == NULL)
return;
- top_window = g_list_last (info->windows)->data;
+ if (!meta_is_display_server ())
+ {
+ top_window = g_list_last (info->windows)->data;
- if (meta_window_actor_should_unredirect (top_window) &&
- info->disable_unredirect_count == 0)
- expected_unredirected_window = top_window;
+ if (meta_window_actor_should_unredirect (top_window) &&
+ info->disable_unredirect_count == 0)
+ expected_unredirected_window = top_window;
- if (info->unredirected_window != expected_unredirected_window)
- {
- if (info->unredirected_window != NULL)
+ if (info->unredirected_window != expected_unredirected_window)
{
- meta_window_actor_set_redirected (info->unredirected_window, TRUE);
- meta_shape_cow_for_window (meta_window_get_screen (meta_window_actor_get_meta_window
(info->unredirected_window)),
- NULL);
- }
+ if (info->unredirected_window != NULL)
+ {
+ meta_window_actor_set_redirected (info->unredirected_window, TRUE);
+ meta_shape_cow_for_window (meta_window_get_screen (meta_window_actor_get_meta_window
(info->unredirected_window)),
+ NULL);
+ }
- if (expected_unredirected_window != NULL)
- {
- meta_shape_cow_for_window (meta_window_get_screen (meta_window_actor_get_meta_window (top_window)),
- meta_window_actor_get_meta_window (top_window));
- meta_window_actor_set_redirected (top_window, FALSE);
- }
+ if (expected_unredirected_window != NULL)
+ {
+ meta_shape_cow_for_window (meta_window_get_screen (meta_window_actor_get_meta_window
(top_window)),
+ meta_window_actor_get_meta_window (top_window));
+ meta_window_actor_set_redirected (top_window, FALSE);
+ }
- info->unredirected_window = expected_unredirected_window;
+ info->unredirected_window = expected_unredirected_window;
+ }
}
for (l = info->windows; l; l = l->next)
diff --git a/src/compositor/meta-plugin-manager.c b/src/compositor/meta-plugin-manager.c
index 130a82b..ad2ae7b 100644
--- a/src/compositor/meta-plugin-manager.c
+++ b/src/compositor/meta-plugin-manager.c
@@ -317,6 +317,9 @@ meta_plugin_manager_xevent_filter (MetaPluginManager *plugin_mgr,
*/
if (klass->xevent_filter)
return klass->xevent_filter (plugin, xev);
+
+ if (meta_is_display_server ())
+ return FALSE;
else
return clutter_x11_handle_event (xev) != CLUTTER_X11_FILTER_CONTINUE;
}
diff --git a/src/compositor/meta-shaped-texture.c b/src/compositor/meta-shaped-texture.c
index c93bb47..23382c8 100644
--- a/src/compositor/meta-shaped-texture.c
+++ b/src/compositor/meta-shaped-texture.c
@@ -30,8 +30,14 @@
#include <config.h>
#include <meta/meta-shaped-texture.h>
+#include <meta/util.h>
#include "meta-texture-tower.h"
+#ifdef HAVE_WAYLAND
+#include "meta-wayland-private.h"
+#include <cogl/cogl-wayland-server.h>
+#endif
+
#include <clutter/clutter.h>
#include <cogl/cogl.h>
#include <cogl/cogl-texture-pixmap-x11.h>
@@ -55,6 +61,15 @@ static void meta_shaped_texture_get_preferred_height (ClutterActor *self,
static gboolean meta_shaped_texture_get_paint_volume (ClutterActor *self, ClutterPaintVolume *volume);
+typedef enum _MetaShapedTextureType
+{
+ META_SHAPED_TEXTURE_TYPE_X11_PIXMAP,
+#ifdef HAVE_WAYLAND
+ META_SHAPED_TEXTURE_TYPE_WAYLAND_SURFACE,
+#endif
+} MetaShapedTextureType;
+
+
G_DEFINE_TYPE (MetaShapedTexture, meta_shaped_texture,
CLUTTER_TYPE_ACTOR);
@@ -65,8 +80,23 @@ G_DEFINE_TYPE (MetaShapedTexture, meta_shaped_texture,
struct _MetaShapedTexturePrivate
{
MetaTextureTower *paint_tower;
- Pixmap pixmap;
- CoglTexturePixmapX11 *texture;
+
+ MetaShapedTextureType type;
+ union {
+ struct {
+ Pixmap pixmap;
+ } x11;
+#ifdef HAVE_WAYLAND
+ struct {
+ MetaWaylandSurface *surface;
+#warning "FIXME: now that we track the surface we don't need to track the buffer separately"
+ //struct wl_buffer *buffer;
+ } wayland;
+#endif
+ };
+
+ CoglTexture *texture;
+
CoglTexture *mask_texture;
CoglPipeline *pipeline;
CoglPipeline *pipeline_unshaped;
@@ -104,7 +134,10 @@ meta_shaped_texture_init (MetaShapedTexture *self)
priv = self->priv = META_SHAPED_TEXTURE_GET_PRIVATE (self);
priv->paint_tower = meta_texture_tower_new ();
+
+ priv->type = META_SHAPED_TEXTURE_TYPE_X11_PIXMAP;
priv->texture = NULL;
+
priv->mask_texture = NULL;
priv->create_mipmaps = TRUE;
}
@@ -130,6 +163,56 @@ meta_shaped_texture_dispose (GObject *object)
}
static void
+set_cogl_texture (MetaShapedTexture *stex,
+ CoglTexture *cogl_tex)
+{
+ MetaShapedTexturePrivate *priv;
+ guint width, height;
+
+ g_return_if_fail (META_IS_SHAPED_TEXTURE (stex));
+
+ priv = stex->priv;
+
+ if (priv->texture)
+ cogl_object_unref (priv->texture);
+
+ priv->texture = cogl_tex;
+
+ if (priv->pipeline != NULL)
+ cogl_pipeline_set_layer_texture (priv->pipeline, 0, COGL_TEXTURE (cogl_tex));
+
+ if (priv->pipeline_unshaped != NULL)
+ cogl_pipeline_set_layer_texture (priv->pipeline_unshaped, 0, COGL_TEXTURE (cogl_tex));
+
+ if (cogl_tex != NULL)
+ {
+ width = cogl_texture_get_width (COGL_TEXTURE (cogl_tex));
+ height = cogl_texture_get_height (COGL_TEXTURE (cogl_tex));
+
+ if (width != priv->tex_width ||
+ height != priv->tex_height)
+ {
+ priv->tex_width = width;
+ priv->tex_height = height;
+
+ clutter_actor_queue_relayout (CLUTTER_ACTOR (stex));
+ }
+ }
+ else
+ {
+ /* size changed to 0 going to an invalid handle */
+ priv->tex_width = 0;
+ priv->tex_height = 0;
+ clutter_actor_queue_relayout (CLUTTER_ACTOR (stex));
+ }
+
+ /* NB: We don't queue a redraw of the actor here because we don't
+ * know how much of the buffer has changed with respect to the
+ * previous buffer. We only queue a redraw in response to surface
+ * damage. */
+}
+
+static void
meta_shaped_texture_paint (ClutterActor *actor)
{
MetaShapedTexture *stex = (MetaShapedTexture *) actor;
@@ -312,6 +395,7 @@ meta_shaped_texture_pick (ClutterActor *actor,
*/
n_rects = cairo_region_num_rectangles (priv->input_shape_region);
+ rectangles = g_alloca (sizeof (float) * 4 * n_rects);
for (i = 0; i < n_rects; i++)
{
@@ -380,12 +464,55 @@ meta_shaped_texture_get_paint_volume (ClutterActor *self,
return clutter_paint_volume_set_from_allocation (volume, self);
}
+#ifdef HAVE_WAYLAND
ClutterActor *
-meta_shaped_texture_new (void)
+meta_shaped_texture_new_with_wayland_surface (MetaWaylandSurface *surface)
+{
+ ClutterActor *actor = g_object_new (META_TYPE_SHAPED_TEXTURE, NULL);
+ MetaShapedTexturePrivate *priv = META_SHAPED_TEXTURE (actor)->priv;
+
+ /* XXX: it could probably be better to have a "type" construct-only
+ * property or create wayland/x11 subclasses */
+ priv->type = META_SHAPED_TEXTURE_TYPE_WAYLAND_SURFACE;
+
+ meta_shaped_texture_set_wayland_surface (META_SHAPED_TEXTURE (actor),
+ surface);
+
+ return actor;
+}
+
+void
+meta_shaped_texture_set_wayland_surface (MetaShapedTexture *stex,
+ MetaWaylandSurface *surface)
+{
+ MetaShapedTexturePrivate *priv = stex->priv;
+
+ priv->wayland.surface = surface;
+
+ if (surface && surface->buffer)
+ meta_shaped_texture_attach_wayland_buffer (stex, surface->buffer);
+}
+
+MetaWaylandSurface *
+meta_shaped_texture_get_wayland_surface (MetaShapedTexture *stex)
{
- ClutterActor *self = g_object_new (META_TYPE_SHAPED_TEXTURE, NULL);
+ MetaShapedTexturePrivate *priv = stex->priv;
+ return priv->wayland.surface;
+}
+#endif /* HAVE_WAYLAND */
- return self;
+ClutterActor *
+meta_shaped_texture_new_with_xwindow (Window xwindow)
+{
+#ifdef HAVE_WAYLAND
+ if (meta_is_display_server ())
+ {
+ MetaWaylandSurface *surface = meta_wayland_lookup_surface_for_xid (xwindow);
+ return meta_shaped_texture_new_with_wayland_surface (surface);
+ }
+ else
+#endif
+ return g_object_new (META_TYPE_SHAPED_TEXTURE, NULL);
}
void
@@ -404,8 +531,7 @@ meta_shaped_texture_set_create_mipmaps (MetaShapedTexture *stex,
{
CoglTexture *base_texture;
priv->create_mipmaps = create_mipmaps;
- base_texture = create_mipmaps ?
- COGL_TEXTURE (priv->texture) : NULL;
+ base_texture = create_mipmaps ? priv->texture : NULL;
meta_texture_tower_set_base_texture (priv->paint_tower, base_texture);
}
}
@@ -431,74 +557,140 @@ meta_shaped_texture_set_mask_texture (MetaShapedTexture *stex,
clutter_actor_queue_redraw (CLUTTER_ACTOR (stex));
}
-void
-meta_shaped_texture_update_area (MetaShapedTexture *stex,
- int x,
- int y,
- int width,
- int height)
+#ifdef HAVE_WAYLAND
+static void
+wayland_surface_update_area (MetaShapedTexture *stex,
+ int x,
+ int y,
+ int width,
+ int height)
{
MetaShapedTexturePrivate *priv;
- const cairo_rectangle_int_t clip = { x, y, width, height };
+ struct wl_buffer *buffer;
priv = stex->priv;
- if (priv->texture == NULL)
- return;
+ g_return_if_fail (priv->type == META_SHAPED_TEXTURE_TYPE_WAYLAND_SURFACE);
+ g_return_if_fail (priv->texture != NULL);
- cogl_texture_pixmap_x11_update_area (priv->texture,
- x, y, width, height);
+ buffer = priv->wayland.surface->buffer;
- meta_texture_tower_update_area (priv->paint_tower, x, y, width, height);
+ if (buffer && wl_buffer_is_shm (buffer))
+ {
+ CoglPixelFormat format;
- clutter_actor_queue_redraw_with_clip (CLUTTER_ACTOR (stex), &clip);
+ switch (wl_shm_buffer_get_format (buffer))
+ {
+#if G_BYTE_ORDER == G_BIG_ENDIAN
+ case WL_SHM_FORMAT_ARGB8888:
+ format = COGL_PIXEL_FORMAT_ARGB_8888_PRE;
+ break;
+ case WL_SHM_FORMAT_XRGB8888:
+ format = COGL_PIXEL_FORMAT_ARGB_8888;
+ break;
+#elif G_BYTE_ORDER == G_LITTLE_ENDIAN
+ case WL_SHM_FORMAT_ARGB8888:
+ format = COGL_PIXEL_FORMAT_BGRA_8888_PRE;
+ break;
+ case WL_SHM_FORMAT_XRGB8888:
+ format = COGL_PIXEL_FORMAT_BGRA_8888;
+ break;
+#endif
+ default:
+ g_warn_if_reached ();
+ format = COGL_PIXEL_FORMAT_ARGB_8888;
+ }
+
+ cogl_texture_set_region (priv->texture,
+ x, y,
+ x, y,
+ width, height,
+ width, height,
+ format,
+ wl_shm_buffer_get_stride (buffer),
+ wl_shm_buffer_get_data (buffer));
+ }
}
+#endif /* HAVE_WAYLAND */
static void
-set_cogl_texture (MetaShapedTexture *stex,
- CoglTexturePixmapX11 *cogl_tex)
+queue_damage_redraw_with_clip (MetaShapedTexture *stex,
+ int x,
+ int y,
+ int width,
+ int height)
{
+ ClutterActor *self = CLUTTER_ACTOR (stex);
MetaShapedTexturePrivate *priv;
- guint width, height;
+ ClutterActorBox allocation;
+ float scale_x;
+ float scale_y;
+ cairo_rectangle_int_t clip;
+
+ /* NB: clutter_actor_queue_redraw_with_clip expects a box in the actor's
+ * coordinate space so we need to convert from surface coordinates to
+ * actor coordinates...
+ */
- g_return_if_fail (META_IS_SHAPED_TEXTURE (stex));
+ /* Calling clutter_actor_get_allocation_box() is enormously expensive
+ * if the actor has an out-of-date allocation, since it triggers
+ * a full redraw. clutter_actor_queue_redraw_with_clip() would redraw
+ * the whole stage anyways in that case, so just go ahead and do
+ * it here.
+ */
+ if (!clutter_actor_has_allocation (self))
+ {
+ clutter_actor_queue_redraw (self);
+ return;
+ }
priv = stex->priv;
- if (priv->texture != NULL)
- cogl_object_unref (priv->texture);
+ if (priv->tex_width == 0 || priv->tex_height == 0)
+ return;
- priv->texture = cogl_tex;
+ clutter_actor_get_allocation_box (self, &allocation);
- if (priv->pipeline != NULL)
- cogl_pipeline_set_layer_texture (priv->pipeline, 0, COGL_TEXTURE (cogl_tex));
+ scale_x = (allocation.x2 - allocation.x1) / priv->tex_width;
+ scale_y = (allocation.y2 - allocation.y1) / priv->tex_height;
- if (priv->pipeline_unshaped != NULL)
- cogl_pipeline_set_layer_texture (priv->pipeline_unshaped, 0, COGL_TEXTURE (cogl_tex));
+ clip.x = x * scale_x;
+ clip.y = y * scale_y;
+ clip.width = width * scale_x;
+ clip.height = height * scale_y;
+ clutter_actor_queue_redraw_with_clip (self, &clip);
+}
- if (cogl_tex != NULL)
- {
- width = cogl_texture_get_width (COGL_TEXTURE (cogl_tex));
- height = cogl_texture_get_height (COGL_TEXTURE (cogl_tex));
+void
+meta_shaped_texture_update_area (MetaShapedTexture *stex,
+ int x,
+ int y,
+ int width,
+ int height)
+{
+ MetaShapedTexturePrivate *priv;
- if (width != priv->tex_width ||
- height != priv->tex_height)
- {
- priv->tex_width = width;
- priv->tex_height = height;
+ priv = stex->priv;
- clutter_actor_queue_relayout (CLUTTER_ACTOR (stex));
- }
- }
- else
+ if (priv->texture == NULL)
+ return;
+
+ switch (priv->type)
{
- /* size changed to 0 going to an inavlid texture */
- priv->tex_width = 0;
- priv->tex_height = 0;
- clutter_actor_queue_relayout (CLUTTER_ACTOR (stex));
+ case META_SHAPED_TEXTURE_TYPE_X11_PIXMAP:
+ cogl_texture_pixmap_x11_update_area (COGL_TEXTURE_PIXMAP_X11 (priv->texture),
+ x, y, width, height);
+ break;
+#ifdef HAVE_WAYLAND
+ case META_SHAPED_TEXTURE_TYPE_WAYLAND_SURFACE:
+ wayland_surface_update_area (stex, x, y, width, height);
+ break;
+#endif
}
- clutter_actor_queue_redraw (CLUTTER_ACTOR (stex));
+ meta_texture_tower_update_area (priv->paint_tower, x, y, width, height);
+
+ queue_damage_redraw_with_clip (stex, x, y, width, height);
}
/**
@@ -516,16 +708,55 @@ meta_shaped_texture_set_pixmap (MetaShapedTexture *stex,
priv = stex->priv;
- if (priv->pixmap == pixmap)
+ if (priv->x11.pixmap == pixmap)
return;
- priv->pixmap = pixmap;
+ priv->x11.pixmap = pixmap;
if (pixmap != None)
{
CoglContext *ctx =
clutter_backend_get_cogl_context (clutter_get_default_backend ());
- set_cogl_texture (stex, cogl_texture_pixmap_x11_new (ctx, pixmap, FALSE, NULL));
+ CoglTexture *texture =
+ COGL_TEXTURE (cogl_texture_pixmap_x11_new (ctx, pixmap, FALSE, NULL));
+ set_cogl_texture (stex, texture);
+ }
+ else
+ set_cogl_texture (stex, NULL);
+
+ if (priv->create_mipmaps)
+ meta_texture_tower_set_base_texture (priv->paint_tower,
+ COGL_TEXTURE (priv->texture));
+}
+
+#ifdef HAVE_WAYLAND
+void
+meta_shaped_texture_attach_wayland_buffer (MetaShapedTexture *stex,
+ struct wl_buffer *buffer)
+{
+ MetaShapedTexturePrivate *priv;
+
+ g_return_if_fail (META_IS_SHAPED_TEXTURE (stex));
+
+ priv = stex->priv;
+
+ /* TODO: we should change this api to be something like
+ * meta_shaped_texture_notify_buffer_attach() since we now maintain
+ * a reference to the MetaWaylandSurface where we can access the
+ * buffer without it being explicitly passed as an argument.
+ */
+ g_return_if_fail (priv->wayland.surface->buffer == buffer);
+
+ if (buffer)
+ {
+ CoglContext *ctx =
+ clutter_backend_get_cogl_context (clutter_get_default_backend ());
+ CoglError *catch_error = NULL;
+ CoglTexture *texture =
+ COGL_TEXTURE (cogl_wayland_texture_2d_new_from_buffer (ctx, buffer, &catch_error));
+ if (!texture)
+ cogl_error_free (catch_error);
+ set_cogl_texture (stex, texture);
}
else
set_cogl_texture (stex, NULL);
@@ -534,6 +765,7 @@ meta_shaped_texture_set_pixmap (MetaShapedTexture *stex,
meta_texture_tower_set_base_texture (priv->paint_tower,
COGL_TEXTURE (priv->texture));
}
+#endif /* HAVE_WAYLAND */
/**
* meta_shaped_texture_get_texture:
diff --git a/src/compositor/meta-window-actor-private.h b/src/compositor/meta-window-actor-private.h
index 90a9e35..1daa84c 100644
--- a/src/compositor/meta-window-actor-private.h
+++ b/src/compositor/meta-window-actor-private.h
@@ -5,6 +5,11 @@
#include <config.h>
+#ifdef HAVE_WAYLAND
+#include <wayland-server.h>
+#include <meta-wayland-private.h>
+#endif
+
#include <X11/extensions/Xdamage.h>
#include <meta/compositor-mutter.h>
@@ -24,8 +29,20 @@ void meta_window_actor_unmaximize (MetaWindowActor *self,
MetaRectangle *old_rect,
MetaRectangle *new_rect);
-void meta_window_actor_process_damage (MetaWindowActor *self,
- XDamageNotifyEvent *event);
+void meta_window_actor_process_x11_damage (MetaWindowActor *self,
+ XDamageNotifyEvent *event);
+
+#ifdef HAVE_WAYLAND
+void meta_window_actor_process_wayland_damage (MetaWindowActor *self,
+ int x,
+ int y,
+ int width,
+ int height);
+void meta_window_actor_set_wayland_surface (MetaWindowActor *self,
+ MetaWaylandSurface *surface);
+void meta_window_actor_attach_wayland_buffer (MetaWindowActor *self,
+ struct wl_buffer *buffer);
+#endif
void meta_window_actor_pre_paint (MetaWindowActor *self);
void meta_window_actor_post_paint (MetaWindowActor *self);
@@ -46,7 +63,7 @@ gboolean meta_window_actor_effect_in_progress (MetaWindowActor *self);
void meta_window_actor_sync_actor_geometry (MetaWindowActor *self,
gboolean did_placement);
void meta_window_actor_sync_visibility (MetaWindowActor *self);
-void meta_window_actor_update_shape (MetaWindowActor *self);
+void meta_window_actor_update_x11_shape (MetaWindowActor *self);
void meta_window_actor_update_opacity (MetaWindowActor *self);
void meta_window_actor_mapped (MetaWindowActor *self);
void meta_window_actor_unmapped (MetaWindowActor *self);
diff --git a/src/compositor/meta-window-actor.c b/src/compositor/meta-window-actor.c
index ccb5391..a2387be 100644
--- a/src/compositor/meta-window-actor.c
+++ b/src/compositor/meta-window-actor.c
@@ -32,6 +32,9 @@
#include "meta-window-actor-private.h"
#include "meta-texture-rectangle.h"
#include "region-utils.h"
+#ifdef HAVE_WAYLAND
+#include "meta-wayland-private.h"
+#endif
enum {
POSITION_CHANGED,
@@ -64,9 +67,9 @@ struct _MetaWindowActorPrivate
MetaShadow *focused_shadow;
MetaShadow *unfocused_shadow;
- Pixmap back_pixmap;
+ Pixmap back_pixmap; /* Not used in display server mode */
- Damage damage;
+ Damage damage; /* Not used in display server mode */
guint8 opacity;
guint8 shadow_opacity;
@@ -113,20 +116,22 @@ struct _MetaWindowActorPrivate
guint disposed : 1;
guint redecorating : 1;
- guint needs_damage_all : 1;
- guint received_damage : 1;
- guint repaint_scheduled : 1;
+ guint needs_damage_all : 1; /* Not used in display server mode */
+ guint received_x11_damage : 1; /* Not used in display server mode */
+
+ guint needs_pixmap : 1; /* Not used in display server mode */
/* If set, the client needs to be sent a _NET_WM_FRAME_DRAWN
* client message using the most recent frame in ->frames */
guint needs_frame_drawn : 1;
+ guint repaint_scheduled : 1;
- guint needs_pixmap : 1;
- guint needs_reshape : 1;
+ guint needs_x11_reshape : 1;
guint recompute_focused_shadow : 1;
guint recompute_unfocused_shadow : 1;
- guint size_changed : 1;
- guint updates_frozen : 1;
+
+ guint size_changed : 1; /* Not used in display server mode */
+ guint updates_frozen : 1; /* Not used in display server mode */
guint needs_destroy : 1;
@@ -134,11 +139,11 @@ struct _MetaWindowActorPrivate
guint no_more_x_calls : 1;
- guint unredirected : 1;
+ guint unredirected : 1; /* Not used in display server mode */
/* This is used to detect fullscreen windows that need to be unredirected */
- guint full_damage_frames_count;
- guint does_full_damage : 1;
+ guint full_damage_frames_count; /* Not used in display server mode */
+ guint does_full_damage : 1; /* Not used in display server mode */
};
typedef struct _FrameData FrameData;
@@ -182,7 +187,7 @@ static gboolean meta_window_actor_has_shadow (MetaWindowActor *self);
static void meta_window_actor_handle_updates (MetaWindowActor *self);
-static void check_needs_reshape (MetaWindowActor *self);
+static void check_needs_x11_reshape (MetaWindowActor *self);
G_DEFINE_TYPE (MetaWindowActor, meta_window_actor, CLUTTER_TYPE_ACTOR);
@@ -311,18 +316,21 @@ window_decorated_notify (MetaWindow *mw,
else
new_xwindow = meta_window_get_xwindow (mw);
- meta_window_actor_detach (self);
-
- /*
- * First of all, clean up any resources we are currently using and will
- * be replacing.
- */
- if (priv->damage != None)
+ if (!meta_is_display_server ())
{
- meta_error_trap_push (display);
- XDamageDestroy (xdisplay, priv->damage);
- meta_error_trap_pop (display);
- priv->damage = None;
+ meta_window_actor_detach (self);
+
+ /*
+ * First of all, clean up any resources we are currently using and will
+ * be replacing.
+ */
+ if (priv->damage != None)
+ {
+ meta_error_trap_push (display);
+ XDamageDestroy (xdisplay, priv->damage);
+ meta_error_trap_pop (display);
+ priv->damage = None;
+ }
}
g_free (priv->desc);
@@ -356,8 +364,9 @@ meta_window_actor_constructed (GObject *object)
Display *xdisplay = meta_display_get_xdisplay (display);
XRenderPictFormat *format;
- priv->damage = XDamageCreate (xdisplay, xwindow,
- XDamageReportBoundingBox);
+ if (!meta_is_display_server ())
+ priv->damage = XDamageCreate (xdisplay, xwindow,
+ XDamageReportBoundingBox);
format = XRenderFindVisualFormat (xdisplay, window->xvisual);
@@ -366,7 +375,12 @@ meta_window_actor_constructed (GObject *object)
if (!priv->actor)
{
- priv->actor = meta_shaped_texture_new ();
+ if (window->client_type == META_WINDOW_CLIENT_TYPE_X11)
+ priv->actor = meta_shaped_texture_new_with_xwindow (xwindow);
+#ifdef HAVE_WAYLAND
+ else
+ priv->actor = meta_shaped_texture_new_with_wayland_surface (window->surface);
+#endif
clutter_actor_add_child (CLUTTER_ACTOR (self), priv->actor);
@@ -416,11 +430,15 @@ meta_window_actor_dispose (GObject *object)
priv->disposed = TRUE;
screen = priv->screen;
- display = meta_screen_get_display (screen);
- xdisplay = meta_display_get_xdisplay (display);
info = meta_screen_get_compositor_data (screen);
- meta_window_actor_detach (self);
+ if (!meta_is_display_server ())
+ {
+ display = meta_screen_get_display (screen);
+ xdisplay = meta_display_get_xdisplay (display);
+
+ meta_window_actor_detach (self);
+ }
g_clear_pointer (&priv->shape_region, cairo_region_destroy);
g_clear_pointer (&priv->input_shape_region, cairo_region_destroy);
@@ -432,7 +450,7 @@ meta_window_actor_dispose (GObject *object)
g_clear_pointer (&priv->unfocused_shadow, meta_shadow_unref);
g_clear_pointer (&priv->shadow_shape, meta_window_shape_unref);
- if (priv->damage != None)
+ if (!meta_is_display_server () && priv->damage != None)
{
meta_error_trap_push (display);
XDamageDestroy (xdisplay, priv->damage);
@@ -924,7 +942,8 @@ meta_window_actor_showing_on_its_workspace (MetaWindowActor *self)
static void
meta_window_actor_freeze (MetaWindowActor *self)
{
- self->priv->freeze_count++;
+ if (!meta_is_display_server ())
+ self->priv->freeze_count++;
}
static void
@@ -953,30 +972,33 @@ meta_window_actor_damage_all (MetaWindowActor *self)
static void
meta_window_actor_thaw (MetaWindowActor *self)
{
- self->priv->freeze_count--;
-
- if (G_UNLIKELY (self->priv->freeze_count < 0))
+ if (!meta_is_display_server ())
{
- g_warning ("Error in freeze/thaw accounting.");
- self->priv->freeze_count = 0;
- return;
- }
+ self->priv->freeze_count--;
- if (self->priv->freeze_count)
- return;
+ if (G_UNLIKELY (self->priv->freeze_count < 0))
+ {
+ g_warning ("Error in freeze/thaw accounting.");
+ self->priv->freeze_count = 0;
+ return;
+ }
- /* We sometimes ignore moves and resizes on frozen windows */
- meta_window_actor_sync_actor_geometry (self, FALSE);
+ if (self->priv->freeze_count)
+ return;
- /* We do this now since we might be going right back into the
- * frozen state */
- meta_window_actor_handle_updates (self);
+ /* We sometimes ignore moves and resizes on frozen windows */
+ meta_window_actor_sync_actor_geometry (self, FALSE);
- /* 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);
+ /* We do this now since we might be going right back into the
+ * frozen state */
+ meta_window_actor_handle_updates (self);
+
+ /* 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);
+ }
}
void
@@ -1007,7 +1029,7 @@ meta_window_actor_queue_frame_drawn (MetaWindowActor *self,
* send a _NET_WM_FRAME_DRAWN. We do a 1-pixel redraw to get
* consistent timing with non-empty frames.
*/
- if (priv->mapped && !priv->needs_pixmap)
+ if (priv->mapped && (!meta_is_display_server () || !priv->needs_pixmap))
{
const cairo_rectangle_int_t clip = { 0, 0, 1, 1 };
clutter_actor_queue_redraw_with_clip (priv->actor, &clip);
@@ -1033,7 +1055,7 @@ is_frozen (MetaWindowActor *self)
}
static void
-meta_window_actor_queue_create_pixmap (MetaWindowActor *self)
+meta_window_actor_queue_create_x11_pixmap (MetaWindowActor *self)
{
MetaWindowActorPrivate *priv = self->priv;
@@ -1137,11 +1159,14 @@ meta_window_actor_after_effects (MetaWindowActor *self)
meta_window_actor_sync_visibility (self);
meta_window_actor_sync_actor_geometry (self, FALSE);
- if (!meta_window_is_mapped (priv->window))
- meta_window_actor_detach (self);
+ if (!meta_is_display_server ())
+ {
+ if (!meta_window_is_mapped (priv->window))
+ meta_window_actor_detach (self);
- if (priv->needs_pixmap)
- clutter_actor_queue_redraw (priv->actor);
+ if (priv->needs_pixmap)
+ clutter_actor_queue_redraw (priv->actor);
+ }
}
void
@@ -1243,7 +1268,7 @@ meta_window_actor_detach (MetaWindowActor *self)
XFreePixmap (xdisplay, priv->back_pixmap);
priv->back_pixmap = None;
- meta_window_actor_queue_create_pixmap (self);
+ meta_window_actor_queue_create_x11_pixmap (self);
}
gboolean
@@ -1273,7 +1298,7 @@ meta_window_actor_should_unredirect (MetaWindowActor *self)
if (meta_window_is_override_redirect (metaWindow))
return TRUE;
- if (priv->does_full_damage)
+ if (!meta_is_display_server () && priv->does_full_damage)
return TRUE;
return FALSE;
@@ -1377,9 +1402,13 @@ meta_window_actor_sync_actor_geometry (MetaWindowActor *self,
if (priv->last_width != window_rect.width ||
priv->last_height != window_rect.height)
{
- priv->size_changed = TRUE;
- meta_window_actor_queue_create_pixmap (self);
- meta_window_actor_update_shape (self);
+ if (!meta_is_display_server ())
+ {
+ priv->size_changed = TRUE;
+ meta_window_actor_queue_create_x11_pixmap (self);
+ }
+
+ meta_window_actor_update_x11_shape (self);
priv->last_width = window_rect.width;
priv->last_height = window_rect.height;
@@ -1546,16 +1575,27 @@ meta_window_actor_new (MetaWindow *window)
MetaWindowActor *self;
MetaWindowActorPrivate *priv;
MetaFrame *frame;
- Window top_window;
+ Window top_window = None;
ClutterActor *window_group;
- frame = meta_window_get_frame (window);
- if (frame)
- top_window = meta_frame_get_xwindow (frame);
- else
- top_window = meta_window_get_xwindow (window);
+ if (window->client_type == META_WINDOW_CLIENT_TYPE_X11)
+ {
+ frame = meta_window_get_frame (window);
+ if (frame)
+ top_window = meta_frame_get_xwindow (frame);
+ else
+ top_window = meta_window_get_xwindow (window);
- meta_verbose ("add window: Meta %p, xwin 0x%x\n", window, (guint)top_window);
+ meta_verbose ("add window: Meta %p, xwin 0x%x\n", window, (guint)top_window);
+ }
+#ifdef HAVE_WAYLAND
+ else
+ {
+ meta_verbose ("add window: Meta %p, wayland surface %p\n",
+ window, window->surface);
+ top_window = None;
+ }
+#endif
self = g_object_new (META_TYPE_WINDOW_ACTOR,
"meta-window", window,
@@ -1565,21 +1605,25 @@ meta_window_actor_new (MetaWindow *window)
priv = self->priv;
- priv->last_width = -1;
- priv->last_height = -1;
+ if (!meta_is_display_server ())
+ {
+ priv->last_width = -1;
+ priv->last_height = -1;
- priv->mapped = meta_window_toplevel_is_mapped (priv->window);
- if (priv->mapped)
- meta_window_actor_queue_create_pixmap (self);
+ priv->mapped = meta_window_toplevel_is_mapped (priv->window);
+ if (priv->mapped)
+ meta_window_actor_queue_create_x11_pixmap (self);
- meta_window_actor_set_updates_frozen (self,
- meta_window_updates_are_frozen (priv->window));
+ meta_window_actor_set_updates_frozen (self,
+ meta_window_updates_are_frozen (priv->window));
- /* If a window doesn't start off with updates frozen, we should
- * we should send a _NET_WM_FRAME_DRAWN immediately after the first drawn.
- */
- if (priv->window->extended_sync_request_counter && !priv->updates_frozen)
- meta_window_actor_queue_frame_drawn (self, FALSE);
+ /* If a window doesn't start off with updates frozen, we should
+ * we should send a _NET_WM_FRAME_DRAWN immediately after the first drawn.
+ */
+ if (priv->window->extended_sync_request_counter && !priv->updates_frozen)
+ meta_window_actor_queue_frame_drawn (self, FALSE);
+ }
+#warning "FIXME: need to figure out how to handle _FRAME_DRAWN equivalent for wayland"
meta_window_actor_sync_actor_geometry (self, priv->window->placed);
@@ -1614,7 +1658,8 @@ meta_window_actor_mapped (MetaWindowActor *self)
priv->mapped = TRUE;
- meta_window_actor_queue_create_pixmap (self);
+ if (!meta_is_display_server ())
+ meta_window_actor_queue_create_x11_pixmap (self);
}
void
@@ -1629,8 +1674,11 @@ meta_window_actor_unmapped (MetaWindowActor *self)
if (meta_window_actor_effect_in_progress (self))
return;
- meta_window_actor_detach (self);
- priv->needs_pixmap = FALSE;
+ if (!meta_is_display_server ())
+ {
+ meta_window_actor_detach (self);
+ priv->needs_pixmap = FALSE;
+ }
}
/**
@@ -1648,10 +1696,21 @@ meta_window_actor_get_obscured_region (MetaWindowActor *self)
{
MetaWindowActorPrivate *priv = self->priv;
- if (priv->back_pixmap && priv->opacity == 0xff && !priv->window->shaded)
- return priv->opaque_region;
- else
- return NULL;
+ if (!priv->window->shaded)
+ {
+ if (meta_is_display_server ())
+ {
+ if (priv->opacity == 0xff)
+ return priv->opaque_region;
+ }
+ else
+ {
+ if (priv->back_pixmap && priv->opacity == 0xff)
+ return priv->opaque_region;
+ }
+ }
+
+ return NULL;
}
#if 0
@@ -1764,8 +1823,11 @@ meta_window_actor_reset_visible_regions (MetaWindowActor *self)
g_clear_pointer (&priv->shadow_clip, cairo_region_destroy);
}
+/* 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. */
static void
-check_needs_pixmap (MetaWindowActor *self)
+check_needs_x11_pixmap (MetaWindowActor *self)
{
MetaWindowActorPrivate *priv = self->priv;
MetaScreen *screen = priv->screen;
@@ -1922,13 +1984,13 @@ check_needs_shadow (MetaWindowActor *self)
}
void
-meta_window_actor_process_damage (MetaWindowActor *self,
- XDamageNotifyEvent *event)
+meta_window_actor_process_x11_damage (MetaWindowActor *self,
+ XDamageNotifyEvent *event)
{
MetaWindowActorPrivate *priv = self->priv;
MetaCompScreen *info = meta_screen_get_compositor_data (priv->screen);
- priv->received_damage = TRUE;
+ priv->received_x11_damage = TRUE;
if (meta_window_is_fullscreen (priv->window) && g_list_last (info->windows)->data == self &&
!priv->unredirected)
{
@@ -1983,6 +2045,23 @@ meta_window_actor_process_damage (MetaWindowActor *self,
}
void
+meta_window_actor_process_wayland_damage (MetaWindowActor *self,
+ int x,
+ int y,
+ int width,
+ int height)
+{
+ MetaWindowActorPrivate *priv = self->priv;
+
+ if (!priv->mapped)
+ return;
+
+ meta_shaped_texture_update_area (META_SHAPED_TEXTURE (priv->actor),
+ x, y, width, height);
+ priv->repaint_scheduled = TRUE;
+}
+
+void
meta_window_actor_sync_visibility (MetaWindowActor *self)
{
MetaWindowActorPrivate *priv = self->priv;
@@ -2142,8 +2221,8 @@ region_create_from_x_rectangles (const XRectangle *rects,
}
static void
-meta_window_actor_update_shape_region (MetaWindowActor *self,
- cairo_rectangle_int_t *client_area)
+meta_window_actor_update_x11_shape_region (MetaWindowActor *self,
+ cairo_rectangle_int_t *client_area)
{
MetaWindowActorPrivate *priv = self->priv;
cairo_region_t *region = NULL;
@@ -2248,8 +2327,8 @@ meta_window_actor_update_shape_region (MetaWindowActor *self,
}
static void
-meta_window_actor_update_input_shape_region (MetaWindowActor *self,
- cairo_rectangle_int_t *client_area)
+meta_window_actor_update_x11_input_shape_region (MetaWindowActor *self,
+ cairo_rectangle_int_t *client_area)
{
MetaWindowActorPrivate *priv = self->priv;
cairo_region_t *region = NULL;
@@ -2313,7 +2392,7 @@ meta_window_actor_update_input_shape_region (MetaWindowActor *self,
}
static void
-check_needs_reshape (MetaWindowActor *self)
+check_needs_x11_reshape (MetaWindowActor *self)
{
MetaWindowActorPrivate *priv = self->priv;
MetaFrameBorders borders;
@@ -2322,7 +2401,7 @@ check_needs_reshape (MetaWindowActor *self)
if (!priv->mapped)
return;
- if (!priv->needs_reshape)
+ if (!priv->needs_x11_reshape)
return;
meta_frame_calc_borders (priv->window->frame, &borders);
@@ -2335,18 +2414,18 @@ check_needs_reshape (MetaWindowActor *self)
else
client_area.height = priv->window->rect.height;
- meta_window_actor_update_shape_region (self, &client_area);
- meta_window_actor_update_input_shape_region (self, &client_area);
+ meta_window_actor_update_x11_shape_region (self, &client_area);
+ meta_window_actor_update_x11_input_shape_region (self, &client_area);
- priv->needs_reshape = FALSE;
+ priv->needs_x11_reshape = FALSE;
}
void
-meta_window_actor_update_shape (MetaWindowActor *self)
+meta_window_actor_update_x11_shape (MetaWindowActor *self)
{
MetaWindowActorPrivate *priv = self->priv;
- priv->needs_reshape = TRUE;
+ priv->needs_x11_reshape = TRUE;
if (is_frozen (self))
return;
@@ -2354,6 +2433,52 @@ meta_window_actor_update_shape (MetaWindowActor *self)
clutter_actor_queue_redraw (priv->actor);
}
+#ifdef HAVE_WAYLAND
+static void
+maybe_emit_size_changed (MetaWindowActor *self,
+ struct wl_buffer *new_buffer)
+{
+ MetaWindowActorPrivate *priv = self->priv;
+ MetaWindow *window = priv->window;
+ MetaRectangle outer_rect;
+ int width = 0, height = 0;
+
+ if (new_buffer)
+ {
+ width = new_buffer->width;
+ height = new_buffer->height;
+ }
+
+ meta_window_get_outer_rect (window, &outer_rect);
+
+ if (outer_rect.width != width || outer_rect.height != height)
+ g_signal_emit (self, signals[SIZE_CHANGED], 0);
+}
+
+void
+meta_window_actor_set_wayland_surface (MetaWindowActor *self,
+ MetaWaylandSurface *surface)
+{
+ MetaWindowActorPrivate *priv = self->priv;
+
+ meta_shaped_texture_set_wayland_surface (META_SHAPED_TEXTURE (priv->actor),
+ surface);
+ if (surface->buffer)
+ maybe_emit_size_changed (self, surface->buffer);
+}
+
+void
+meta_window_actor_attach_wayland_buffer (MetaWindowActor *self,
+ struct wl_buffer *buffer)
+{
+ MetaWindowActorPrivate *priv = self->priv;
+
+ meta_shaped_texture_attach_wayland_buffer (META_SHAPED_TEXTURE (priv->actor), buffer);
+
+ maybe_emit_size_changed (self, buffer);
+}
+#endif /* HAVE_WAYLAND */
+
static void
meta_window_actor_handle_updates (MetaWindowActor *self)
{
@@ -2369,43 +2494,47 @@ meta_window_actor_handle_updates (MetaWindowActor *self)
return;
}
- if (priv->unredirected)
+ if (!meta_is_display_server ())
{
- /* Nothing to do here until/if the window gets redirected again */
- return;
- }
+ if (priv->unredirected)
+ {
+ /* Nothing to do here until/if the window gets redirected again */
+ return;
+ }
- if (priv->received_damage)
- {
- meta_error_trap_push (display);
- XDamageSubtract (xdisplay, priv->damage, None, None);
- meta_error_trap_pop (display);
+ if (priv->received_x11_damage)
+ {
+ meta_error_trap_push (display);
+ XDamageSubtract (xdisplay, priv->damage, None, None);
+ meta_error_trap_pop (display);
+
+ /* We need to make sure that any X drawing that happens before the
+ * XDamageSubtract() above is visible to subsequent GL rendering;
+ * the only standardized way to do this is EXT_x11_sync_object,
+ * which isn't yet widely available. For now, we count on details
+ * of Xorg and the open source drivers, and hope for the best
+ * otherwise.
+ *
+ * Xorg and open source driver specifics:
+ *
+ * The X server makes sure to flush drawing to the kernel before
+ * sending out damage events, but since we use DamageReportBoundingBox
+ * there may be drawing between the last damage event and the
+ * XDamageSubtract() that needs to be flushed as well.
+ *
+ * Xorg always makes sure that drawing is flushed to the kernel
+ * before writing events or responses to the client, so any round trip
+ * request at this point is sufficient to flush the GLX buffers.
+ */
+ XSync (xdisplay, False);
- /* We need to make sure that any X drawing that happens before the
- * XDamageSubtract() above is visible to subsequent GL rendering;
- * the only standardized way to do this is EXT_x11_sync_object,
- * which isn't yet widely available. For now, we count on details
- * of Xorg and the open source drivers, and hope for the best
- * otherwise.
- *
- * Xorg and open source driver specifics:
- *
- * The X server makes sure to flush drawing to the kernel before
- * sending out damage events, but since we use DamageReportBoundingBox
- * there may be drawing between the last damage event and the
- * XDamageSubtract() that needs to be flushed as well.
- *
- * Xorg always makes sure that drawing is flushed to the kernel
- * before writing events or responses to the client, so any round trip
- * request at this point is sufficient to flush the GLX buffers.
- */
- XSync (xdisplay, False);
+ priv->received_x11_damage = FALSE;
+ }
- priv->received_damage = FALSE;
+ check_needs_x11_pixmap (self);
}
- check_needs_pixmap (self);
- check_needs_reshape (self);
+ check_needs_x11_reshape (self);
check_needs_shadow (self);
}
@@ -2583,16 +2712,20 @@ void
meta_window_actor_set_updates_frozen (MetaWindowActor *self,
gboolean updates_frozen)
{
- MetaWindowActorPrivate *priv = self->priv;
+ /* On wayland we shouldn't need to ever freeze updates... */
+ if (!meta_is_display_server ())
+ {
+ MetaWindowActorPrivate *priv = self->priv;
- updates_frozen = updates_frozen != FALSE;
+ updates_frozen = updates_frozen != FALSE;
- if (priv->updates_frozen != updates_frozen)
- {
- priv->updates_frozen = updates_frozen;
- if (updates_frozen)
- meta_window_actor_freeze (self);
- else
- meta_window_actor_thaw (self);
+ if (priv->updates_frozen != updates_frozen)
+ {
+ priv->updates_frozen = updates_frozen;
+ if (updates_frozen)
+ meta_window_actor_freeze (self);
+ else
+ meta_window_actor_thaw (self);
+ }
}
}
diff --git a/src/compositor/meta-window-group.c b/src/compositor/meta-window-group.c
index fedae95..442d128 100644
--- a/src/compositor/meta-window-group.c
+++ b/src/compositor/meta-window-group.c
@@ -13,6 +13,7 @@
#include "meta-window-group.h"
#include "meta-background-actor-private.h"
#include "meta-background-group-private.h"
+#include "window-private.h"
struct _MetaWindowGroupClass
{
@@ -98,7 +99,9 @@ meta_window_group_paint (ClutterActor *actor)
int paint_x_offset, paint_y_offset;
MetaWindowGroup *window_group = META_WINDOW_GROUP (actor);
+#ifndef HAVE_WAYLAND
MetaCompScreen *info = meta_screen_get_compositor_data (window_group->screen);
+#endif
/* Normally we expect an actor to be drawn at it's position on the screen.
* However, if we're inside the paint of a ClutterClone, that won't be the
@@ -142,6 +145,7 @@ meta_window_group_paint (ClutterActor *actor)
visible_region = cairo_region_create_rectangle (&visible_rect);
+#ifndef HAVE_WAYLAND
if (info->unredirected_window != NULL)
{
cairo_rectangle_int_t unredirected_rect;
@@ -150,14 +154,17 @@ meta_window_group_paint (ClutterActor *actor)
meta_window_get_outer_rect (window, (MetaRectangle *)&unredirected_rect);
cairo_region_subtract_rectangle (visible_region, &unredirected_rect);
}
+#endif
for (l = children; l; l = l->next)
{
if (!CLUTTER_ACTOR_IS_VISIBLE (l->data))
continue;
+#ifndef HAVE_WAYLAND
if (l->data == info->unredirected_window)
continue;
+#endif
/* If an actor has effects applied, then that can change the area
* it paints and the opacity, so we no longer can figure out what
@@ -180,6 +187,7 @@ meta_window_group_paint (ClutterActor *actor)
if (META_IS_WINDOW_ACTOR (l->data))
{
+ MetaWindow *meta_window;
MetaWindowActor *window_actor = l->data;
int x, y;
@@ -194,7 +202,13 @@ meta_window_group_paint (ClutterActor *actor)
meta_window_actor_set_visible_region (window_actor, visible_region);
- if (clutter_actor_get_paint_opacity (CLUTTER_ACTOR (window_actor)) == 0xff)
+ /* Currently wayland clients have no way to report opaque
+ * window regions so for now we assume that all wayland
+ * clients are transparent... */
+ meta_window = meta_window_actor_get_meta_window (window_actor);
+
+ if (meta_window->client_type != META_WINDOW_CLIENT_TYPE_WAYLAND &&
+ clutter_actor_get_paint_opacity (CLUTTER_ACTOR (window_actor)) == 0xff)
{
cairo_region_t *obscured_region = meta_window_actor_get_obscured_region (window_actor);
if (obscured_region)
diff --git a/src/core/display.c b/src/core/display.c
index 60e2175..a89041e 100644
--- a/src/core/display.c
+++ b/src/core/display.c
@@ -903,14 +903,30 @@ meta_display_open (void)
enable_compositor (the_display);
meta_display_grab (the_display);
-
+
/* Now manage all existing windows */
tmp = the_display->screens;
while (tmp != NULL)
{
MetaScreen *screen = tmp->data;
-
- meta_screen_manage_all_windows (screen);
+
+ if (meta_is_display_server ())
+ {
+ /* 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);
+ }
+ else
+ meta_screen_manage_all_windows (screen);
tmp = tmp->next;
}
@@ -2003,8 +2019,8 @@ event_callback (XEvent *event,
}
if (display->compositor)
- meta_compositor_window_shape_changed (display->compositor,
- window);
+ meta_compositor_window_x11_shape_changed (display->compositor,
+ window);
}
else if (sev->kind == ShapeInput)
{
@@ -2030,8 +2046,8 @@ event_callback (XEvent *event,
}
if (display->compositor)
- meta_compositor_window_shape_changed (display->compositor,
- window);
+ meta_compositor_window_x11_shape_changed (display->compositor,
+ window);
}
}
else
@@ -3612,6 +3628,11 @@ void
meta_display_unregister_x_window (MetaDisplay *display,
Window xwindow)
{
+#ifdef HAVE_WAYLAND
+ if (xwindow == None)
+ return;
+#endif
+
g_return_if_fail (g_hash_table_lookup (display->xids, &xwindow) != NULL);
g_hash_table_remove (display->xids, &xwindow);
diff --git a/src/core/main.c b/src/core/main.c
index 4bec3d2..81cff36 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -55,6 +55,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>
@@ -346,28 +347,74 @@ meta_finalize (void)
if (display)
meta_display_close (display,
CurrentTime); /* I doubt correct timestamps matter here */
+
+#ifdef HAVE_WAYLAND
+ if (meta_is_display_server ())
+ meta_wayland_finalize ();
+#endif
}
-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;
+#ifdef HAVE_WAYLAND
+ case 'C': /* SIGCHLD */
+ meta_wayland_handle_sig_child ();
+ break;
+#endif
+ default:
+ g_warning ("Spurious character '%c' read from signal pipe", signal);
+ }
+
+ return TRUE;
}
/**
@@ -396,21 +443,28 @@ 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));
+ if (meta_is_display_server ())
+ {
+ if (sigaction (SIGCHLD, &act, NULL) < 0)
+ g_printerr ("Failed to register SIGCHLD handler: %s\n",
+ g_strerror (errno));
+ }
+
if (g_getenv ("MUTTER_VERBOSE"))
meta_set_verbose (TRUE);
if (g_getenv ("MUTTER_DEBUG"))
@@ -427,9 +481,18 @@ meta_init (void)
g_irepository_prepend_search_path (MUTTER_PKGLIBDIR);
#endif
- meta_set_syncing (opt_sync || (g_getenv ("MUTTER_SYNC") != NULL));
+#ifdef HAVE_WAYLAND
+ if (meta_is_display_server ())
+ {
+ /* 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
+#endif
+ meta_select_display (opt_display_name);
- meta_select_display (opt_display_name);
+ meta_set_syncing (opt_sync || (g_getenv ("MUTTER_SYNC") != NULL));
if (opt_replace_wm)
meta_set_replace_current_wm (TRUE);
@@ -441,10 +504,17 @@ meta_init (void)
meta_ui_init ();
- /*
- * Clutter can only be initialized after the UI.
- */
- meta_clutter_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. */
+ if (!meta_is_display_server ())
+ {
+ /*
+ * Clutter can only be initialized after the UI.
+ */
+ meta_clutter_init ();
+ }
}
/**
diff --git a/src/core/screen-private.h b/src/core/screen-private.h
index 56a0be4..b74daf5 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 3bab340..4d9ffd2 100644
--- a/src/core/screen.c
+++ b/src/core/screen.c
@@ -45,6 +45,9 @@
#include <meta/compositor.h>
#include "mutter-enum-types.h"
#include "core.h"
+#ifdef HAVE_WAYLAND
+#include "meta-wayland-private.h"
+#endif
#include <X11/extensions/Xinerama.h>
@@ -603,8 +606,8 @@ reload_monitor_infos (MetaScreen *screen)
* should effectively be forwarded to events on the background actor,
* providing that the scene graph is set up correctly.
*/
-static Window
-create_guard_window (Display *xdisplay, MetaScreen *screen)
+Window
+meta_screen_create_guard_window (Display *xdisplay, MetaScreen *screen)
{
XSetWindowAttributes attributes;
Window guard_window;
@@ -668,6 +671,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 ();
@@ -826,8 +832,21 @@ meta_screen_new (MetaDisplay *display,
screen->xscreen = ScreenOfDisplay (xdisplay, number);
screen->xroot = xroot;
screen->rect.x = screen->rect.y = 0;
- screen->rect.width = WidthOfScreen (screen->xscreen);
- screen->rect.height = HeightOfScreen (screen->xscreen);
+
+#ifdef HAVE_WAYLAND
+ if (meta_is_display_server ())
+ {
+ 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
+#endif
+ {
+ screen->rect.width = WidthOfScreen (screen->xscreen);
+ screen->rect.height = HeightOfScreen (screen->xscreen);
+ }
+
screen->current_cursor = -1; /* invalid/unset */
screen->default_xvisual = DefaultVisualOfScreen (screen->xscreen);
screen->default_depth = DefaultDepthOfScreen (screen->xscreen);
@@ -1082,8 +1101,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);
diff --git a/src/core/window-private.h b/src/core/window-private.h
index 7948209..6fd73fc 100644
--- a/src/core/window-private.h
+++ b/src/core/window-private.h
@@ -44,6 +44,17 @@
#include <X11/Xutil.h>
#include <cairo.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
+#ifdef HAVE_WAYLAND
+#include "meta-wayland-private.h"
+#endif
+
+/* XXX: We should find a nicer approach to deal with the
+ * circular dependency we have with the current headers
+ * (meta-wayland-private.h which typedefs MetaWaylandSurface
+ * also includes window-private.h) */
+#ifndef HAVE_META_WAYLAND_SURFACE_TYPE
+typedef struct _MetaWaylandSurface MetaWaylandSurface;
+#endif
typedef struct _MetaWindowQueue MetaWindowQueue;
@@ -69,6 +80,11 @@ typedef enum {
_NET_WM_BYPASS_COMPOSITOR_HINT_OFF = 2,
} MetaBypassCompositorHintValue;
+typedef enum {
+ META_WINDOW_CLIENT_TYPE_WAYLAND,
+ META_WINDOW_CLIENT_TYPE_X11
+} MetaWindowClientType;
+
struct _MetaWindow
{
GObject parent_instance;
@@ -77,6 +93,10 @@ struct _MetaWindow
MetaScreen *screen;
const MetaMonitorInfo *monitor;
MetaWorkspace *workspace;
+ MetaWindowClientType client_type;
+#ifdef HAVE_WAYLAND
+ MetaWaylandSurface *surface;
+#endif
Window xwindow;
/* may be NULL! not all windows get decorated */
MetaFrame *frame;
@@ -489,6 +509,10 @@ MetaWindow* meta_window_new_with_attrs (MetaDisplay *display,
gboolean must_be_viewable,
MetaCompEffect effect,
XWindowAttributes *attrs);
+MetaWindow *meta_window_new_for_wayland (MetaDisplay *display,
+ int width,
+ int height,
+ MetaWaylandSurface *surface);
void meta_window_unmanage (MetaWindow *window,
guint32 timestamp);
void meta_window_calc_showing (MetaWindow *window);
diff --git a/src/core/window.c b/src/core/window.c
index d2db9cd..60f8b06 100644
--- a/src/core/window.c
+++ b/src/core/window.c
@@ -810,28 +810,31 @@ meta_window_should_attach_to_parent (MetaWindow *window)
}
}
-MetaWindow*
-meta_window_new_with_attrs (MetaDisplay *display,
- Window xwindow,
- gboolean must_be_viewable,
- MetaCompEffect effect,
- XWindowAttributes *attrs)
+static MetaWindow*
+meta_window_new_full (MetaDisplay *display,
+ MetaWindowClientType client_type,
+ MetaWaylandSurface *surface,
+ Window xwindow,
+ gboolean must_be_viewable,
+ MetaCompEffect effect,
+ XWindowAttributes *attrs)
{
MetaWindow *window;
GSList *tmp;
MetaWorkspace *space;
- gulong existing_wm_state;
+ gulong existing_wm_state = WithdrawnState;
gulong event_mask;
MetaMoveResizeFlags flags;
- gboolean has_shape;
- gboolean has_input_shape;
+ gboolean has_shape = FALSE;
+ gboolean has_input_shape = FALSE;
MetaScreen *screen;
g_assert (attrs != NULL);
meta_verbose ("Attempting to manage 0x%lx\n", xwindow);
- if (meta_display_xwindow_is_a_no_focus_window (display, xwindow))
+ if (client_type == META_WINDOW_CLIENT_TYPE_X11 &&
+ meta_display_xwindow_is_a_no_focus_window (display, xwindow))
{
meta_verbose ("Not managing no_focus_window 0x%lx\n",
xwindow);
@@ -896,150 +899,155 @@ meta_window_new_with_attrs (MetaDisplay *display,
"IsUnviewable" :
"(unknown)");
- existing_wm_state = WithdrawnState;
- if (must_be_viewable && attrs->map_state != IsViewable)
+ if (client_type == META_WINDOW_CLIENT_TYPE_X11)
{
- /* Only manage if WM_STATE is IconicState or NormalState */
- gulong state;
-
- /* WM_STATE isn't a cardinal, it's type WM_STATE, but is an int */
- if (!(meta_prop_get_cardinal_with_atom_type (display, xwindow,
- display->atom_WM_STATE,
- display->atom_WM_STATE,
- &state) &&
- (state == IconicState || state == NormalState)))
+ existing_wm_state = WithdrawnState;
+ if (must_be_viewable && attrs->map_state != IsViewable)
{
- meta_verbose ("Deciding not to manage unmapped or unviewable window 0x%lx\n", xwindow);
- meta_error_trap_pop (display);
- meta_display_ungrab (display);
- return NULL;
- }
+ /* Only manage if WM_STATE is IconicState or NormalState */
+ gulong state;
+
+ /* WM_STATE isn't a cardinal, it's type WM_STATE, but is an int */
+ if (!(meta_prop_get_cardinal_with_atom_type (display, xwindow,
+ display->atom_WM_STATE,
+ display->atom_WM_STATE,
+ &state) &&
+ (state == IconicState || state == NormalState)))
+ {
+ meta_verbose ("Deciding not to manage unmapped or unviewable window 0x%lx\n", xwindow);
+ meta_error_trap_pop (display);
+ meta_display_ungrab (display);
+ return NULL;
+ }
- existing_wm_state = state;
- meta_verbose ("WM_STATE of %lx = %s\n", xwindow,
- wm_state_to_string (existing_wm_state));
- }
+ existing_wm_state = state;
+ meta_verbose ("WM_STATE of %lx = %s\n", xwindow,
+ wm_state_to_string (existing_wm_state));
+ }
- meta_error_trap_push_with_return (display);
+ meta_error_trap_push_with_return (display);
- /*
- * XAddToSaveSet can only be called on windows created by a different client.
- * with Mutter we want to be able to create manageable windows from within
- * the process (such as a dummy desktop window), so we do not want this
- * call failing to prevent the window from being managed -- wrap it in its
- * own error trap (we use the _with_return() version here to ensure that
- * XSync() is done on the pop, otherwise the error will not get caught).
- */
- meta_error_trap_push_with_return (display);
- XAddToSaveSet (display->xdisplay, xwindow);
- meta_error_trap_pop_with_return (display);
+ /*
+ * XAddToSaveSet can only be called on windows created by a different
+ * client. with Mutter we want to be able to create manageable windows
+ * from within the process (such as a dummy desktop window), so we do not
+ * want this call failing to prevent the window from being managed -- wrap
+ * it in its own error trap (we use the _with_return() version here to
+ * ensure that XSync() is done on the pop, otherwise the error will not
+ * get caught).
+ */
+ meta_error_trap_push_with_return (display);
+ XAddToSaveSet (display->xdisplay, xwindow);
+ meta_error_trap_pop_with_return (display);
- event_mask = PropertyChangeMask | ColormapChangeMask;
- if (attrs->override_redirect)
- event_mask |= StructureNotifyMask;
+ event_mask = PropertyChangeMask | ColormapChangeMask;
+ if (attrs->override_redirect)
+ event_mask |= StructureNotifyMask;
- /* If the window is from this client (a menu, say) we need to augment
- * the event mask, not replace it. For windows from other clients,
- * attrs->your_event_mask will be empty at this point.
- */
- XSelectInput (display->xdisplay, xwindow, attrs->your_event_mask | event_mask);
+ /* If the window is from this client (a menu, say) we need to augment
+ * the event mask, not replace it. For windows from other clients,
+ * attrs->your_event_mask will be empty at this point.
+ */
+ XSelectInput (display->xdisplay, xwindow, attrs->your_event_mask | event_mask);
- {
- unsigned char mask_bits[XIMaskLen (XI_LASTEVENT)] = { 0 };
- XIEventMask mask = { XIAllMasterDevices, sizeof (mask_bits), mask_bits };
+ {
+ unsigned char mask_bits[XIMaskLen (XI_LASTEVENT)] = { 0 };
+ XIEventMask mask = { XIAllMasterDevices, sizeof (mask_bits), mask_bits };
- meta_core_add_old_event_mask (display->xdisplay, xwindow, &mask);
+ meta_core_add_old_event_mask (display->xdisplay, xwindow, &mask);
- XISetMask (mask.mask, XI_Enter);
- XISetMask (mask.mask, XI_Leave);
- XISetMask (mask.mask, XI_FocusIn);
- XISetMask (mask.mask, XI_FocusOut);
+ XISetMask (mask.mask, XI_Enter);
+ XISetMask (mask.mask, XI_Leave);
+ XISetMask (mask.mask, XI_FocusIn);
+ XISetMask (mask.mask, XI_FocusOut);
- XISelectEvents (display->xdisplay, xwindow, &mask, 1);
- }
+ XISelectEvents (display->xdisplay, xwindow, &mask, 1);
+ }
- has_shape = FALSE;
- has_input_shape = FALSE;
#ifdef HAVE_SHAPE
- if (META_DISPLAY_HAS_SHAPE (display))
- {
- int x_bounding, y_bounding, x_clip, y_clip;
- unsigned w_bounding, h_bounding, w_clip, h_clip;
- int bounding_shaped, clip_shaped;
- XRectangle *input_rectangles;
- int n_rects, ordering;
-
- XShapeSelectInput (display->xdisplay, xwindow, ShapeNotifyMask);
-
- XShapeQueryExtents (display->xdisplay, xwindow,
- &bounding_shaped, &x_bounding, &y_bounding,
- &w_bounding, &h_bounding,
- &clip_shaped, &x_clip, &y_clip,
- &w_clip, &h_clip);
-
- has_shape = bounding_shaped != FALSE;
-
- /* XXX: The x shape extension doesn't provide a way to only test if an
- * input shape has been specified, so we have to query and throw away the
- * rectangles. */
- meta_error_trap_push (display);
- input_rectangles = XShapeGetRectangles (display->xdisplay, xwindow,
- ShapeInput, &n_rects, &ordering);
- meta_error_trap_pop (display);
- if (input_rectangles)
- {
- if (n_rects > 1 ||
- (n_rects == 1 &&
- (input_rectangles[0].x != x_bounding ||
- input_rectangles[1].y != y_bounding ||
- input_rectangles[2].width != w_bounding ||
- input_rectangles[3].height != h_bounding)))
- {
- has_input_shape = TRUE;
- }
- XFree (input_rectangles);
- }
-
- meta_topic (META_DEBUG_SHAPES,
- "Window has_shape = %d extents %d,%d %u x %u\n",
- has_shape, x_bounding, y_bounding,
- w_bounding, h_bounding);
- }
+ if (META_DISPLAY_HAS_SHAPE (display))
+ {
+ int x_bounding, y_bounding, x_clip, y_clip;
+ unsigned w_bounding, h_bounding, w_clip, h_clip;
+ int bounding_shaped, clip_shaped;
+ XRectangle *input_rectangles;
+ int n_rects, ordering;
+
+ XShapeSelectInput (display->xdisplay, xwindow, ShapeNotifyMask);
+
+ XShapeQueryExtents (display->xdisplay, xwindow,
+ &bounding_shaped, &x_bounding, &y_bounding,
+ &w_bounding, &h_bounding,
+ &clip_shaped, &x_clip, &y_clip,
+ &w_clip, &h_clip);
+
+ has_shape = bounding_shaped != FALSE;
+
+ /* XXX: The x shape extension doesn't provide a way to only test if an
+ * input shape has been specified, so we have to query and throw away the
+ * rectangles. */
+ meta_error_trap_push (display);
+ input_rectangles = XShapeGetRectangles (display->xdisplay, xwindow,
+ ShapeInput, &n_rects, &ordering);
+ meta_error_trap_pop (display);
+ if (input_rectangles)
+ {
+ if (n_rects > 1 ||
+ (n_rects == 1 &&
+ (input_rectangles[0].x != x_bounding ||
+ input_rectangles[1].y != y_bounding ||
+ input_rectangles[2].width != w_bounding ||
+ input_rectangles[3].height != h_bounding)))
+ {
+ has_input_shape = TRUE;
+ }
+ XFree (input_rectangles);
+ }
+
+ meta_topic (META_DEBUG_SHAPES,
+ "Window has_shape = %d extents %d,%d %u x %u\n",
+ has_shape, x_bounding, y_bounding,
+ w_bounding, h_bounding);
+ }
#endif
- /* Get rid of any borders */
- if (attrs->border_width != 0)
- XSetWindowBorderWidth (display->xdisplay, xwindow, 0);
+ /* Get rid of any borders */
+ if (attrs->border_width != 0)
+ XSetWindowBorderWidth (display->xdisplay, xwindow, 0);
- /* Get rid of weird gravities */
- if (attrs->win_gravity != NorthWestGravity)
- {
- XSetWindowAttributes set_attrs;
+ /* Get rid of weird gravities */
+ if (attrs->win_gravity != NorthWestGravity)
+ {
+ XSetWindowAttributes set_attrs;
- set_attrs.win_gravity = NorthWestGravity;
+ set_attrs.win_gravity = NorthWestGravity;
- XChangeWindowAttributes (display->xdisplay,
- xwindow,
- CWWinGravity,
- &set_attrs);
- }
+ XChangeWindowAttributes (display->xdisplay,
+ xwindow,
+ CWWinGravity,
+ &set_attrs);
+ }
- if (meta_error_trap_pop_with_return (display) != Success)
- {
- meta_verbose ("Window 0x%lx disappeared just as we tried to manage it\n",
- xwindow);
- meta_error_trap_pop (display);
- meta_display_ungrab (display);
- return NULL;
+ if (meta_error_trap_pop_with_return (display) != Success)
+ {
+ meta_verbose ("Window 0x%lx disappeared just as we tried to manage it\n",
+ xwindow);
+ meta_error_trap_pop (display);
+ meta_display_ungrab (display);
+ return NULL;
+ }
}
-
window = g_object_new (META_TYPE_WINDOW, NULL);
window->constructing = TRUE;
window->dialog_pid = -1;
+ window->client_type = client_type;
+#ifdef HAVE_WAYLAND
+ window->surface = surface;
+#endif
window->xwindow = xwindow;
/* this is in window->screen->display, but that's too annoying to
@@ -1166,7 +1174,11 @@ meta_window_new_with_attrs (MetaDisplay *display,
window->mwm_has_move_func = TRUE;
window->mwm_has_resize_func = TRUE;
- window->decorated = TRUE;
+ if (client_type == META_WINDOW_CLIENT_TYPE_X11)
+ window->decorated = TRUE;
+ else
+ window->decorated = FALSE;
+
window->has_close_func = TRUE;
window->has_minimize_func = TRUE;
window->has_maximize_func = TRUE;
@@ -1235,7 +1247,8 @@ meta_window_new_with_attrs (MetaDisplay *display,
window->has_resize_func = FALSE;
}
- meta_display_register_x_window (display, &window->xwindow, window);
+ if (client_type == META_WINDOW_CLIENT_TYPE_X11)
+ meta_display_register_x_window (display, &window->xwindow, window);
/* Assign this #MetaWindow a sequence number which can be used
* for sorting.
@@ -1250,7 +1263,8 @@ meta_window_new_with_attrs (MetaDisplay *display,
meta_window_load_initial_properties (window);
- if (!window->override_redirect)
+ if (!window->override_redirect &&
+ client_type == META_WINDOW_CLIENT_TYPE_X11)
{
update_sm_hints (window); /* must come after transient_for */
@@ -1540,6 +1554,68 @@ meta_window_new_with_attrs (MetaDisplay *display,
return window;
}
+#ifdef HAVE_WAYLAND
+MetaWindow *
+meta_window_new_for_wayland (MetaDisplay *display,
+ int width,
+ int height,
+ MetaWaylandSurface *surface)
+{
+ XWindowAttributes attrs;
+ MetaScreen *scr = display->screens->data;
+ MetaWindow *window;
+
+ attrs.x = 0;
+ attrs.y = 0;
+ attrs.width = width;
+ attrs.height = height;
+ attrs.border_width = 0;
+ attrs.depth = 24;
+ attrs.visual = NULL;
+ attrs.root = scr->xroot;
+ attrs.class = InputOutput;
+ attrs.bit_gravity = NorthWestGravity;
+ attrs.win_gravity = NorthWestGravity;
+ attrs.backing_store = 0;
+ attrs.backing_planes = ~0;
+ attrs.backing_pixel = 0;
+ attrs.save_under = 0;
+ attrs.colormap = 0;
+ attrs.map_installed = 1;
+ attrs.map_state = IsUnmapped;
+ attrs.all_event_masks = ~0;
+ attrs.your_event_mask = 0;
+ attrs.do_not_propagate_mask = 0;
+ attrs.override_redirect = 0;
+ attrs.screen = scr->xscreen;
+
+ window = meta_window_new_full (display,
+ META_WINDOW_CLIENT_TYPE_WAYLAND,
+ surface,
+ None,
+ TRUE,
+ META_COMP_EFFECT_NONE,
+ &attrs);
+ return window;
+}
+#endif
+
+MetaWindow*
+meta_window_new_with_attrs (MetaDisplay *display,
+ Window xwindow,
+ gboolean must_be_viewable,
+ MetaCompEffect effect,
+ XWindowAttributes *attrs)
+{
+ return meta_window_new_full (display,
+ META_WINDOW_CLIENT_TYPE_X11,
+ NULL,
+ xwindow,
+ must_be_viewable,
+ effect,
+ attrs);
+}
+
/* This function should only be called from the end of meta_window_new_with_attrs () */
static void
meta_window_apply_session_info (MetaWindow *window,
@@ -7819,7 +7895,7 @@ meta_window_update_opaque_region (MetaWindow *window)
meta_XFree (region);
if (window->display->compositor)
- meta_compositor_window_shape_changed (window->display->compositor, window);
+ meta_compositor_window_x11_shape_changed (window->display->compositor, window);
}
static void
diff --git a/src/meta/compositor.h b/src/meta/compositor.h
index 13143c9..de81c20 100644
--- a/src/meta/compositor.h
+++ b/src/meta/compositor.h
@@ -64,8 +64,8 @@ void meta_compositor_manage_screen (MetaCompositor *compositor,
void meta_compositor_unmanage_screen (MetaCompositor *compositor,
MetaScreen *screen);
-void meta_compositor_window_shape_changed (MetaCompositor *compositor,
- MetaWindow *window);
+void meta_compositor_window_x11_shape_changed (MetaCompositor *compositor,
+ MetaWindow *window);
gboolean meta_compositor_process_event (MetaCompositor *compositor,
XEvent *event,
diff --git a/src/meta/meta-shaped-texture.h b/src/meta/meta-shaped-texture.h
index 2e12284..af46bd0 100644
--- a/src/meta/meta-shaped-texture.h
+++ b/src/meta/meta-shaped-texture.h
@@ -29,6 +29,12 @@
#include <clutter/clutter.h>
#include <X11/Xlib.h>
+#ifdef HAVE_WAYLAND
+#include <wayland-server.h>
+#include "meta-wayland-private.h"
+#include <clutter/wayland/clutter-wayland-surface.h>
+#endif
+
G_BEGIN_DECLS
#define META_TYPE_SHAPED_TEXTURE (meta_shaped_texture_get_type())
@@ -45,7 +51,11 @@ typedef struct _MetaShapedTexturePrivate MetaShapedTexturePrivate;
struct _MetaShapedTextureClass
{
/*< private >*/
+#ifdef HAVE_WAYLAND
+ ClutterWaylandSurfaceClass parent_class;
+#else
ClutterActorClass parent_class;
+#endif
};
/**
@@ -64,7 +74,13 @@ struct _MetaShapedTexture
GType meta_shaped_texture_get_type (void) G_GNUC_CONST;
-ClutterActor *meta_shaped_texture_new (void);
+ClutterActor *meta_shaped_texture_new_with_xwindow (Window xwindow);
+#ifdef HAVE_WAYLAND
+ClutterActor *meta_shaped_texture_new_with_wayland_surface (MetaWaylandSurface *surface);
+void meta_shaped_texture_set_wayland_surface (MetaShapedTexture *stex,
+ MetaWaylandSurface *surface);
+MetaWaylandSurface *meta_shaped_texture_get_wayland_surface (MetaShapedTexture *stex);
+#endif
void meta_shaped_texture_set_create_mipmaps (MetaShapedTexture *stex,
gboolean create_mipmaps);
@@ -77,6 +93,10 @@ void meta_shaped_texture_update_area (MetaShapedTexture *stex,
void meta_shaped_texture_set_pixmap (MetaShapedTexture *stex,
Pixmap pixmap);
+#ifdef HAVE_WAYLAND
+void meta_shaped_texture_attach_wayland_buffer (MetaShapedTexture *stex,
+ struct wl_buffer *buffer);
+#endif
CoglTexture * meta_shaped_texture_get_texture (MetaShapedTexture *stex);
diff --git a/src/wayland/meta-wayland-private.h b/src/wayland/meta-wayland-private.h
new file mode 100644
index 0000000..5b7e8e1
--- /dev/null
+++ b/src/wayland/meta-wayland-private.h
@@ -0,0 +1,151 @@
+/*
+ * 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>
+#include <cairo.h>
+
+#include "window-private.h"
+
+typedef struct _MetaWaylandCompositor MetaWaylandCompositor;
+
+typedef struct
+{
+ struct wl_resource resource;
+ cairo_region_t *region;
+} MetaWaylandRegion;
+
+struct _MetaWaylandSurface
+{
+ struct wl_surface wayland_surface;
+ MetaWaylandCompositor *compositor;
+ guint32 xid;
+ int x;
+ int y;
+ struct wl_buffer *buffer;
+ struct wl_listener buffer_destroy_listener;
+ MetaWindow *window;
+ ClutterActor *actor;
+ gboolean has_shell_surface;
+
+ /* All the pending state, that wl_surface.commit will apply. */
+ struct
+ {
+ /* wl_surface.attach */
+ gboolean newly_attached;
+ struct wl_buffer *buffer;
+ struct wl_listener buffer_destroy_listener;
+ int32_t sx;
+ int32_t sy;
+
+ /* wl_surface.damage */
+ cairo_region_t *damage;
+
+ /* wl_surface.frame */
+ struct wl_list frame_callback_list;
+ } pending;
+};
+
+#ifndef HAVE_META_WAYLAND_SURFACE_TYPE
+typedef struct _MetaWaylandSurface MetaWaylandSurface;
+#endif
+
+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_display *display;
+} WaylandEventSource;
+
+typedef struct
+{
+ struct wl_list link;
+
+ /* Pointer back to the compositor */
+ MetaWaylandCompositor *compositor;
+
+ struct wl_resource resource;
+} MetaWaylandFrameCallback;
+
+struct _MetaWaylandCompositor
+{
+ struct wl_display *wayland_display;
+ struct wl_event_loop *wayland_loop;
+ GMainLoop *init_loop;
+ ClutterActor *stage;
+ GList *outputs;
+ GSource *wayland_event_source;
+ GList *surfaces;
+ struct wl_list 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);
+void meta_wayland_finalize (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..01df167
--- /dev/null
+++ b/src/wayland/meta-wayland.c
@@ -0,0 +1,1369 @@
+/*
+ * 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 <meta/main.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)
+{
+ WaylandEventSource *source = (WaylandEventSource *)base;
+
+ *timeout = -1;
+
+ wl_display_flush_clients (source->display);
+
+ 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;
+ struct wl_event_loop *loop = wl_display_get_event_loop (source->display);
+
+ wl_event_loop_dispatch (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_display *display)
+{
+ WaylandEventSource *source;
+ struct wl_event_loop *loop = wl_display_get_event_loop (display);
+
+ source = (WaylandEventSource *) g_source_new (&wayland_event_source_funcs,
+ sizeof (WaylandEventSource));
+ source->display = display;
+ 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
+surface_process_damage (MetaWaylandSurface *surface,
+ cairo_region_t *region)
+{
+ if (surface->window)
+ {
+ MetaWindowActor *window_actor =
+ META_WINDOW_ACTOR (meta_window_get_compositor_private (surface->window));
+
+ if (window_actor)
+ {
+ int i, n_rectangles = cairo_region_num_rectangles (region);
+
+ for (i = 0; i < n_rectangles; i++)
+ {
+ cairo_rectangle_int_t rectangle;
+
+ cairo_region_get_rectangle (region, i, &rectangle);
+
+ meta_window_actor_process_wayland_damage (window_actor,
+ rectangle.x,
+ rectangle.y,
+ rectangle.width,
+ rectangle.height);
+ }
+ }
+ }
+}
+
+static void
+meta_wayland_surface_destroy (struct wl_client *wayland_client,
+ struct wl_resource *wayland_resource)
+{
+ wl_resource_destroy (wayland_resource);
+}
+
+static void
+meta_wayland_surface_detach_buffer (MetaWaylandSurface *surface)
+{
+ struct wl_buffer *buffer = surface->buffer;
+
+ if (buffer)
+ {
+ wl_list_remove (&surface->buffer_destroy_listener.link);
+
+ surface->buffer = NULL;
+ }
+}
+
+static void
+meta_wayland_surface_detach_buffer_and_notify (MetaWaylandSurface *surface)
+{
+ struct wl_buffer *buffer = surface->buffer;
+
+ if (buffer)
+ {
+ g_assert (buffer->resource.client != NULL);
+ wl_resource_queue_event (&buffer->resource, WL_BUFFER_RELEASE);
+ }
+
+ meta_wayland_surface_detach_buffer (surface);
+}
+
+static void
+surface_handle_buffer_destroy (struct wl_listener *listener,
+ void *data)
+{
+ MetaWaylandSurface *surface =
+ wl_container_of (listener, surface, buffer_destroy_listener);
+
+ meta_wayland_surface_detach_buffer (surface);
+}
+
+static void
+meta_wayland_surface_attach (struct wl_client *wayland_client,
+ struct wl_resource *wayland_surface_resource,
+ struct wl_resource *wayland_buffer_resource,
+ gint32 sx, gint32 sy)
+{
+ MetaWaylandSurface *surface = wayland_surface_resource->data;
+ struct wl_buffer *buffer =
+ wayland_buffer_resource ? wayland_buffer_resource->data : NULL;
+
+ /* Attach without commit in between does not send wl_buffer.release */
+ if (surface->pending.buffer)
+ wl_list_remove (&surface->pending.buffer_destroy_listener.link);
+
+ surface->pending.sx = sx;
+ surface->pending.sy = sy;
+ surface->pending.buffer = buffer;
+ surface->pending.newly_attached = TRUE;
+
+ if (buffer)
+ wl_signal_add (&buffer->resource.destroy_signal,
+ &surface->pending.buffer_destroy_listener);
+}
+
+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;
+ cairo_rectangle_int_t rectangle = { x, y, width, height };
+
+ cairo_region_union_rectangle (surface->pending.damage, &rectangle);
+}
+
+static void
+destroy_frame_callback (struct wl_resource *callback_resource)
+{
+ MetaWaylandFrameCallback *callback = callback_resource->data;
+
+ wl_list_remove (&callback->link);
+ 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->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);
+ wl_list_insert (surface->pending.frame_callback_list.prev, &callback->link);
+}
+
+static void
+meta_wayland_surface_set_opaque_region (struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *region)
+{
+}
+
+static void
+meta_wayland_surface_set_input_region (struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *region)
+{
+}
+
+static void
+empty_region (cairo_region_t *region)
+{
+ cairo_rectangle_int_t rectangle = { 0, 0, 0, 0 };
+ cairo_region_intersect_rectangle (region, &rectangle);
+}
+
+static void
+meta_wayland_surface_commit (struct wl_client *client,
+ struct wl_resource *resource)
+{
+ MetaWaylandSurface *surface = resource->data;
+ MetaWaylandCompositor *compositor = surface->compositor;
+
+ /* wl_surface.attach */
+ if (surface->pending.newly_attached &&
+ surface->pending.buffer != surface->buffer)
+ {
+ struct wl_buffer *buffer = surface->pending.buffer;
+
+ meta_wayland_surface_detach_buffer_and_notify (surface);
+
+ if (surface->pending.buffer)
+ {
+ /* Note: we set this before informing any window-actor since
+ * the window actor will expect to find the new buffer
+ * within the surface. */
+ surface->buffer = surface->pending.buffer;
+
+ if (surface->window)
+ {
+ MetaWindow *window = surface->window;
+ MetaWindowActor *window_actor =
+ META_WINDOW_ACTOR (meta_window_get_compositor_private (window));
+ MetaRectangle rect;
+
+ if (window_actor)
+ meta_window_actor_attach_wayland_buffer (window_actor,
+ surface->pending.buffer);
+
+ meta_window_get_outer_rect (surface->window, &rect);
+
+ /* XXX: we resize X based surfaces according to X events */
+ if (surface->xid == 0 &&
+ (buffer->width != rect.width || buffer->height != rect.height))
+ meta_window_resize (surface->window, FALSE, buffer->width, buffer->height);
+ }
+
+ wl_signal_add (&surface->buffer->resource.destroy_signal,
+ &surface->buffer_destroy_listener);
+ }
+ }
+
+ if (surface->pending.buffer)
+ {
+ wl_list_remove (&surface->pending.buffer_destroy_listener.link);
+ surface->pending.buffer = NULL;
+ }
+ surface->pending.sx = 0;
+ surface->pending.sy = 0;
+ surface->pending.newly_attached = FALSE;
+
+ surface_process_damage (surface, surface->pending.damage);
+ empty_region (surface->pending.damage);
+
+ /* wl_surface.frame */
+ wl_list_insert_list (&compositor->frame_callbacks,
+ &surface->pending.frame_callback_list);
+ wl_list_init (&surface->pending.frame_callback_list);
+}
+
+static void
+meta_wayland_surface_set_buffer_transform (struct wl_client *client,
+ struct wl_resource *resource,
+ int32_t transform)
+{
+}
+
+const struct wl_surface_interface meta_wayland_surface_interface = {
+ meta_wayland_surface_destroy,
+ meta_wayland_surface_attach,
+ meta_wayland_surface_damage,
+ meta_wayland_surface_frame,
+ meta_wayland_surface_set_opaque_region,
+ meta_wayland_surface_set_input_region,
+ meta_wayland_surface_commit,
+ meta_wayland_surface_set_buffer_transform
+};
+
+static void
+window_destroyed_cb (void *user_data, GObject *old_object)
+{
+ MetaWaylandSurface *surface = user_data;
+
+ surface->window = NULL;
+}
+
+static void
+meta_wayland_surface_free (MetaWaylandSurface *surface)
+{
+ MetaWaylandCompositor *compositor = surface->compositor;
+ MetaWaylandFrameCallback *cb, *next;
+
+ compositor->surfaces = g_list_remove (compositor->surfaces, surface);
+ meta_wayland_surface_detach_buffer_and_notify (surface);
+
+ if (surface->window)
+ g_object_weak_unref (G_OBJECT (surface->window),
+ window_destroyed_cb,
+ surface);
+
+ /* NB: If the surface corresponds to an X window then we will be
+ * sure to free the MetaWindow according to some X event. */
+ if (surface->window &&
+ surface->window->client_type == META_WINDOW_CLIENT_TYPE_WAYLAND)
+ {
+ MetaDisplay *display = meta_get_display ();
+ guint32 timestamp = meta_display_get_current_time_roundtrip (display);
+ meta_window_unmanage (surface->window, timestamp);
+ }
+
+ if (surface->pending.buffer)
+ wl_list_remove (&surface->pending.buffer_destroy_listener.link);
+
+ cairo_region_destroy (surface->pending.damage);
+
+ wl_list_for_each_safe (cb, next,
+ &surface->pending.frame_callback_list, link)
+ wl_resource_destroy (&cb->resource);
+
+ g_slice_free (MetaWaylandSurface, surface);
+}
+
+static void
+meta_wayland_surface_resource_destroy_cb (struct wl_resource *resource)
+{
+ MetaWaylandSurface *surface = resource->data;
+ meta_wayland_surface_free (surface);
+}
+
+static void
+surface_handle_pending_buffer_destroy (struct wl_listener *listener,
+ void *data)
+{
+ MetaWaylandSurface *surface =
+ wl_container_of (listener, surface, pending.buffer_destroy_listener);
+
+ surface->pending.buffer = NULL;
+}
+
+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;
+
+ surface->pending.damage = cairo_region_create ();
+
+ surface->buffer_destroy_listener.notify =
+ surface_handle_buffer_destroy;
+
+ surface->pending.buffer_destroy_listener.notify =
+ surface_handle_pending_buffer_destroy;
+ wl_list_init (&surface->pending.frame_callback_list);
+
+ wl_client_add_resource (wayland_client, &surface->wayland_surface.resource);
+
+ compositor->surfaces = g_list_prepend (compositor->surfaces, surface);
+}
+
+static void
+meta_wayland_region_destroy (struct wl_client *client,
+ struct wl_resource *resource)
+{
+ wl_resource_destroy (resource);
+}
+
+static void
+meta_wayland_region_add (struct wl_client *client,
+ struct wl_resource *resource,
+ gint32 x,
+ gint32 y,
+ gint32 width,
+ gint32 height)
+{
+ MetaWaylandRegion *region = resource->data;
+ cairo_rectangle_int_t rectangle = { x, y, width, height };
+
+ cairo_region_union_rectangle (region->region, &rectangle);
+}
+
+static void
+meta_wayland_region_subtract (struct wl_client *client,
+ struct wl_resource *resource,
+ gint32 x,
+ gint32 y,
+ gint32 width,
+ gint32 height)
+{
+ MetaWaylandRegion *region = resource->data;
+ cairo_rectangle_int_t rectangle = { x, y, width, height };
+
+ cairo_region_subtract_rectangle (region->region, &rectangle);
+}
+
+const struct wl_region_interface meta_wayland_region_interface = {
+ meta_wayland_region_destroy,
+ meta_wayland_region_add,
+ meta_wayland_region_subtract
+};
+
+static void
+meta_wayland_region_resource_destroy_cb (struct wl_resource *resource)
+{
+ MetaWaylandRegion *region = resource->data;
+
+ cairo_region_destroy (region->region);
+ g_slice_free (MetaWaylandRegion, region);
+}
+
+static void
+meta_wayland_compositor_create_region (struct wl_client *wayland_client,
+ struct wl_resource *compositor_resource,
+ uint32_t id)
+{
+ MetaWaylandRegion *region = g_slice_new0 (MetaWaylandRegion);
+
+ region->resource.destroy =
+ meta_wayland_region_resource_destroy_cb;
+ region->resource.object.id = id;
+ region->resource.object.interface = &wl_region_interface;
+ region->resource.object.implementation =
+ (void (**)(void)) &meta_wayland_region_interface;
+ region->resource.data = region;
+
+ region->region = cairo_region_create ();
+
+ wl_client_add_resource (wayland_client, ®ion->resource);
+}
+
+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,
+ int height,
+ int width_mm,
+ int height_mm)
+{
+ MetaWaylandOutput *output = g_slice_new0 (MetaWaylandOutput);
+ MetaWaylandMode *mode;
+ float final_width, final_height;
+
+ /* 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, height);
+
+ /* Read back the actual size we were given.
+ * XXX: This really needs re-thinking later though so we know the
+ * correct output geometry to use. */
+ clutter_actor_get_size (compositor->stage, &final_width, &final_height);
+ width = final_width;
+ height = final_height;
+
+ 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);
+
+ mode = g_slice_new0 (MetaWaylandMode);
+ mode->flags = WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
+ mode->width = width;
+ mode->height = height;
+ mode->refresh = 60;
+
+ output->modes = g_list_prepend (output->modes, mode);
+
+ compositor->outputs = g_list_prepend (compositor->outputs, output);
+}
+
+const static struct wl_compositor_interface meta_wayland_compositor_interface = {
+ meta_wayland_compositor_create_surface,
+ meta_wayland_compositor_create_region
+};
+
+static void
+paint_finished_cb (ClutterActor *self, void *user_data)
+{
+ MetaWaylandCompositor *compositor = user_data;
+
+ while (!wl_list_empty (&compositor->frame_callbacks))
+ {
+ MetaWaylandFrameCallback *callback =
+ wl_container_of (compositor->frame_callbacks.next, callback, link);
+
+ wl_resource_post_event (&callback->resource,
+ WL_CALLBACK_DONE, get_time ());
+ wl_resource_destroy (&callback->resource);
+ }
+}
+
+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_pong (struct wl_client *client,
+ struct wl_resource *resource,
+ guint32 serial)
+{
+}
+
+static void
+shell_surface_move (struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *seat,
+ guint32 serial)
+{
+}
+
+static void
+shell_surface_resize (struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *seat,
+ guint32 serial,
+ guint32 edges)
+{
+}
+
+static void
+ensure_surface_window (MetaWaylandSurface *surface)
+{
+ MetaDisplay *display = meta_get_display ();
+
+ if (!surface->window)
+ {
+ int width, height;
+
+ if (surface->buffer)
+ {
+ struct wl_buffer *buffer = surface->buffer;
+ width = buffer->width;
+ height = buffer->width;
+ }
+ else
+ {
+ width = 0;
+ height = 0;
+ }
+
+ surface->window =
+ meta_window_new_for_wayland (display, width, height, surface);
+
+ /* If the MetaWindow becomes unmanaged (surface->window will be
+ * freed in this case) we need to make sure to clear our
+ * ->window pointers. */
+ g_object_weak_ref (G_OBJECT (surface->window),
+ window_destroyed_cb,
+ surface);
+
+ meta_window_calc_showing (surface->window);
+ }
+}
+
+static void
+shell_surface_set_toplevel (struct wl_client *client,
+ struct wl_resource *resource)
+{
+ MetaWaylandCompositor *compositor = &_meta_wayland_compositor;
+ MetaWaylandShellSurface *shell_surface = resource->data;
+ MetaWaylandSurface *surface = shell_surface->surface;
+
+ /* NB: Surfaces from xwayland become managed based on X events. */
+ if (client == compositor->xwayland_client)
+ return;
+
+ ensure_surface_window (surface);
+
+ meta_window_unmake_fullscreen (surface->window);
+}
+
+static void
+shell_surface_set_transient (struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *parent,
+ int x,
+ int y,
+ guint32 flags)
+{
+ MetaWaylandCompositor *compositor = &_meta_wayland_compositor;
+ MetaWaylandShellSurface *shell_surface = resource->data;
+ MetaWaylandSurface *surface = shell_surface->surface;
+
+ /* NB: Surfaces from xwayland become managed based on X events. */
+ if (client == compositor->xwayland_client)
+ return;
+
+ ensure_surface_window (surface);
+}
+
+static void
+shell_surface_set_fullscreen (struct wl_client *client,
+ struct wl_resource *resource,
+ guint32 method,
+ guint32 framerate,
+ struct wl_resource *output)
+{
+ MetaWaylandCompositor *compositor = &_meta_wayland_compositor;
+ MetaWaylandShellSurface *shell_surface = resource->data;
+ MetaWaylandSurface *surface = shell_surface->surface;
+
+ /* NB: Surfaces from xwayland become managed based on X events. */
+ if (client == compositor->xwayland_client)
+ return;
+
+ ensure_surface_window (surface);
+
+ meta_window_make_fullscreen (surface->window);
+}
+
+static void
+shell_surface_set_popup (struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *seat,
+ guint32 serial,
+ struct wl_resource *parent,
+ gint32 x,
+ gint32 y,
+ guint32 flags)
+{
+}
+
+static void
+shell_surface_set_maximized (struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *output)
+{
+}
+
+static void
+shell_surface_set_title (struct wl_client *client,
+ struct wl_resource *resource,
+ const char *title)
+{
+}
+
+static void
+shell_surface_set_class (struct wl_client *client,
+ struct wl_resource *resource,
+ const char *class_)
+{
+}
+
+static const struct wl_shell_surface_interface meta_wayland_shell_surface_interface =
+{
+ shell_surface_pong,
+ shell_surface_move,
+ shell_surface_resize,
+ shell_surface_set_toplevel,
+ shell_surface_set_transient,
+ shell_surface_set_fullscreen,
+ shell_surface_set_popup,
+ shell_surface_set_maximized,
+ shell_surface_set_title,
+ shell_surface_set_class
+};
+
+static void
+shell_handle_surface_destroy (struct wl_listener *listener,
+ void *data)
+{
+ MetaWaylandShellSurface *shell_surface =
+ wl_container_of (listener, shell_surface, surface_destroy_listener);
+ shell_surface->surface->has_shell_surface = FALSE;
+ shell_surface->surface = NULL;
+ wl_resource_destroy (&shell_surface->resource);
+}
+
+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.notify = shell_handle_surface_destroy;
+ wl_signal_add (&surface->wayland_surface.resource.destroy_signal,
+ &shell_surface->surface_destroy_listener);
+ 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;
+ }
+ close (fd);
+
+ 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);
+ if (unlink (filename) < 0)
+ {
+ const char *msg = strerror (errno);
+ g_warning ("failed to unlink stale lock file: %s", msg);
+ display++;
+ }
+ g_free (filename);
+ continue;
+ }
+
+ 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 (XWAYLAND_PATH,
+ XWAYLAND_PATH,
+ display_name,
+ "-wayland",
+ "-rootless",
+ "-retro",
+ "-noreset",
+ /* FIXME: does it make sense to log to the filesystem by
+ * default? */
+ "-logfile", "/tmp/xwayland.log",
+ "-nolisten", "all",
+ 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));
+
+ meta_window_actor_set_wayland_surface (window_actor, surface);
+
+ surface->window = window;
+
+ /* If the MetaWindow becomes unmanaged (surface->window will be
+ * freed in this case) we need to make sure to clear our
+ * ->window pointers in this case. */
+ g_object_weak_ref (G_OBJECT (surface->window),
+ window_destroyed_cb,
+ surface);
+ }
+#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;
+}
+
+static void
+stage_destroy_cb (void)
+{
+ meta_quit (META_EXIT_SUCCESS);
+}
+
+void
+meta_wayland_init (void)
+{
+ MetaWaylandCompositor *compositor = &_meta_wayland_compositor;
+
+ memset (compositor, 0, sizeof (MetaWaylandCompositor));
+
+ compositor->wayland_display = wl_display_create ();
+ if (compositor->wayland_display == NULL)
+ g_error ("failed to create wayland display");
+
+ wl_display_init_shm (compositor->wayland_display);
+
+ wl_list_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_loop =
+ wl_display_get_event_loop (compositor->wayland_display);
+ compositor->wayland_event_source =
+ wayland_event_source_new (compositor->wayland_display);
+
+ /* XXX: Here we are setting the wayland event source to have a
+ * slightly lower priority than the X event source, because we are
+ * much more likely to get confused being told about surface changes
+ * relating to X clients when we don't know what's happened to them
+ * according to the X protocol.
+ *
+ * At some point we could perhaps try and get the X protocol proxied
+ * over the wayland protocol so that we don't have to worry about
+ * synchronizing the two command streams. */
+ g_source_set_priority (compositor->wayland_event_source,
+ GDK_PRIORITY_EVENTS + 1);
+ 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 ();
+ clutter_stage_set_user_resizable (CLUTTER_STAGE (compositor->stage), FALSE);
+ g_signal_connect_after (compositor->stage, "paint",
+ G_CALLBACK (paint_finished_cb), compositor);
+ g_signal_connect (compositor->stage, "destroy",
+ G_CALLBACK (stage_destroy_cb), NULL);
+
+ meta_wayland_compositor_create_output (compositor, 0, 0, 1024, 600, 222, 125);
+
+ 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_finalize (void)
+{
+ stop_xwayland (meta_wayland_compositor_get_default ());
+}
+
+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]