mutter/src/compositor/meta-surface-actor-x11.c
Niels De Graef d539fe28d5 Use G_DECLARE_DERIVABLE/FINAL_TYPE on some types
This is only for types in the `Meta` namespace.

* Clears up a lot of boilerplate
* We get `g_autoptr` support for free
2018-12-21 19:48:50 +00:00

466 lines
13 KiB
C

/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* Copyright (C) 2013 Red Hat
*
* 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.
*
* Written by:
* Owen Taylor <otaylor@redhat.com>
* Jasper St. Pierre <jstpierre@mecheye.net>
*/
#include "config.h"
#include "compositor/meta-surface-actor-x11.h"
#include <X11/extensions/Xcomposite.h>
#include "cogl/winsys/cogl-texture-pixmap-x11.h"
#include "compositor/meta-cullable.h"
#include "compositor/meta-shaped-texture-private.h"
#include "core/window-private.h"
#include "meta/meta-x11-errors.h"
#include "x11/meta-x11-display-private.h"
#include "x11/window-x11.h"
struct _MetaSurfaceActorX11
{
MetaSurfaceActor parent;
MetaWindow *window;
MetaDisplay *display;
CoglTexture *texture;
Pixmap pixmap;
Damage damage;
int last_width;
int last_height;
/* This is used to detect fullscreen windows that need to be unredirected */
guint full_damage_frames_count;
guint does_full_damage : 1;
/* Other state... */
guint received_damage : 1;
guint size_changed : 1;
guint unredirected : 1;
};
G_DEFINE_TYPE (MetaSurfaceActorX11,
meta_surface_actor_x11,
META_TYPE_SURFACE_ACTOR)
static void
free_damage (MetaSurfaceActorX11 *self)
{
MetaDisplay *display = self->display;
Display *xdisplay = meta_x11_display_get_xdisplay (display->x11_display);
if (self->damage == None)
return;
meta_x11_error_trap_push (display->x11_display);
XDamageDestroy (xdisplay, self->damage);
self->damage = None;
meta_x11_error_trap_pop (display->x11_display);
}
static void
detach_pixmap (MetaSurfaceActorX11 *self)
{
MetaDisplay *display = self->display;
Display *xdisplay = meta_x11_display_get_xdisplay (display->x11_display);
MetaShapedTexture *stex = meta_surface_actor_get_texture (META_SURFACE_ACTOR (self));
if (self->pixmap == None)
return;
/* Get rid of all references to the pixmap before freeing it; it's unclear whether
* you are supposed to be able to free a GLXPixmap after freeing the underlying
* pixmap, but it certainly doesn't work with current DRI/Mesa
*/
meta_shaped_texture_set_texture (stex, NULL);
cogl_flush ();
meta_x11_error_trap_push (display->x11_display);
XFreePixmap (xdisplay, self->pixmap);
self->pixmap = None;
meta_x11_error_trap_pop (display->x11_display);
g_clear_pointer (&self->texture, cogl_object_unref);
}
static void
set_pixmap (MetaSurfaceActorX11 *self,
Pixmap pixmap)
{
CoglContext *ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ());
MetaShapedTexture *stex = meta_surface_actor_get_texture (META_SURFACE_ACTOR (self));
CoglError *error = NULL;
CoglTexture *texture;
g_assert (self->pixmap == None);
self->pixmap = pixmap;
texture = COGL_TEXTURE (cogl_texture_pixmap_x11_new (ctx, self->pixmap, FALSE, &error));
if (error != NULL)
{
g_warning ("Failed to allocate stex texture: %s", error->message);
cogl_error_free (error);
}
else if (G_UNLIKELY (!cogl_texture_pixmap_x11_is_using_tfp_extension (COGL_TEXTURE_PIXMAP_X11 (texture))))
g_warning ("NOTE: Not using GLX TFP!\n");
self->texture = texture;
meta_shaped_texture_set_texture (stex, texture);
}
static void
update_pixmap (MetaSurfaceActorX11 *self)
{
MetaDisplay *display = self->display;
Display *xdisplay = meta_x11_display_get_xdisplay (display->x11_display);
if (self->size_changed)
{
detach_pixmap (self);
self->size_changed = FALSE;
}
if (self->pixmap == None)
{
Pixmap new_pixmap;
Window xwindow = meta_window_x11_get_toplevel_xwindow (self->window);
meta_x11_error_trap_push (display->x11_display);
new_pixmap = XCompositeNameWindowPixmap (xdisplay, xwindow);
if (meta_x11_error_trap_pop_with_return (display->x11_display) != Success)
{
/* Probably a BadMatch if the window isn't viewable; we could
* GrabServer/GetWindowAttributes/NameWindowPixmap/UngrabServer/Sync
* to avoid this, but there's no reason to take two round trips
* when one will do. (We need that Sync if we want to handle failures
* for any reason other than !viewable. That's unlikely, but maybe
* we'll BadAlloc or something.)
*/
new_pixmap = None;
}
if (new_pixmap == None)
{
meta_verbose ("Unable to get named pixmap for %s\n",
meta_window_get_description (self->window));
return;
}
set_pixmap (self, new_pixmap);
}
}
static gboolean
is_visible (MetaSurfaceActorX11 *self)
{
return (self->pixmap != None) && !self->unredirected;
}
static void
meta_surface_actor_x11_process_damage (MetaSurfaceActor *actor,
int x, int y, int width, int height)
{
MetaSurfaceActorX11 *self = META_SURFACE_ACTOR_X11 (actor);
self->received_damage = TRUE;
if (meta_window_is_fullscreen (self->window) && !self->unredirected && !self->does_full_damage)
{
MetaRectangle window_rect;
meta_window_get_frame_rect (self->window, &window_rect);
if (x == 0 &&
y == 0 &&
window_rect.width == width &&
window_rect.height == height)
self->full_damage_frames_count++;
else
self->full_damage_frames_count = 0;
if (self->full_damage_frames_count >= 100)
self->does_full_damage = TRUE;
}
if (!is_visible (self))
return;
cogl_texture_pixmap_x11_update_area (COGL_TEXTURE_PIXMAP_X11 (self->texture),
x, y, width, height);
}
static void
meta_surface_actor_x11_pre_paint (MetaSurfaceActor *actor)
{
MetaSurfaceActorX11 *self = META_SURFACE_ACTOR_X11 (actor);
MetaDisplay *display = self->display;
Display *xdisplay = meta_x11_display_get_xdisplay (display->x11_display);
if (self->received_damage)
{
meta_x11_error_trap_push (display->x11_display);
XDamageSubtract (xdisplay, self->damage, None, None);
meta_x11_error_trap_pop (display->x11_display);
self->received_damage = FALSE;
}
update_pixmap (self);
}
static gboolean
meta_surface_actor_x11_is_visible (MetaSurfaceActor *actor)
{
MetaSurfaceActorX11 *self = META_SURFACE_ACTOR_X11 (actor);
return is_visible (self);
}
static gboolean
meta_surface_actor_x11_is_opaque (MetaSurfaceActor *actor)
{
MetaSurfaceActorX11 *self = META_SURFACE_ACTOR_X11 (actor);
/* If we're not ARGB32, then we're opaque. */
if (!meta_surface_actor_is_argb32 (actor))
return TRUE;
cairo_region_t *opaque_region = meta_surface_actor_get_opaque_region (actor);
/* If we have no opaque region, then no pixels are opaque. */
if (!opaque_region)
return FALSE;
MetaWindow *window = self->window;
cairo_rectangle_int_t client_area;
meta_window_get_client_area_rect (window, &client_area);
/* Otherwise, check if our opaque region covers our entire surface. */
if (cairo_region_contains_rectangle (opaque_region, &client_area) == CAIRO_REGION_OVERLAP_IN)
return TRUE;
return FALSE;
}
static gboolean
meta_surface_actor_x11_should_unredirect (MetaSurfaceActor *actor)
{
MetaSurfaceActorX11 *self = META_SURFACE_ACTOR_X11 (actor);
MetaWindow *window = self->window;
if (meta_window_requested_dont_bypass_compositor (window))
return FALSE;
if (window->opacity != 0xFF)
return FALSE;
if (window->shape_region != NULL)
return FALSE;
if (!meta_window_is_monitor_sized (window))
return FALSE;
if (meta_window_requested_bypass_compositor (window))
return TRUE;
if (!meta_surface_actor_x11_is_opaque (actor))
return FALSE;
if (meta_window_is_override_redirect (window))
return TRUE;
if (self->does_full_damage)
return TRUE;
return FALSE;
}
static void
sync_unredirected (MetaSurfaceActorX11 *self)
{
MetaDisplay *display = self->display;
Display *xdisplay = meta_x11_display_get_xdisplay (display->x11_display);
Window xwindow = meta_window_x11_get_toplevel_xwindow (self->window);
meta_x11_error_trap_push (display->x11_display);
if (self->unredirected)
{
detach_pixmap (self);
XCompositeUnredirectWindow (xdisplay, xwindow, CompositeRedirectManual);
}
else
{
XCompositeRedirectWindow (xdisplay, xwindow, CompositeRedirectManual);
}
meta_x11_error_trap_pop (display->x11_display);
}
static void
meta_surface_actor_x11_set_unredirected (MetaSurfaceActor *actor,
gboolean unredirected)
{
MetaSurfaceActorX11 *self = META_SURFACE_ACTOR_X11 (actor);
if (self->unredirected == unredirected)
return;
self->unredirected = unredirected;
sync_unredirected (self);
}
static gboolean
meta_surface_actor_x11_is_unredirected (MetaSurfaceActor *actor)
{
MetaSurfaceActorX11 *self = META_SURFACE_ACTOR_X11 (actor);
return self->unredirected;
}
static void
meta_surface_actor_x11_dispose (GObject *object)
{
MetaSurfaceActorX11 *self = META_SURFACE_ACTOR_X11 (object);
detach_pixmap (self);
free_damage (self);
G_OBJECT_CLASS (meta_surface_actor_x11_parent_class)->dispose (object);
}
static MetaWindow *
meta_surface_actor_x11_get_window (MetaSurfaceActor *actor)
{
MetaSurfaceActorX11 *self = META_SURFACE_ACTOR_X11 (actor);
return self->window;
}
static void
meta_surface_actor_x11_class_init (MetaSurfaceActorX11Class *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
MetaSurfaceActorClass *surface_actor_class = META_SURFACE_ACTOR_CLASS (klass);
object_class->dispose = meta_surface_actor_x11_dispose;
surface_actor_class->process_damage = meta_surface_actor_x11_process_damage;
surface_actor_class->pre_paint = meta_surface_actor_x11_pre_paint;
surface_actor_class->is_visible = meta_surface_actor_x11_is_visible;
surface_actor_class->should_unredirect = meta_surface_actor_x11_should_unredirect;
surface_actor_class->set_unredirected = meta_surface_actor_x11_set_unredirected;
surface_actor_class->is_unredirected = meta_surface_actor_x11_is_unredirected;
surface_actor_class->get_window = meta_surface_actor_x11_get_window;
}
static void
meta_surface_actor_x11_init (MetaSurfaceActorX11 *self)
{
self->last_width = -1;
self->last_height = -1;
}
static void
create_damage (MetaSurfaceActorX11 *self)
{
Display *xdisplay = meta_x11_display_get_xdisplay (self->display->x11_display);
Window xwindow = meta_window_x11_get_toplevel_xwindow (self->window);
self->damage = XDamageCreate (xdisplay, xwindow, XDamageReportBoundingBox);
}
static void
window_decorated_notify (MetaWindow *window,
GParamSpec *pspec,
gpointer user_data)
{
MetaSurfaceActorX11 *self = META_SURFACE_ACTOR_X11 (user_data);
detach_pixmap (self);
free_damage (self);
create_damage (self);
}
static void
reset_texture (MetaSurfaceActorX11 *self)
{
MetaShapedTexture *stex = meta_surface_actor_get_texture (META_SURFACE_ACTOR (self));
if (!self->texture)
return;
/* Setting the texture to NULL will cause all the FBO's cached by the
* shaped texture's MetaTextureTower to be discarded and recreated.
*/
meta_shaped_texture_set_texture (stex, NULL);
meta_shaped_texture_set_texture (stex, self->texture);
}
MetaSurfaceActor *
meta_surface_actor_x11_new (MetaWindow *window)
{
MetaSurfaceActorX11 *self = g_object_new (META_TYPE_SURFACE_ACTOR_X11, NULL);
MetaDisplay *display = meta_window_get_display (window);
g_assert (!meta_is_wayland_compositor ());
self->window = window;
self->display = display;
g_signal_connect_object (self->display, "gl-video-memory-purged",
G_CALLBACK (reset_texture), self, G_CONNECT_SWAPPED);
create_damage (self);
g_signal_connect_object (self->window, "notify::decorated",
G_CALLBACK (window_decorated_notify), self, 0);
self->unredirected = FALSE;
sync_unredirected (self);
clutter_actor_set_reactive (CLUTTER_ACTOR (self), TRUE);
return META_SURFACE_ACTOR (self);
}
void
meta_surface_actor_x11_set_size (MetaSurfaceActorX11 *self,
int width, int height)
{
MetaShapedTexture *stex = meta_surface_actor_get_texture (META_SURFACE_ACTOR (self));
if (self->last_width == width &&
self->last_height == height)
return;
self->size_changed = TRUE;
self->last_width = width;
self->last_height = height;
meta_shaped_texture_set_fallback_size (stex, width, height);
}