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 <robert@linux.intel.com>
Authored-by: Neil Roberts <neil@linux.intel.com>
Authored-by: Rico Tzschichholz.
Authored-by: Giovanni Campagna <gcampagna@src.gnome.org>
This commit is contained in:
Robert Bragg 2012-01-07 22:21:32 +00:00 committed by Jasper St. Pierre
parent bd3c357212
commit f9a11b3b18
20 changed files with 3156 additions and 677 deletions

View File

@ -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])

View File

@ -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)

View File

@ -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 <X11/extensions/shape.h>
#include <X11/extensions/Xcomposite.h>
@ -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,27 +330,35 @@ 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);
}
else
{
/* Reset info->pending_input_region if one existed before and set the new
* one to use it later. */
if (info->pending_input_region)
if (info->stage && info->output)
{
XFixesDestroyRegion (xdpy, info->pending_input_region);
info->pending_input_region = None;
do_set_stage_input_region (screen, region);
}
if (region != None)
else
{
info->pending_input_region = XFixesCreateRegion (xdpy, NULL, 0);
XFixesCopyRegion (xdpy, info->pending_input_region, region);
/* Reset info->pending_input_region if one existed before and set the new
* one to use it later. */
if (info->pending_input_region)
{
XFixesDestroyRegion (xdpy, info->pending_input_region);
info->pending_input_region = None;
}
if (region != None)
{
info->pending_input_region = XFixesCreateRegion (xdpy, NULL, 0);
XFixesCopyRegion (xdpy, info->pending_input_region, region);
}
}
}
}
@ -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)

View File

@ -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;
}

View File

@ -30,8 +30,14 @@
#include <config.h>
#include <meta/meta-shaped-texture.h>
#include <meta/util.h>
#include "meta-texture-tower.h"
#ifdef HAVE_WAYLAND
#include "meta-wayland-private.h"
#include <cogl/cogl-wayland-server.h>
#endif
#include <clutter/clutter.h>
#include <cogl/cogl.h>
#include <cogl/cogl-texture-pixmap-x11.h>
@ -55,6 +61,15 @@ static void meta_shaped_texture_get_preferred_height (ClutterActor *self,
static gboolean meta_shaped_texture_get_paint_volume (ClutterActor *self, ClutterPaintVolume *volume);
typedef enum _MetaShapedTextureType
{
META_SHAPED_TEXTURE_TYPE_X11_PIXMAP,
#ifdef HAVE_WAYLAND
META_SHAPED_TEXTURE_TYPE_WAYLAND_SURFACE,
#endif
} MetaShapedTextureType;
G_DEFINE_TYPE (MetaShapedTexture, meta_shaped_texture,
CLUTTER_TYPE_ACTOR);
@ -65,8 +80,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

View File

@ -5,6 +5,11 @@
#include <config.h>
#ifdef HAVE_WAYLAND
#include <wayland-server.h>
#include <meta-wayland-private.h>
#endif
#include <X11/extensions/Xdamage.h>
#include <meta/compositor-mutter.h>
@ -24,8 +29,20 @@ void meta_window_actor_unmaximize (MetaWindowActor *self,
MetaRectangle *old_rect,
MetaRectangle *new_rect);
void meta_window_actor_process_damage (MetaWindowActor *self,
XDamageNotifyEvent *event);
void meta_window_actor_process_x11_damage (MetaWindowActor *self,
XDamageNotifyEvent *event);
#ifdef HAVE_WAYLAND
void meta_window_actor_process_wayland_damage (MetaWindowActor *self,
int x,
int y,
int width,
int height);
void meta_window_actor_set_wayland_surface (MetaWindowActor *self,
MetaWaylandSurface *surface);
void meta_window_actor_attach_wayland_buffer (MetaWindowActor *self,
MetaWaylandBuffer *buffer);
#endif
void meta_window_actor_pre_paint (MetaWindowActor *self);
void meta_window_actor_post_paint (MetaWindowActor *self);

View File

@ -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);
}
}
}

View File

@ -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)

View File

