From f9a11b3b18eb0637e9da20caa612b5671a04d3f8 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Sat, 7 Jan 2012 22:21:32 +0000 Subject: [PATCH] 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. Note: At this point no input is supported Note: multiple authors have contributed to this patch: Authored-by: Robert Bragg Authored-by: Neil Roberts Authored-by: Rico Tzschichholz. Authored-by: Giovanni Campagna --- configure.ac | 2 +- src/Makefile.am | 15 + src/compositor/compositor.c | 349 +++--- src/compositor/meta-plugin-manager.c | 14 +- src/compositor/meta-shaped-texture.c | 381 +++++-- src/compositor/meta-window-actor-private.h | 21 +- src/compositor/meta-window-actor.c | 482 +++++--- src/compositor/meta-window-group.c | 32 +- src/core/display.c | 28 +- src/core/main.c | 116 +- src/core/screen-private.h | 2 + src/core/screen.c | 31 +- src/core/window-private.h | 24 + src/core/window.c | 656 ++++++----- src/meta/compositor.h | 4 +- src/meta/meta-shaped-texture.h | 17 +- src/wayland/meta-wayland-private.h | 171 +++ src/wayland/meta-wayland.c | 1145 ++++++++++++++++++++ src/wayland/meta-xwayland-private.h | 33 + src/wayland/meta-xwayland.c | 310 ++++++ 20 files changed, 3156 insertions(+), 677 deletions(-) create mode 100644 src/wayland/meta-wayland-private.h create mode 100644 src/wayland/meta-wayland.c create mode 100644 src/wayland/meta-xwayland-private.h create mode 100644 src/wayland/meta-xwayland.c diff --git a/configure.ac b/configure.ac index deecd1faa..7c20ac5a3 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 c0884c58a..e25d4a8ee 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -39,6 +39,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 +174,14 @@ 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 \ + wayland/meta-xwayland-private.h \ + wayland/meta-xwayland.c +endif + libmutter_la_LDFLAGS = -no-undefined libmutter_la_LIBADD = $(MUTTER_LIBS) diff --git a/src/compositor/compositor.c b/src/compositor/compositor.c index 89235e641..2d4817f68 100644 --- a/src/compositor/compositor.c +++ b/src/compositor/compositor.c @@ -84,6 +84,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 #include @@ -172,7 +175,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 @@ -327,29 +330,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); + /* 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 sees them. + */ + if (!meta_is_wayland_compositor ()) + { + 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); + if (info->stage && info->output) + { + do_set_stage_input_region (screen, region); + } + else + { + /* 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); + } + } } - else - { - /* 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 @@ -562,6 +573,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 @@ -604,6 +620,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)) @@ -616,7 +635,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_wayland_compositor ()) + 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; @@ -627,7 +653,55 @@ meta_compositor_manage_screen (MetaCompositor *compositor, meta_screen_set_cm_selection (screen); - info->stage = clutter_stage_new (); + /* We will have already created a stage if running as a wayland + * compositor... */ +#ifdef HAVE_WAYLAND + if (meta_is_wayland_compositor ()) + { + wayland_compositor = meta_wayland_compositor_get_default (); + info->stage = wayland_compositor->stage; + } + else +#endif /* HAVE_WAYLAND */ + { + info->stage = clutter_stage_new (); + + meta_screen_get_size (screen, &width, &height); + clutter_actor_realize (info->stage); + + xwin = clutter_x11_get_stage_window (CLUTTER_STAGE (info->stage)); + + XResizeWindow (xdisplay, xwin, width, height); + + { + 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); + XIClearMask (mask.mask, XI_TouchBegin); + XIClearMask (mask.mask, XI_TouchEnd); + XIClearMask (mask.mask, XI_TouchUpdate); + 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); + } + } clutter_stage_set_paint_callback (CLUTTER_STAGE (info->stage), after_stage_paint, @@ -636,42 +710,6 @@ meta_compositor_manage_screen (MetaCompositor *compositor, 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)); - - XResizeWindow (xdisplay, xwin, width, height); - - { - 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); - XIClearMask (mask.mask, XI_TouchBegin); - XIClearMask (mask.mask, XI_TouchEnd); - XIClearMask (mask.mask, XI_TouchUpdate); - 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); @@ -680,53 +718,66 @@ 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_wayland_compositor ()) { - 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); - /* Map overlay window before redirecting windows offscreen so we catch their - * contents until we show the stage. - */ - XMapWindow (xdisplay, info->output); + /* 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); - redirect_windows (compositor, screen); + 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); + + redirect_windows (compositor, screen); + } } 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); + if (!meta_is_wayland_compositor ()) + { + 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); + /* 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); + } } /* @@ -798,15 +849,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_wayland_compositor ()) { - 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); @@ -866,8 +920,8 @@ 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)); @@ -993,7 +1047,8 @@ meta_compositor_process_event (MetaCompositor *compositor, break; default: - if (event->type == meta_display_get_damage_event_base (compositor->display) + XDamageNotify) + if (!meta_is_wayland_compositor () && + 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 @@ -1012,7 +1067,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_wayland_compositor () && event->type == MapNotify) clutter_x11_handle_event (event); /* The above handling is basically just "observing" the events, so we return @@ -1354,22 +1409,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_wayland_compositor ()) + { + /* 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 @@ -1433,29 +1499,32 @@ pre_paint_windows (MetaCompScreen *info) if (info->windows == NULL) return; - 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 (info->unredirected_window != expected_unredirected_window) + if (!meta_is_wayland_compositor ()) { - 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); - } + top_window = g_list_last (info->windows)->data; - 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 (meta_window_actor_should_unredirect (top_window) && + info->disable_unredirect_count == 0) + expected_unredirected_window = top_window; - info->unredirected_window = expected_unredirected_window; + if (info->unredirected_window != expected_unredirected_window) + { + 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); + } + + 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 130a82bf7..2ae100e84 100644 --- a/src/compositor/meta-plugin-manager.c +++ b/src/compositor/meta-plugin-manager.c @@ -317,6 +317,16 @@ meta_plugin_manager_xevent_filter (MetaPluginManager *plugin_mgr, */ if (klass->xevent_filter) return klass->xevent_filter (plugin, xev); - else - return clutter_x11_handle_event (xev) != CLUTTER_X11_FILTER_CONTINUE; + + /* When mutter is running as a wayland compositor, things like input + * events just come directly from clutter so it won't have disabled + * clutter's event retrieval and won't need to forward it events (if + * it did it would lead to recursion). Also when running as a + * wayland compositor we shouldn't be assuming that we're running + * with the clutter x11 backend. + */ + if (meta_is_wayland_compositor ()) + return FALSE; + + 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 c93bb473b..cc095751b 100644 --- a/src/compositor/meta-shaped-texture.c +++ b/src/compositor/meta-shaped-texture.c @@ -30,8 +30,14 @@ #include #include +#include #include "meta-texture-tower.h" +#ifdef HAVE_WAYLAND +#include "meta-wayland-private.h" +#include +#endif + #include #include #include @@ -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,21 @@ 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; + } wayland; +#endif + }; + + CoglTexture *texture; + CoglTexture *mask_texture; CoglPipeline *pipeline; CoglPipeline *pipeline_unshaped; @@ -104,7 +132,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; } @@ -129,6 +160,56 @@ meta_shaped_texture_dispose (GObject *object) G_OBJECT_CLASS (meta_shaped_texture_parent_class)->dispose (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) { @@ -312,6 +393,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 +462,56 @@ 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 *self = g_object_new (META_TYPE_SHAPED_TEXTURE, NULL); + ClutterActor *actor = g_object_new (META_TYPE_SHAPED_TEXTURE, NULL); + MetaShapedTexturePrivate *priv = META_SHAPED_TEXTURE (actor)->priv; - return self; + /* 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_ref.buffer) + meta_shaped_texture_attach_wayland_buffer (stex, + surface->buffer_ref.buffer); +} + +MetaWaylandSurface * +meta_shaped_texture_get_wayland_surface (MetaShapedTexture *stex) +{ + MetaShapedTexturePrivate *priv = stex->priv; + return priv->wayland.surface; +} +#endif /* HAVE_WAYLAND */ + +ClutterActor * +meta_shaped_texture_new_with_xwindow (Window xwindow) +{ +#ifdef HAVE_WAYLAND + if (meta_is_wayland_compositor ()) + { + 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 +530,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 +556,146 @@ 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; + MetaWaylandBuffer *buffer; + + priv = stex->priv; + + g_return_if_fail (priv->type == META_SHAPED_TEXTURE_TYPE_WAYLAND_SURFACE); + g_return_if_fail (priv->texture != NULL); + + buffer = priv->wayland.surface->buffer_ref.buffer; + + if (buffer) + { + struct wl_resource *resource = buffer->resource; + struct wl_shm_buffer *shm_buffer = wl_shm_buffer_get (resource); + + if (shm_buffer) + { + CoglPixelFormat format; + + switch (wl_shm_buffer_get_format (shm_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 (shm_buffer), + wl_shm_buffer_get_data (shm_buffer)); + } + } +} +#endif /* HAVE_WAYLAND */ + +static void +queue_damage_redraw_with_clip (MetaShapedTexture *stex, + int x, + int y, + int width, + int height) +{ + ClutterActor *self = CLUTTER_ACTOR (stex); + MetaShapedTexturePrivate *priv; + 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... + */ + + /* 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->tex_width == 0 || priv->tex_height == 0) + return; + + clutter_actor_get_allocation_box (self, &allocation); + + scale_x = (allocation.x2 - allocation.x1) / priv->tex_width; + scale_y = (allocation.y2 - allocation.y1) / priv->tex_height; + + 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); +} + +void +meta_shaped_texture_update_area (MetaShapedTexture *stex, + int x, + int y, + int width, + int height) { MetaShapedTexturePrivate *priv; - const cairo_rectangle_int_t clip = { x, y, width, height }; priv = stex->priv; if (priv->texture == NULL) return; - cogl_texture_pixmap_x11_update_area (priv->texture, - x, y, width, height); + switch (priv->type) + { + 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 + } meta_texture_tower_update_area (priv->paint_tower, x, y, width, height); - clutter_actor_queue_redraw_with_clip (CLUTTER_ACTOR (stex), &clip); -} - -static void -set_cogl_texture (MetaShapedTexture *stex, - CoglTexturePixmapX11 *cogl_tex) -{ - MetaShapedTexturePrivate *priv; - guint width, height; - - g_return_if_fail (META_IS_SHAPED_TEXTURE (stex)); - - priv = stex->priv; - - if (priv->texture != NULL) - 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 inavlid texture */ - priv->tex_width = 0; - priv->tex_height = 0; - clutter_actor_queue_relayout (CLUTTER_ACTOR (stex)); - } - - clutter_actor_queue_redraw (CLUTTER_ACTOR (stex)); + queue_damage_redraw_with_clip (stex, x, y, width, height); } /** @@ -516,16 +713,18 @@ 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); @@ -535,6 +734,54 @@ meta_shaped_texture_set_pixmap (MetaShapedTexture *stex, COGL_TEXTURE (priv->texture)); } +#ifdef HAVE_WAYLAND +void +meta_shaped_texture_attach_wayland_buffer (MetaShapedTexture *stex, + MetaWaylandBuffer *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_ref.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->resource, + &catch_error)); + if (!texture) + { + cogl_error_free (catch_error); + } + else + { + buffer->width = cogl_texture_get_width (texture); + buffer->height = cogl_texture_get_height (texture); + } + + 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)); +} +#endif /* HAVE_WAYLAND */ + /** * meta_shaped_texture_get_texture: * @stex: The #MetaShapedTexture diff --git a/src/compositor/meta-window-actor-private.h b/src/compositor/meta-window-actor-private.h index 90a9e35e0..d37ee2dbb 100644 --- a/src/compositor/meta-window-actor-private.h +++ b/src/compositor/meta-window-actor-private.h @@ -5,6 +5,11 @@ #include +#ifdef HAVE_WAYLAND +#include +#include +#endif + #include #include @@ -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, + MetaWaylandBuffer *buffer); +#endif void meta_window_actor_pre_paint (MetaWindowActor *self); void meta_window_actor_post_paint (MetaWindowActor *self); diff --git a/src/compositor/meta-window-actor.c b/src/compositor/meta-window-actor.c index 34a8fa1b9..54f6838aa 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,10 +67,6 @@ struct _MetaWindowActorPrivate MetaShadow *focused_shadow; MetaShadow *unfocused_shadow; - Pixmap back_pixmap; - - Damage damage; - guint8 opacity; /* A region that matches the shape of the window, including frame bounds */ @@ -104,31 +103,41 @@ struct _MetaWindowActorPrivate /* List of FrameData for recent frames */ GList *frames; + Pixmap back_pixmap; /* Not used in wayland compositor mode */ + Damage damage; /* Not used in wayland compositor mode */ + guint visible : 1; guint mapped : 1; guint argb32 : 1; guint disposed : 1; guint redecorating : 1; - guint needs_damage_all : 1; - guint received_damage : 1; - guint repaint_scheduled : 1; - /* 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 recompute_focused_shadow : 1; guint recompute_unfocused_shadow : 1; - guint size_changed : 1; - guint updates_frozen : 1; guint needs_destroy : 1; guint no_shadow : 1; + + /* + * None of these are used in wayland compositor mode... + */ + + guint needs_damage_all : 1; + guint received_x11_damage : 1; + + guint needs_pixmap : 1; + + guint x11_size_changed : 1; + guint updates_frozen : 1; + guint unredirected : 1; /* This is used to detect fullscreen windows that need to be unredirected */ @@ -172,7 +181,7 @@ static gboolean meta_window_actor_get_paint_volume (ClutterActor *actor, ClutterPaintVolume *volume); -static void meta_window_actor_detach (MetaWindowActor *self); +static void meta_window_actor_detach_x11_pixmap (MetaWindowActor *self); static gboolean meta_window_actor_has_shadow (MetaWindowActor *self); static void meta_window_actor_handle_updates (MetaWindowActor *self); @@ -306,18 +315,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_wayland_compositor ()) { - meta_error_trap_push (display); - XDamageDestroy (xdisplay, priv->damage); - meta_error_trap_pop (display); - priv->damage = None; + meta_window_actor_detach_x11_pixmap (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; + } } priv->xwindow = new_xwindow; @@ -348,8 +360,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_wayland_compositor ()) + priv->damage = XDamageCreate (xdisplay, xwindow, + XDamageReportBoundingBox); format = XRenderFindVisualFormat (xdisplay, window->xvisual); @@ -358,7 +371,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); @@ -387,9 +405,10 @@ meta_window_actor_constructed (GObject *object) meta_window_actor_update_opacity (self); - /* Start off with an empty region to maintain the invariant that - the shape region is always set */ + /* Start off with empty regions to maintain the invariant that + these regions are always set */ priv->shape_region = cairo_region_create(); + priv->input_shape_region = cairo_region_create(); } static void @@ -408,11 +427,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_wayland_compositor ()) + { + display = meta_screen_get_display (screen); + xdisplay = meta_display_get_xdisplay (display); + + meta_window_actor_detach_x11_pixmap (self); + } g_clear_pointer (&priv->shape_region, cairo_region_destroy); g_clear_pointer (&priv->input_shape_region, cairo_region_destroy); @@ -424,7 +447,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_wayland_compositor () && priv->damage != None) { meta_error_trap_push (display); XDamageDestroy (xdisplay, priv->damage); @@ -896,7 +919,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_wayland_compositor ()) + self->priv->freeze_count++; } static void @@ -925,30 +949,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_wayland_compositor ()) { - g_warning ("Error in freeze/thaw accounting."); - self->priv->freeze_count = 0; - return; + self->priv->freeze_count--; + + if (G_UNLIKELY (self->priv->freeze_count < 0)) + { + g_warning ("Error in freeze/thaw accounting."); + self->priv->freeze_count = 0; + return; + } + + if (self->priv->freeze_count) + return; + + /* We sometimes ignore moves and resizes on frozen windows */ + meta_window_actor_sync_actor_geometry (self, FALSE); + + /* 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); } - - if (self->priv->freeze_count) - return; - - /* We sometimes ignore moves and resizes on frozen windows */ - meta_window_actor_sync_actor_geometry (self, FALSE); - - /* 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 @@ -979,7 +1006,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_wayland_compositor () || !priv->needs_pixmap)) { const cairo_rectangle_int_t clip = { 0, 0, 1, 1 }; clutter_actor_queue_redraw_with_clip (priv->actor, &clip); @@ -1005,7 +1032,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; @@ -1109,11 +1136,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_wayland_compositor ()) + { + if (!meta_window_is_mapped (priv->window)) + meta_window_actor_detach_x11_pixmap (self); - if (priv->needs_pixmap) - clutter_actor_queue_redraw (priv->actor); + if (priv->needs_pixmap) + clutter_actor_queue_redraw (priv->actor); + } } void @@ -1194,7 +1224,7 @@ meta_window_actor_effect_completed (MetaWindowActor *self, * pixmap for a new size. */ static void -meta_window_actor_detach (MetaWindowActor *self) +meta_window_actor_detach_x11_pixmap (MetaWindowActor *self) { MetaWindowActorPrivate *priv = self->priv; MetaScreen *screen = priv->screen; @@ -1215,7 +1245,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 @@ -1245,7 +1275,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_wayland_compositor () && priv->does_full_damage) return TRUE; return FALSE; @@ -1265,7 +1295,7 @@ meta_window_actor_set_redirected (MetaWindowActor *self, gboolean state) meta_error_trap_push (display); XCompositeRedirectWindow (xdisplay, xwin, CompositeRedirectManual); meta_error_trap_pop (display); - meta_window_actor_detach (self); + meta_window_actor_detach_x11_pixmap (self); self->priv->unredirected = FALSE; } else @@ -1338,15 +1368,21 @@ meta_window_actor_sync_actor_geometry (MetaWindowActor *self, meta_window_get_input_rect (priv->window, &window_rect); - if (priv->last_width != window_rect.width || - priv->last_height != window_rect.height) + /* When running as a display server then we instead catch size changes when + * new buffers are attached */ + if (!meta_is_wayland_compositor ()) { - priv->size_changed = TRUE; - meta_window_actor_queue_create_pixmap (self); - meta_window_actor_update_shape (self); + if (priv->last_width != window_rect.width || + priv->last_height != window_rect.height) + { + priv->x11_size_changed = TRUE; + meta_window_actor_queue_create_x11_pixmap (self); - priv->last_width = window_rect.width; - priv->last_height = window_rect.height; + meta_window_actor_update_shape (self); + + priv->last_width = window_rect.width; + priv->last_height = window_rect.height; + } } if (meta_window_actor_effect_in_progress (self)) @@ -1510,16 +1546,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, @@ -1529,21 +1576,24 @@ meta_window_actor_new (MetaWindow *window) priv = self->priv; - priv->last_width = -1; - priv->last_height = -1; + if (!meta_is_wayland_compositor ()) + { + 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); + } meta_window_actor_sync_actor_geometry (self, priv->window->placed); @@ -1578,7 +1628,8 @@ meta_window_actor_mapped (MetaWindowActor *self) priv->mapped = TRUE; - meta_window_actor_queue_create_pixmap (self); + if (!meta_is_wayland_compositor ()) + meta_window_actor_queue_create_x11_pixmap (self); } void @@ -1593,8 +1644,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_wayland_compositor ()) + { + meta_window_actor_detach_x11_pixmap (self); + priv->needs_pixmap = FALSE; + } } /** @@ -1612,10 +1666,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_wayland_compositor ()) + { + if (priv->opacity == 0xff) + return priv->opaque_region; + } + else + { + if (priv->back_pixmap && priv->opacity == 0xff) + return priv->opaque_region; + } + } + + return NULL; } #if 0 @@ -1728,8 +1793,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; @@ -1751,10 +1819,10 @@ check_needs_pixmap (MetaWindowActor *self) compositor = meta_display_get_compositor (display); - if (priv->size_changed) + if (priv->x11_size_changed) { - meta_window_actor_detach (self); - priv->size_changed = FALSE; + meta_window_actor_detach_x11_pixmap (self); + priv->x11_size_changed = FALSE; } meta_error_trap_push (display); @@ -1886,13 +1954,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) { @@ -1946,6 +2014,25 @@ meta_window_actor_process_damage (MetaWindowActor *self, priv->repaint_scheduled = TRUE; } +#ifdef HAVE_WAYLAND +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; +} +#endif + void meta_window_actor_sync_visibility (MetaWindowActor *self) { @@ -2106,8 +2193,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; @@ -2212,8 +2299,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; @@ -2299,8 +2386,24 @@ 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); + if (priv->window->client_type == META_WINDOW_CLIENT_TYPE_X11) + { + meta_window_actor_update_x11_shape_region (self, &client_area); + meta_window_actor_update_x11_input_shape_region (self, &client_area); + } + else + { + /* TODO: properly support setting an input region as specified + * via the wayland protocol */ + + g_clear_pointer (&priv->shape_region, cairo_region_destroy); + g_clear_pointer (&priv->opaque_region, cairo_region_destroy); + g_clear_pointer (&priv->input_shape_region, cairo_region_destroy); + + priv->shape_region = cairo_region_create_rectangle (&client_area); + priv->opaque_region = cairo_region_reference (priv->shape_region); + priv->input_shape_region = cairo_region_reference (priv->shape_region); + } priv->needs_reshape = FALSE; } @@ -2318,6 +2421,69 @@ meta_window_actor_update_shape (MetaWindowActor *self) clutter_actor_queue_redraw (priv->actor); } +#ifdef HAVE_WAYLAND +static void +maybe_emit_size_changed (MetaWindowActor *self, + MetaWaylandBuffer *new_buffer) +{ + MetaWindowActorPrivate *priv = self->priv; + int width = 0, height = 0; + + if (new_buffer) + { + width = new_buffer->width; + height = new_buffer->height; + } + + if (priv->last_width != width || priv->last_height != height) + { + meta_window_actor_update_shape (self); + + /* ::size-changed is supposed to refer to meta_window_get_outer_rect() + * but here we are only looking at buffer size changes. + * + * Emitting it here works pretty much OK because a new buffer size (which + * will correspond to the outer rect with the addition of invisible + * borders) also normally implies a change to the outer rect. In the rare + * case where a change to the window size was exactly balanced by a + * change to the invisible borders, we would miss emitting the signal. + */ + g_signal_emit (self, signals[SIZE_CHANGED], 0); + + priv->last_width = width; + priv->last_height = height; + } +} + +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_ref.buffer) + maybe_emit_size_changed (self, surface->buffer_ref.buffer); +} + +void +meta_window_actor_attach_wayland_buffer (MetaWindowActor *self, + MetaWaylandBuffer *buffer) +{ + MetaWindowActorPrivate *priv = self->priv; + MetaShapedTexture *stex = META_SHAPED_TEXTURE (priv->actor); + CoglTexture *prev_tex = meta_shaped_texture_get_texture (stex); + + meta_shaped_texture_attach_wayland_buffer (stex, buffer); + + if (!prev_tex) + meta_window_actor_sync_actor_geometry (self, FALSE); + + maybe_emit_size_changed (self, buffer); +} +#endif /* HAVE_WAYLAND */ + static void meta_window_actor_handle_updates (MetaWindowActor *self) { @@ -2333,42 +2499,46 @@ meta_window_actor_handle_updates (MetaWindowActor *self) return; } - if (priv->unredirected) + if (!meta_is_wayland_compositor ()) { - /* 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_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); + + priv->received_x11_damage = FALSE; + } + + check_needs_x11_pixmap (self); } - if (priv->received_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); - - priv->received_damage = FALSE; - } - - check_needs_pixmap (self); check_needs_reshape (self); check_needs_shadow (self); } @@ -2547,16 +2717,20 @@ void meta_window_actor_set_updates_frozen (MetaWindowActor *self, gboolean updates_frozen) { - MetaWindowActorPrivate *priv = self->priv; - - updates_frozen = updates_frozen != FALSE; - - if (priv->updates_frozen != updates_frozen) + /* On wayland we shouldn't need to ever freeze updates... */ + if (!meta_is_wayland_compositor ()) { - priv->updates_frozen = updates_frozen; - if (updates_frozen) - meta_window_actor_freeze (self); - else - meta_window_actor_thaw (self); + MetaWindowActorPrivate *priv = self->priv; + + 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); + } } } diff --git a/src/compositor/meta-window-group.c b/src/compositor/meta-window-group.c index ec22af31a..8fddc5e67 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 { @@ -99,7 +100,7 @@ meta_window_group_paint (ClutterActor *actor) int paint_x_offset, paint_y_offset; MetaWindowGroup *window_group = META_WINDOW_GROUP (actor); - MetaCompScreen *info = meta_screen_get_compositor_data (window_group->screen); + MetaCompScreen *info; /* 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 @@ -136,13 +137,17 @@ meta_window_group_paint (ClutterActor *actor) visible_region = cairo_region_create_rectangle (&visible_rect); - if (info->unredirected_window != NULL) + if (!meta_is_wayland_compositor ()) { - cairo_rectangle_int_t unredirected_rect; - MetaWindow *window = meta_window_actor_get_meta_window (info->unredirected_window); + info = meta_screen_get_compositor_data (window_group->screen); + if (info->unredirected_window != NULL) + { + cairo_rectangle_int_t unredirected_rect; + MetaWindow *window = meta_window_actor_get_meta_window (info->unredirected_window); - meta_window_get_outer_rect (window, (MetaRectangle *)&unredirected_rect); - cairo_region_subtract_rectangle (visible_region, &unredirected_rect); + meta_window_get_outer_rect (window, (MetaRectangle *)&unredirected_rect); + cairo_region_subtract_rectangle (visible_region, &unredirected_rect); + } } /* We walk the list from top to bottom (opposite of painting order), @@ -155,7 +160,8 @@ meta_window_group_paint (ClutterActor *actor) if (!CLUTTER_ACTOR_IS_VISIBLE (child)) continue; - if (info->unredirected_window != NULL && + if (!meta_is_wayland_compositor () && + info->unredirected_window != NULL && child == CLUTTER_ACTOR (info->unredirected_window)) continue; @@ -180,7 +186,8 @@ meta_window_group_paint (ClutterActor *actor) if (META_IS_WINDOW_ACTOR (child)) { - MetaWindowActor *window_actor = META_WINDOW_ACTOR (child); + MetaWindow *meta_window; + MetaWindowActor *window_actor = child; int x, y; if (!meta_actor_is_untransformed (CLUTTER_ACTOR (window_actor), &x, &y)) @@ -194,7 +201,14 @@ 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) + /* TODO: Track the opaque regions of wayland clients. + * Although wayland clients can report opaque window + * regions, 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 9bcb5e5c7..5e19f1439 100644 --- a/src/core/display.c +++ b/src/core/display.c @@ -931,8 +931,24 @@ meta_display_open (void) while (tmp != NULL) { MetaScreen *screen = tmp->data; - - meta_screen_manage_all_windows (screen); + + if (meta_is_wayland_compositor ()) + { + /* 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; } @@ -2284,8 +2300,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) { @@ -2311,8 +2327,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 diff --git a/src/core/main.c b/src/core/main.c index 4bec3d260..9dc758e7d 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -55,6 +55,9 @@ #include "session.h" #include #include +#ifdef HAVE_WAYLAND +#include "meta-wayland-private.h" +#endif #include #include @@ -346,28 +349,74 @@ meta_finalize (void) if (display) meta_display_close (display, CurrentTime); /* I doubt correct timestamps matter here */ + +#ifdef HAVE_WAYLAND + if (meta_is_wayland_compositor ()) + 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 +445,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_wayland_compositor ()) + { + 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 +483,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_wayland_compositor ()) + { + /* 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 +506,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_wayland_compositor ()) + { + /* + * 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 83adb8317..7e8a13318 100644 --- a/src/core/screen-private.h +++ b/src/core/screen-private.h @@ -257,4 +257,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 6db3ea385..e3c997eb4 100644 --- a/src/core/screen.c +++ b/src/core/screen.c @@ -45,6 +45,9 @@ #include #include "mutter-enum-types.h" #include "core.h" +#ifdef HAVE_WAYLAND +#include "meta-wayland-private.h" +#endif #include @@ -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_wayland_compositor ()) + { + 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 42d380f7e..044d89710 100644 --- a/src/core/window-private.h +++ b/src/core/window-private.h @@ -44,6 +44,17 @@ #include #include #include +#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 faa9c773d..df535289d 100644 --- a/src/core/window.c +++ b/src/core/window.c @@ -713,10 +713,10 @@ meta_window_new (MetaDisplay *display, * Returns TRUE if window has been filtered out and should be ignored. */ static gboolean -maybe_filter_window (MetaDisplay *display, - Window xwindow, - gboolean must_be_viewable, - XWindowAttributes *attrs) +maybe_filter_xwindow (MetaDisplay *display, + Window xwindow, + gboolean must_be_viewable, + XWindowAttributes *attrs) { static char **filter_wm_classes = NULL; static gboolean initialized = FALSE; @@ -812,81 +812,25 @@ 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_shared (MetaDisplay *display, + MetaScreen *screen, + MetaWindowClientType client_type, + MetaWaylandSurface *surface, + Window xwindow, + gboolean must_be_viewable, + gulong existing_wm_state, + gboolean has_shape, + gboolean has_input_shape, + MetaCompEffect effect, + XWindowAttributes *attrs) { MetaWindow *window; - GSList *tmp; MetaWorkspace *space; - gulong existing_wm_state; - gulong event_mask; MetaMoveResizeFlags flags; - gboolean has_shape; - gboolean has_input_shape; - 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)) - { - meta_verbose ("Not managing no_focus_window 0x%lx\n", - xwindow); - return NULL; - } - - screen = NULL; - for (tmp = display->screens; tmp != NULL; tmp = tmp->next) - { - MetaScreen *scr = tmp->data; - - if (scr->xroot == attrs->root) - { - screen = tmp->data; - break; - } - } - - g_assert (screen); - - /* A black list of override redirect windows that we don't need to manage: */ - if (attrs->override_redirect && - (xwindow == screen->no_focus_window || - xwindow == screen->flash_window || - xwindow == screen->wm_sn_selection_window || - attrs->class == InputOnly || - /* any windows created via meta_create_offscreen_window: */ - (attrs->x == -100 && attrs->y == -100 - && attrs->width == 1 && attrs->height == 1) || - xwindow == screen->wm_cm_selection_window || - xwindow == screen->guard_window || - (display->compositor && - xwindow == XCompositeGetOverlayWindow (display->xdisplay, - screen->xroot) - ) - ) - ) { - meta_verbose ("Not managing our own windows\n"); - return NULL; - } - - if (maybe_filter_window (display, xwindow, must_be_viewable, attrs)) - { - meta_verbose ("Not managing filtered window\n"); - return NULL; - } - - /* Grab server */ - meta_display_grab (display); - meta_error_trap_push (display); /* Push a trap over all of window - * creation, to reduce XSync() calls - */ - meta_verbose ("must_be_viewable = %d attrs->map_state = %d (%s)\n", must_be_viewable, attrs->map_state, @@ -898,150 +842,16 @@ meta_window_new_with_attrs (MetaDisplay *display, "IsUnviewable" : "(unknown)"); - existing_wm_state = WithdrawnState; - if (must_be_viewable && attrs->map_state != IsViewable) - { - /* 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)); - } - - 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); - - 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); - - { - 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); - - 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); - } - - 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); - } -#endif - - /* 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; - - set_attrs.win_gravity = NorthWestGravity; - - 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; - } - - 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 @@ -1168,7 +978,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; @@ -1227,23 +1041,26 @@ meta_window_new_with_attrs (MetaDisplay *display, window->tile_match = NULL; - if (window->override_redirect) - { - window->decorated = FALSE; - window->always_sticky = TRUE; - window->has_close_func = FALSE; - window->has_shade_func = FALSE; - window->has_move_func = FALSE; - window->has_resize_func = FALSE; - } - - meta_display_register_x_window (display, &window->xwindow, window); - /* Assign this #MetaWindow a sequence number which can be used * for sorting. */ window->stable_sequence = ++display->window_sequence_counter; + if (client_type == META_WINDOW_CLIENT_TYPE_X11) + { + if (window->override_redirect) + { + window->decorated = FALSE; + window->always_sticky = TRUE; + window->has_close_func = FALSE; + window->has_shade_func = FALSE; + window->has_move_func = FALSE; + window->has_resize_func = FALSE; + } + + meta_display_register_x_window (display, &window->xwindow, window); + } + /* assign the window to its group, or create a new group if needed */ window->group = NULL; @@ -1252,7 +1069,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 */ @@ -1526,11 +1344,14 @@ meta_window_new_with_attrs (MetaDisplay *display, !window->initially_iconic) unminimize_window_and_all_transient_parents (window); - meta_error_trap_pop (display); /* pop the XSync()-reducing trap */ - meta_display_ungrab (display); - window->constructing = FALSE; + return window; +} + +static void +display_notify_window (MetaDisplay *display, MetaWindow *window) +{ meta_display_notify_window_created (display, window); if (window->wm_state_demands_attention) @@ -1538,6 +1359,309 @@ meta_window_new_with_attrs (MetaDisplay *display, if (window->wm_hints_urgent) g_signal_emit_by_name (window->display, "window-marked-urgent", 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; + + /* XXX: Note: In the Wayland case we currently still grab the + * xserver and trap X errors while creating a MetaWindow because we + * will still be making various redundant X requests (passing a + * window xid of None) until we thoroughly audit all the code to + * make sure it knows about non X based clients... + */ + + /* Grab server */ + meta_display_grab (display); + meta_error_trap_push (display); /* Push a trap over all of window + * creation, to reduce XSync() calls + */ + + window = meta_window_new_shared (display, + scr, + META_WINDOW_CLIENT_TYPE_WAYLAND, + surface, + None, + TRUE, + WithdrawnState, + FALSE, /* has shape */ + FALSE, /* has input shape */ + META_COMP_EFFECT_NONE, + &attrs); + + meta_error_trap_pop (display); /* pop the XSync()-reducing trap */ + meta_display_ungrab (display); + + /* XXX: Maybe this could be called in meta_window_new_shared() but + * before splitting the X11 specific code out it came after the + * meta_display_ungrab() and we wanted to minimize the risk of + * breaking something. + */ + display_notify_window (window->display, window); + + return window; +} +#endif + +MetaWindow* +meta_window_new_with_attrs (MetaDisplay *display, + Window xwindow, + gboolean must_be_viewable, + MetaCompEffect effect, + XWindowAttributes *attrs) +{ + MetaScreen *screen = NULL; + GSList *tmp; + gulong existing_wm_state; + MetaWindow *window; + gulong event_mask; + gboolean has_shape = FALSE; + gboolean has_input_shape = FALSE; + + meta_verbose ("Attempting to manage 0x%lx\n", xwindow); + + if (meta_display_xwindow_is_a_no_focus_window (display, xwindow)) + { + meta_verbose ("Not managing no_focus_window 0x%lx\n", + xwindow); + return NULL; + } + + for (tmp = display->screens; tmp != NULL; tmp = tmp->next) + { + MetaScreen *scr = tmp->data; + + if (scr->xroot == attrs->root) + { + screen = tmp->data; + break; + } + } + + g_assert (screen); + + /* A black list of override redirect windows that we don't need to manage: */ + if (attrs->override_redirect && + (xwindow == screen->no_focus_window || + xwindow == screen->flash_window || + xwindow == screen->wm_sn_selection_window || + attrs->class == InputOnly || + /* any windows created via meta_create_offscreen_window: */ + (attrs->x == -100 && attrs->y == -100 + && attrs->width == 1 && attrs->height == 1) || + xwindow == screen->wm_cm_selection_window || + xwindow == screen->guard_window || + (display->compositor && + xwindow == XCompositeGetOverlayWindow (display->xdisplay, + screen->xroot) + ) + ) + ) { + meta_verbose ("Not managing our own windows\n"); + return NULL; + } + + if (maybe_filter_xwindow (display, xwindow, must_be_viewable, attrs)) + { + meta_verbose ("Not managing filtered window\n"); + return NULL; + } + + /* Grab server */ + meta_display_grab (display); + meta_error_trap_push (display); /* Push a trap over all of window + * creation, to reduce XSync() calls + */ + + existing_wm_state = WithdrawnState; + if (must_be_viewable && attrs->map_state != IsViewable) + { + /* 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)); + } + + 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); + + 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); + + { + 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); + + 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); + } + +#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); + } +#endif + + /* 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; + + set_attrs.win_gravity = NorthWestGravity; + + 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; + } + + window = meta_window_new_shared (display, + screen, + META_WINDOW_CLIENT_TYPE_X11, + NULL, + xwindow, + must_be_viewable, + existing_wm_state, + has_shape, + has_input_shape, + effect, + attrs); + + meta_error_trap_pop (display); /* pop the XSync()-reducing trap */ + meta_display_ungrab (display); + + /* XXX: Maybe this could be called in meta_window_new_shared() but + * before splitting the X11 specific code out it came after the + * meta_display_ungrab() and we wanted to minimize the risk of + * breaking something. + */ + display_notify_window (display, window); return window; } @@ -1914,48 +2038,50 @@ meta_window_unmanage (MetaWindow *window, meta_display_ungrab_window_buttons (window->display, window->xwindow); meta_display_ungrab_focus_window_button (window->display, window); - meta_display_unregister_x_window (window->display, window->xwindow); - - - meta_error_trap_push (window->display); - - /* Put back anything we messed up */ - if (window->border_width != 0) - XSetWindowBorderWidth (window->display->xdisplay, - window->xwindow, - window->border_width); - - /* No save set */ - XRemoveFromSaveSet (window->display->xdisplay, - window->xwindow); - - /* Even though the window is now unmanaged, we can't unselect events. This - * window might be a window from this process, like a GdkMenu, in - * which case it will have pointer events and so forth selected - * for it by GDK. There's no way to disentangle those events from the events - * we've selected. Even for a window from a different X client, - * GDK could also have selected events for it for IPC purposes, so we - * can't unselect in that case either. - * - * Similarly, we can't unselected for events on window->user_time_window. - * It might be our own GDK focus window, or it might be a window that a - * different client is using for multiple different things: - * _NET_WM_USER_TIME_WINDOW and IPC, perhaps. - */ - - if (window->user_time_window != None) + if (window->client_type == META_WINDOW_CLIENT_TYPE_X11) { - meta_display_unregister_x_window (window->display, - window->user_time_window); - window->user_time_window = None; - } + meta_display_unregister_x_window (window->display, window->xwindow); + + meta_error_trap_push (window->display); + + /* Put back anything we messed up */ + if (window->border_width != 0) + XSetWindowBorderWidth (window->display->xdisplay, + window->xwindow, + window->border_width); + + /* No save set */ + XRemoveFromSaveSet (window->display->xdisplay, + window->xwindow); + + /* Even though the window is now unmanaged, we can't unselect events. This + * window might be a window from this process, like a GdkMenu, in + * which case it will have pointer events and so forth selected + * for it by GDK. There's no way to disentangle those events from the events + * we've selected. Even for a window from a different X client, + * GDK could also have selected events for it for IPC purposes, so we + * can't unselect in that case either. + * + * Similarly, we can't unselected for events on window->user_time_window. + * It might be our own GDK focus window, or it might be a window that a + * different client is using for multiple different things: + * _NET_WM_USER_TIME_WINDOW and IPC, perhaps. + */ + + if (window->user_time_window != None) + { + meta_display_unregister_x_window (window->display, + window->user_time_window); + window->user_time_window = None; + } #ifdef HAVE_SHAPE - if (META_DISPLAY_HAS_SHAPE (window->display)) - XShapeSelectInput (window->display->xdisplay, window->xwindow, NoEventMask); + if (META_DISPLAY_HAS_SHAPE (window->display)) + XShapeSelectInput (window->display->xdisplay, window->xwindow, NoEventMask); #endif - meta_error_trap_pop (window->display); + meta_error_trap_pop (window->display); + } meta_prefs_remove_listener (prefs_changed_callback, window); @@ -7697,7 +7823,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 13143c992..de81c207a 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 2e122843b..2809b25f8 100644 --- a/src/meta/meta-shaped-texture.h +++ b/src/meta/meta-shaped-texture.h @@ -29,6 +29,11 @@ #include #include +#ifdef HAVE_WAYLAND +#include +#include "meta-wayland-private.h" +#endif + G_BEGIN_DECLS #define META_TYPE_SHAPED_TEXTURE (meta_shaped_texture_get_type()) @@ -64,7 +69,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 +88,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, + MetaWaylandBuffer *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 000000000..59cc6d678 --- /dev/null +++ b/src/wayland/meta-wayland-private.h @@ -0,0 +1,171 @@ +/* + * 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 + +#include + +#include +#include + +#include "window-private.h" + +typedef struct _MetaWaylandCompositor MetaWaylandCompositor; + +typedef struct +{ + struct wl_resource *resource; + struct wl_signal destroy_signal; + struct wl_listener destroy_listener; + + union + { + struct wl_shm_buffer *shm_buffer; + struct wl_buffer *legacy_buffer; + }; + + int32_t width, height; + uint32_t busy_count; +} MetaWaylandBuffer; + +typedef struct +{ + MetaWaylandBuffer *buffer; + struct wl_listener destroy_listener; +} MetaWaylandBufferReference; + +typedef struct +{ + struct wl_resource *resource; + cairo_region_t *region; +} MetaWaylandRegion; + +struct _MetaWaylandSurface +{ + struct wl_resource *resource; + MetaWaylandCompositor *compositor; + guint32 xid; + int x; + int y; + MetaWaylandBufferReference buffer_ref; + MetaWindow *window; + gboolean has_shell_surface; + + /* All the pending state, that wl_surface.commit will apply. */ + struct + { + /* wl_surface.attach */ + gboolean newly_attached; + MetaWaylandBuffer *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 000000000..437406818 --- /dev/null +++ b/src/wayland/meta-wayland.c @@ -0,0 +1,1145 @@ +/* + * Wayland Support + * + * Copyright (C) 2012,2013 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 + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "xserver-server-protocol.h" + +#include "meta-wayland-private.h" +#include "meta-xwayland-private.h" +#include "meta-window-actor-private.h" +#include "display-private.h" +#include "window-private.h" +#include +#include +#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 +meta_wayland_buffer_destroy_handler (struct wl_listener *listener, + void *data) +{ + MetaWaylandBuffer *buffer = + wl_container_of (listener, buffer, destroy_listener); + + wl_signal_emit (&buffer->destroy_signal, buffer); + g_slice_free (MetaWaylandBuffer, buffer); +} + +static MetaWaylandBuffer * +meta_wayland_buffer_from_resource (struct wl_resource *resource) +{ + MetaWaylandBuffer *buffer; + struct wl_listener *listener; + + listener = + wl_resource_get_destroy_listener (resource, + meta_wayland_buffer_destroy_handler); + + if (listener) + { + buffer = wl_container_of (listener, buffer, destroy_listener); + } + else + { + buffer = g_slice_new0 (MetaWaylandBuffer); + + buffer->resource = resource; + wl_signal_init (&buffer->destroy_signal); + buffer->destroy_listener.notify = meta_wayland_buffer_destroy_handler; + wl_resource_add_destroy_listener (resource, &buffer->destroy_listener); + } + + return buffer; +} + +static void +meta_wayland_buffer_reference_handle_destroy (struct wl_listener *listener, + void *data) +{ + MetaWaylandBufferReference *ref = + wl_container_of (listener, ref, destroy_listener); + + g_assert (data == ref->buffer); + + ref->buffer = NULL; +} + +static void +meta_wayland_buffer_reference (MetaWaylandBufferReference *ref, + MetaWaylandBuffer *buffer) +{ + if (ref->buffer && buffer != ref->buffer) + { + ref->buffer->busy_count--; + + if (ref->buffer->busy_count == 0) + { + g_assert (wl_resource_get_client (ref->buffer->resource)); + wl_resource_queue_event (ref->buffer->resource, WL_BUFFER_RELEASE); + } + + wl_list_remove (&ref->destroy_listener.link); + } + + if (buffer && buffer != ref->buffer) + { + buffer->busy_count++; + wl_signal_add (&buffer->destroy_signal, &ref->destroy_listener); + } + + ref->buffer = buffer; + ref->destroy_listener.notify = meta_wayland_buffer_reference_handle_destroy; +} + +static void +surface_process_damage (MetaWaylandSurface *surface, + cairo_region_t *region) +{ + if (surface->window && + surface->buffer_ref.buffer) + { + 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_attach (struct wl_client *wayland_client, + struct wl_resource *wayland_surface_resource, + struct wl_resource *wayland_buffer_resource, + gint32 sx, gint32 sy) +{ + MetaWaylandSurface *surface = + wl_resource_get_user_data (wayland_surface_resource); + MetaWaylandBuffer *buffer; + + if (wayland_buffer_resource) + buffer = meta_wayland_buffer_from_resource (wayland_buffer_resource); + else + buffer = 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->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 = wl_resource_get_user_data (surface_resource); + 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 = + wl_resource_get_user_data (callback_resource); + + 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 = wl_resource_get_user_data (surface_resource); + + callback = g_slice_new0 (MetaWaylandFrameCallback); + callback->compositor = surface->compositor; + callback->resource = wl_resource_create (client, + &wl_callback_interface, 1, + callback_id); + wl_resource_set_user_data (callback->resource, callback); + wl_resource_set_destructor (callback->resource, destroy_frame_callback); + + 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) +{ + g_warning ("TODO: support set_opaque_region request"); +} + +static void +meta_wayland_surface_set_input_region (struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *region) +{ + g_warning ("TODO: support set_input_region request"); +} + +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 = wl_resource_get_user_data (resource); + MetaWaylandCompositor *compositor = surface->compositor; + + /* wl_surface.attach */ + if (surface->pending.newly_attached && + surface->buffer_ref.buffer != 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. */ + meta_wayland_buffer_reference (&surface->buffer_ref, + surface->pending.buffer); + + if (surface->pending.buffer) + { + MetaWaylandBuffer *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; + + meta_window_get_input_rect (surface->window, &rect); + + if (window_actor) + meta_window_actor_attach_wayland_buffer (window_actor, buffer); + + /* 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); + } + } + } + + 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) +{ + g_warning ("TODO: support set_buffer_transform request"); +} + +static void +meta_wayland_surface_set_buffer_scale (struct wl_client *client, + struct wl_resource *resource, + int scale) +{ + g_warning ("TODO: support set_buffer_scale request"); +} + +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, + meta_wayland_surface_set_buffer_scale +}; + +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_buffer_reference (&surface->buffer_ref, NULL); + + 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 = wl_resource_get_user_data (resource); + 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 = + wl_resource_get_user_data (wayland_compositor_resource); + MetaWaylandSurface *surface = g_slice_new0 (MetaWaylandSurface); + + surface->compositor = compositor; + + /* a surface inherits the version from the compositor */ + surface->resource = wl_resource_create (wayland_client, + &wl_surface_interface, + wl_resource_get_version (wayland_compositor_resource), + id); + wl_resource_set_implementation (surface->resource, &meta_wayland_surface_interface, surface, + meta_wayland_surface_resource_destroy_cb); + + surface->pending.damage = cairo_region_create (); + + surface->pending.buffer_destroy_listener.notify = + surface_handle_pending_buffer_destroy; + wl_list_init (&surface->pending.frame_callback_list); + + 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 = wl_resource_get_user_data (resource); + 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 = wl_resource_get_user_data (resource); + 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 = wl_resource_get_user_data (resource); + + 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 = wl_resource_create (wayland_client, + &wl_region_interface, 1, + id); + wl_resource_set_implementation (region->resource, + &meta_wayland_region_interface, region, + meta_wayland_region_resource_destroy_cb); + + region->region = cairo_region_create (); +} + +static void +bind_output (struct wl_client *client, + void *data, + guint32 version, + guint32 id) +{ + MetaWaylandOutput *output = data; + struct wl_resource *resource = + wl_resource_create (client, &wl_output_interface, version, id); + 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_global_create (compositor->wayland_display, + &wl_output_interface, 2, + 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; + struct wl_resource *resource; + + resource = wl_resource_create (client, &wl_compositor_interface, version, id); + wl_resource_set_implementation (resource, &meta_wayland_compositor_interface, compositor, NULL); +} + +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) +{ + g_warning ("TODO: support shell_surface_resize request"); +} + +static void +ensure_surface_window (MetaWaylandSurface *surface) +{ + MetaDisplay *display = meta_get_display (); + + if (!surface->window) + { + int width, height; + + if (surface->buffer_ref.buffer) + { + MetaWaylandBuffer *buffer = surface->buffer_ref.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 = wl_resource_get_user_data (resource); + 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 = wl_resource_get_user_data (resource); + 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 = wl_resource_get_user_data (resource); + 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) +{ + g_warning ("TODO: support shell_surface_set_maximized request"); +} + +static void +shell_surface_set_title (struct wl_client *client, + struct wl_resource *resource, + const char *title) +{ + g_warning ("TODO: support shell_surface_set_title request"); +} + +static void +shell_surface_set_class (struct wl_client *client, + struct wl_resource *resource, + const char *class_) +{ + g_warning ("TODO: support shell_surface_set_class request"); +} + +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 = wl_resource_get_user_data (resource); + + /* 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 = wl_resource_get_user_data (surface_resource); + 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); + + /* a shell surface inherits the version from the shell */ + shell_surface->resource = + wl_resource_create (client, &wl_shell_surface_interface, + wl_resource_get_version (resource), id); + wl_resource_set_implementation (shell_surface->resource, &meta_wayland_shell_surface_interface, + shell_surface, destroy_shell_surface); + + shell_surface->surface = surface; + shell_surface->surface_destroy_listener.notify = shell_handle_surface_destroy; + wl_resource_add_destroy_listener (surface->resource, + &shell_surface->surface_destroy_listener); + surface->has_shell_surface = TRUE; +} + +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) +{ + struct wl_resource *resource; + + resource = wl_resource_create (client, &wl_shell_interface, version, id); + wl_resource_set_implementation (resource, &meta_wayland_shell_interface, data, NULL); +} + +static void +xserver_set_window_id (struct wl_client *client, + struct wl_resource *compositor_resource, + struct wl_resource *surface_resource, + guint32 xid) +{ + MetaWaylandCompositor *compositor = + wl_resource_get_user_data (compositor_resource); + MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource); + 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; + window->surface = surface; + + /* 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_resource_create (client, &xserver_interface, version, id); + wl_resource_set_implementation (compositor->xserver_resource, + &xserver_implementation, compositor, NULL); + + 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); + + /* 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_global_create (compositor->wayland_display, + &wl_compositor_interface, 3, + 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_global_create (compositor->wayland_display, + &wl_shell_interface, 1, + 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_global_create (compositor->wayland_display, + &xserver_interface, 1, + 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 (!meta_xwayland_start (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) +{ + meta_xwayland_stop (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"); + } + } +} diff --git a/src/wayland/meta-xwayland-private.h b/src/wayland/meta-xwayland-private.h new file mode 100644 index 000000000..0d6716b51 --- /dev/null +++ b/src/wayland/meta-xwayland-private.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2013 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_XWAYLAND_PRIVATE_H +#define META_XWAYLAND_PRIVATE_H + +#include "meta-wayland-private.h" + +#include + +gboolean +meta_xwayland_start (MetaWaylandCompositor *compositor); + +void +meta_xwayland_stop (MetaWaylandCompositor *compositor); + +#endif /* META_XWAYLAND_PRIVATE_H */ diff --git a/src/wayland/meta-xwayland.c b/src/wayland/meta-xwayland.c new file mode 100644 index 000000000..7ff30bf92 --- /dev/null +++ b/src/wayland/meta-xwayland.c @@ -0,0 +1,310 @@ +/* + * X Wayland Support + * + * Copyright (C) 2013 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 "meta-xwayland-private.h" + +#include + +#include +#include +#include +#include +#include + +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; +} + +gboolean +meta_xwayland_start (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) + { + unlink (lockfile); + + if (errno == EADDRINUSE) + { + display++; + continue; + } + else + return FALSE; + } + + compositor->xwayland_unix_fd = bind_to_unix_socket (display); + if (compositor->xwayland_abstract_fd < 0) + { + unlink (lockfile); + close (compositor->xwayland_abstract_fd); + 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; +} + +void +meta_xwayland_stop (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); +}