mutter/src/compositor/meta-compositor-x11.c
Jonas Ådahl 25f9406e69 compositor: Get the stage via the backend
We would get the MetaDisplay from the backend singleton before creating
the MetaCompositor, then in MetaCompositor, get the backend singleton
again to get the stage. To get rid of the extra singleton fetching, just
pass the backend the MetaCompositor constructors, and fetch the stage
directly from the backend everytime it's needed.

This also makes it available earlier than before, as we didn't set our
instance private stage pointer until the manage() call.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1289
2020-06-05 21:39:27 +00:00

416 lines
13 KiB
C

/*
* Copyright (C) 2019 Red Hat Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
*/
#include "config.h"
#include "compositor/meta-compositor-x11.h"
#include <X11/extensions/shape.h>
#include <X11/extensions/Xcomposite.h>
#include "backends/x11/meta-backend-x11.h"
#include "backends/x11/meta-event-x11.h"
#include "clutter/x11/clutter-x11.h"
#include "compositor/meta-sync-ring.h"
#include "compositor/meta-window-actor-x11.h"
#include "core/display-private.h"
#include "x11/meta-x11-display-private.h"
struct _MetaCompositorX11
{
MetaCompositor parent;
Window output;
gboolean frame_has_updated_xsurfaces;
gboolean have_x11_sync_object;
MetaWindow *unredirected_window;
};
G_DEFINE_TYPE (MetaCompositorX11, meta_compositor_x11, META_TYPE_COMPOSITOR)
static void
process_damage (MetaCompositorX11 *compositor_x11,
XDamageNotifyEvent *damage_xevent,
MetaWindow *window)
{
MetaWindowActor *window_actor = meta_window_actor_from_window (window);
MetaWindowActorX11 *window_actor_x11 = META_WINDOW_ACTOR_X11 (window_actor);
meta_window_actor_x11_process_damage (window_actor_x11, damage_xevent);
compositor_x11->frame_has_updated_xsurfaces = TRUE;
}
void
meta_compositor_x11_process_xevent (MetaCompositorX11 *compositor_x11,
XEvent *xevent,
MetaWindow *window)
{
MetaCompositor *compositor = META_COMPOSITOR (compositor_x11);
MetaDisplay *display = meta_compositor_get_display (compositor);
MetaX11Display *x11_display = display->x11_display;
int damage_event_base;
damage_event_base = meta_x11_display_get_damage_event_base (x11_display);
if (xevent->type == damage_event_base + XDamageNotify)
{
/*
* Core code doesn't handle damage events, so we need to extract the
* MetaWindow ourselves.
*/
if (!window)
{
Window xwindow;
xwindow = ((XDamageNotifyEvent *) xevent)->drawable;
window = meta_x11_display_lookup_x_window (x11_display, xwindow);
}
if (window)
process_damage (compositor_x11, (XDamageNotifyEvent *) xevent, window);
}
if (compositor_x11->have_x11_sync_object)
meta_sync_ring_handle_event (xevent);
/*
* Clutter needs to know about MapNotify events otherwise it will think the
* stage is invisible
*/
if (xevent->type == MapNotify)
meta_x11_handle_event (xevent);
}
static gboolean
meta_compositor_x11_manage (MetaCompositor *compositor,
GError **error)
{
MetaCompositorX11 *compositor_x11 = META_COMPOSITOR_X11 (compositor);
MetaDisplay *display = meta_compositor_get_display (compositor);
MetaX11Display *x11_display = display->x11_display;
Display *xdisplay = meta_x11_display_get_xdisplay (x11_display);
int composite_version;
MetaBackend *backend = meta_get_backend ();
Window xwindow;
if (!META_X11_DISPLAY_HAS_COMPOSITE (x11_display) ||
!META_X11_DISPLAY_HAS_DAMAGE (x11_display))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Missing required extension %s",
!META_X11_DISPLAY_HAS_COMPOSITE (x11_display) ?
"composite" : "damage");
return FALSE;
}
composite_version = ((x11_display->composite_major_version * 10) +
x11_display->composite_minor_version);
if (composite_version < 3)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"COMPOSITE extension 3.0 required (found %d.%d)",
x11_display->composite_major_version,
x11_display->composite_minor_version);
return FALSE;
}
meta_x11_display_set_cm_selection (display->x11_display);
compositor_x11->output = display->x11_display->composite_overlay_window;
xwindow = meta_backend_x11_get_xwindow (META_BACKEND_X11 (backend));
XReparentWindow (xdisplay, xwindow, compositor_x11->output, 0, 0);
meta_x11_display_clear_stage_input_region (display->x11_display);
/*
* 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, compositor_x11->output,
ShapeBounding, 0, 0, None);
/*
* Map overlay window before redirecting windows offscreen so we catch their
* contents until we show the stage.
*/
XMapWindow (xdisplay, compositor_x11->output);
compositor_x11->have_x11_sync_object = meta_sync_ring_init (xdisplay);
meta_compositor_redirect_x11_windows (META_COMPOSITOR (compositor));
return TRUE;
}
static void
meta_compositor_x11_unmanage (MetaCompositor *compositor)
{
MetaDisplay *display = meta_compositor_get_display (compositor);
MetaX11Display *x11_display = display->x11_display;
Display *xdisplay = x11_display->xdisplay;
Window xroot = x11_display->xroot;
/*
* 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);
}
/*
* Sets an bounding shape on the COW so that the given window
* is exposed. If window is %NULL it clears the shape again.
*
* Used so we can unredirect windows, by shaping away the part
* of the COW, letting the raw window be seen through below.
*/
static void
shape_cow_for_window (MetaCompositorX11 *compositor_x11,
MetaWindow *window)
{
MetaCompositor *compositor = META_COMPOSITOR (compositor_x11);
MetaDisplay *display = meta_compositor_get_display (compositor);
Display *xdisplay = meta_x11_display_get_xdisplay (display->x11_display);
if (!window)
{
XFixesSetWindowShapeRegion (xdisplay, compositor_x11->output,
ShapeBounding, 0, 0, None);
}
else
{
XserverRegion output_region;
XRectangle screen_rect, window_bounds;
int width, height;
MetaRectangle rect;
meta_window_get_frame_rect (window, &rect);
window_bounds.x = rect.x;
window_bounds.y = rect.y;
window_bounds.width = rect.width;
window_bounds.height = rect.height;
meta_display_get_size (display, &width, &height);
screen_rect.x = 0;
screen_rect.y = 0;
screen_rect.width = width;
screen_rect.height = height;
output_region = XFixesCreateRegion (xdisplay, &window_bounds, 1);
XFixesInvertRegion (xdisplay, output_region, &screen_rect, output_region);
XFixesSetWindowShapeRegion (xdisplay, compositor_x11->output,
ShapeBounding, 0, 0, output_region);
XFixesDestroyRegion (xdisplay, output_region);
}
}
static void
set_unredirected_window (MetaCompositorX11 *compositor_x11,
MetaWindow *window)
{
MetaWindow *prev_unredirected_window = compositor_x11->unredirected_window;
if (prev_unredirected_window == window)
return;
if (prev_unredirected_window)
{
MetaWindowActor *window_actor;
MetaWindowActorX11 *window_actor_x11;
window_actor = meta_window_actor_from_window (prev_unredirected_window);
window_actor_x11 = META_WINDOW_ACTOR_X11 (window_actor);
meta_window_actor_x11_set_unredirected (window_actor_x11, FALSE);
}
shape_cow_for_window (compositor_x11, window);
compositor_x11->unredirected_window = window;
if (window)
{
MetaWindowActor *window_actor;
MetaWindowActorX11 *window_actor_x11;
window_actor = meta_window_actor_from_window (window);
window_actor_x11 = META_WINDOW_ACTOR_X11 (window_actor);
meta_window_actor_x11_set_unredirected (window_actor_x11, TRUE);
}
}
static void
maybe_unredirect_top_window (MetaCompositorX11 *compositor_x11)
{
MetaCompositor *compositor = META_COMPOSITOR (compositor_x11);
MetaWindow *window_to_unredirect = NULL;
MetaWindowActor *window_actor;
MetaWindowActorX11 *window_actor_x11;
if (meta_compositor_is_unredirect_inhibited (compositor))
goto out;
window_actor = meta_compositor_get_top_window_actor (compositor);
if (!window_actor)
goto out;
window_actor_x11 = META_WINDOW_ACTOR_X11 (window_actor);
if (!meta_window_actor_x11_should_unredirect (window_actor_x11))
goto out;
window_to_unredirect = meta_window_actor_get_meta_window (window_actor);
out:
set_unredirected_window (compositor_x11, window_to_unredirect);
}
static void
meta_compositor_x11_pre_paint (MetaCompositor *compositor)
{
MetaCompositorX11 *compositor_x11 = META_COMPOSITOR_X11 (compositor);
MetaCompositorClass *parent_class;
maybe_unredirect_top_window (compositor_x11);
parent_class = META_COMPOSITOR_CLASS (meta_compositor_x11_parent_class);
parent_class->pre_paint (compositor);
if (compositor_x11->frame_has_updated_xsurfaces)
{
MetaDisplay *display = meta_compositor_get_display (compositor);
/*
* We need to make sure that any X drawing that happens before the
* XDamageSubtract() for each window above is visible to subsequent GL
* rendering; the standardized way to do this is GL_EXT_X11_sync_object.
* Since this isn't implemented yet in mesa, we also have a path that
* relies on the implementation of the open source drivers.
*
* Anything else, we just hope for the best.
*
* 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.
*/
if (compositor_x11->have_x11_sync_object)
compositor_x11->have_x11_sync_object = meta_sync_ring_insert_wait ();
else
XSync (display->x11_display->xdisplay, False);
}
}
static void
meta_compositor_x11_post_paint (MetaCompositor *compositor)
{
MetaCompositorX11 *compositor_x11 = META_COMPOSITOR_X11 (compositor);
MetaCompositorClass *parent_class;
if (compositor_x11->frame_has_updated_xsurfaces)
{
if (compositor_x11->have_x11_sync_object)
compositor_x11->have_x11_sync_object = meta_sync_ring_after_frame ();
compositor_x11->frame_has_updated_xsurfaces = FALSE;
}
parent_class = META_COMPOSITOR_CLASS (meta_compositor_x11_parent_class);
parent_class->post_paint (compositor);
}
static void
meta_compositor_x11_remove_window (MetaCompositor *compositor,
MetaWindow *window)
{
MetaCompositorX11 *compositor_x11 = META_COMPOSITOR_X11 (compositor);
MetaCompositorClass *parent_class;
if (compositor_x11->unredirected_window == window)
set_unredirected_window (compositor_x11, NULL);
parent_class = META_COMPOSITOR_CLASS (meta_compositor_x11_parent_class);
parent_class->remove_window (compositor, window);
}
Window
meta_compositor_x11_get_output_xwindow (MetaCompositorX11 *compositor_x11)
{
return compositor_x11->output;
}
MetaCompositorX11 *
meta_compositor_x11_new (MetaDisplay *display,
MetaBackend *backend)
{
return g_object_new (META_TYPE_COMPOSITOR_X11,
"display", display,
"backend", backend,
NULL);
}
static void
meta_compositor_x11_dispose (GObject *object)
{
MetaCompositorX11 *compositor_x11 = META_COMPOSITOR_X11 (object);
if (compositor_x11->have_x11_sync_object)
{
meta_sync_ring_destroy ();
compositor_x11->have_x11_sync_object = FALSE;
}
G_OBJECT_CLASS (meta_compositor_x11_parent_class)->dispose (object);
}
static void
meta_compositor_x11_init (MetaCompositorX11 *compositor_x11)
{
}
static void
meta_compositor_x11_class_init (MetaCompositorX11Class *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
MetaCompositorClass *compositor_class = META_COMPOSITOR_CLASS (klass);
object_class->dispose = meta_compositor_x11_dispose;
compositor_class->manage = meta_compositor_x11_manage;
compositor_class->unmanage = meta_compositor_x11_unmanage;
compositor_class->pre_paint = meta_compositor_x11_pre_paint;
compositor_class->post_paint = meta_compositor_x11_post_paint;
compositor_class->remove_window = meta_compositor_x11_remove_window;
}