@ -932,7 +932,23 @@ meta_display_open (void)
{
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

View File

@ -55,6 +55,9 @@
#include "session.h"
#include <meta/prefs.h>
#include <meta/compositor.h>
#ifdef HAVE_WAYLAND
#include "meta-wayland-private.h"
#endif
#include <glib-object.h>
#include <gdk/gdkx.h>
@ -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 ();
}
}
/**

View File

@ -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

View File

@ -45,6 +45,9 @@
#include <meta/compositor.h>
#include "mutter-enum-types.h"
#include "core.h"
#ifdef HAVE_WAYLAND
#include "meta-wayland-private.h"
#endif
#include <X11/extensions/Xinerama.h>
@ -603,8 +606,8 @@ reload_monitor_infos (MetaScreen *screen)
* should effectively be forwarded to events on the background actor,
* providing that the scene graph is set up correctly.
*/
static Window
create_guard_window (Display *xdisplay, MetaScreen *screen)
Window
meta_screen_create_guard_window (Display *xdisplay, MetaScreen *screen)
{
XSetWindowAttributes attributes;
Window guard_window;
@ -668,6 +671,9 @@ meta_screen_new (MetaDisplay *display,
char buf[128];
guint32 manager_timestamp;
gulong current_workspace;
#ifdef HAVE_WAYLAND
MetaWaylandCompositor *compositor;
#endif
replace_current_wm = meta_get_replace_current_wm ();
@ -826,8 +832,21 @@ meta_screen_new (MetaDisplay *display,
screen->xscreen = ScreenOfDisplay (xdisplay, number);
screen->xroot = xroot;
screen->rect.x = screen->rect.y = 0;
screen->rect.width = WidthOfScreen (screen->xscreen);
screen->rect.height = HeightOfScreen (screen->xscreen);
#ifdef HAVE_WAYLAND
if (meta_is_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);

View File

@ -44,6 +44,17 @@
#include <X11/Xutil.h>
#include <cairo.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#ifdef HAVE_WAYLAND
#include "meta-wayland-private.h"
#endif
/* XXX: We should find a nicer approach to deal with the
* circular dependency we have with the current headers
* (meta-wayland-private.h which typedefs MetaWaylandSurface
* also includes window-private.h) */
#ifndef HAVE_META_WAYLAND_SURFACE_TYPE
typedef struct _MetaWaylandSurface MetaWaylandSurface;
#endif
typedef struct _MetaWindowQueue MetaWindowQueue;
@ -69,6 +80,11 @@ typedef enum {
_NET_WM_BYPASS_COMPOSITOR_HINT_OFF = 2,
} MetaBypassCompositorHintValue;
typedef enum {
META_WINDOW_CLIENT_TYPE_WAYLAND,
META_WINDOW_CLIENT_TYPE_X11
} MetaWindowClientType;
struct _MetaWindow
{
GObject parent_instance;
@ -77,6 +93,10 @@ struct _MetaWindow
MetaScreen *screen;
const MetaMonitorInfo *monitor;
MetaWorkspace *workspace;
MetaWindowClientType client_type;
#ifdef HAVE_WAYLAND
MetaWaylandSurface *surface;
#endif
Window xwindow;
/* may be NULL! not all windows get decorated */
MetaFrame *frame;
@ -489,6 +509,10 @@ MetaWindow* meta_window_new_with_attrs (MetaDisplay *display,
gboolean must_be_viewable,
MetaCompEffect effect,
XWindowAttributes *attrs);
MetaWindow *meta_window_new_for_wayland (MetaDisplay *display,
int width,
int height,
MetaWaylandSurface *surface);
void meta_window_unmanage (MetaWindow *window,
guint32 timestamp);
void meta_window_calc_showing (MetaWindow *window);

View File

@ -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

View File

@ -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,

View File

@ -29,6 +29,11 @@
#include <clutter/clutter.h>
#include <X11/Xlib.h>
#ifdef HAVE_WAYLAND
#include <wayland-server.h>
#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);

View File

@ -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 <wayland-server.h>
#include <clutter/clutter.h>
#include <glib.h>
#include <cairo.h>
#include "window-private.h"
typedef struct _MetaWaylandCompositor MetaWaylandCompositor;
typedef struct
{
struct wl_resource *resource;
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 */

1145
src/wayland/meta-wayland.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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 <glib.h>
gboolean
meta_xwayland_start (MetaWaylandCompositor *compositor);
void
meta_xwayland_stop (MetaWaylandCompositor *compositor);
#endif /* META_XWAYLAND_PRIVATE_H */

310
src/wayland/meta-xwayland.c Normal file
View File

@ -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 <glib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/un.h>
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);
}