1f90529365
Setting the size of the texture causes the minimum and preferred width and height values to be fixed at the set value. The normal requisition functions of ClutterTexture will already report the size of the texture pixmap as the natural size, but also allow scaling down as needed if less space is available. We don't need that here, but we want to allow someone to make a ClutterClone of the texture actor.
1696 lines
45 KiB
C
1696 lines
45 KiB
C
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
|
|
|
#define _ISOC99_SOURCE /* for roundf */
|
|
#include <math.h>
|
|
|
|
#include <X11/extensions/shape.h>
|
|
#include <X11/extensions/Xcomposite.h>
|
|
#include <X11/extensions/Xdamage.h>
|
|
#include <X11/extensions/Xrender.h>
|
|
|
|
#include <clutter/x11/clutter-x11.h>
|
|
|
|
#include "../core/window-private.h" /* FIXME: add meta_window_is_override_redirect */
|
|
#include "display.h"
|
|
#include "errors.h"
|
|
#include "frame.h"
|
|
#include "window.h"
|
|
#include "xprops.h"
|
|
|
|
#include "compositor-private.h"
|
|
#include "mutter-shaped-texture.h"
|
|
#include "mutter-window-private.h"
|
|
#include "shadow.h"
|
|
#include "tidy/tidy-texture-frame.h"
|
|
|
|
struct _MutterWindowPrivate
|
|
{
|
|
XWindowAttributes attrs;
|
|
|
|
MetaWindow *window;
|
|
Window xwindow;
|
|
MetaScreen *screen;
|
|
|
|
ClutterActor *actor;
|
|
ClutterActor *shadow;
|
|
Pixmap back_pixmap;
|
|
|
|
MetaCompWindowType type;
|
|
Damage damage;
|
|
|
|
guint8 opacity;
|
|
|
|
gchar * desc;
|
|
|
|
/* If the window is shaped, a region that matches the shape */
|
|
GdkRegion *shape_region;
|
|
/* A rectangular region with the unshaped extends of the window
|
|
* texture */
|
|
GdkRegion *bounding_region;
|
|
|
|
/*
|
|
* These need to be counters rather than flags, since more plugins
|
|
* can implement same effect; the practicality of stacking effects
|
|
* might be dubious, but we have to at least handle it correctly.
|
|
*/
|
|
gint minimize_in_progress;
|
|
gint maximize_in_progress;
|
|
gint unmaximize_in_progress;
|
|
gint map_in_progress;
|
|
gint destroy_in_progress;
|
|
|
|
guint visible : 1;
|
|
guint mapped : 1;
|
|
guint shaped : 1;
|
|
guint argb32 : 1;
|
|
guint disposed : 1;
|
|
guint redecorating : 1;
|
|
|
|
guint needs_repair : 1;
|
|
guint needs_reshape : 1;
|
|
guint size_changed : 1;
|
|
|
|
guint needs_destroy : 1;
|
|
|
|
guint no_shadow : 1;
|
|
|
|
guint no_more_x_calls : 1;
|
|
};
|
|
|
|
enum
|
|
{
|
|
PROP_MCW_META_WINDOW = 1,
|
|
PROP_MCW_META_SCREEN,
|
|
PROP_MCW_X_WINDOW,
|
|
PROP_MCW_X_WINDOW_ATTRIBUTES,
|
|
PROP_MCW_NO_SHADOW,
|
|
};
|
|
|
|
static void mutter_window_dispose (GObject *object);
|
|
static void mutter_window_finalize (GObject *object);
|
|
static void mutter_window_constructed (GObject *object);
|
|
static void mutter_window_set_property (GObject *object,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec);
|
|
static void mutter_window_get_property (GObject *object,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec);
|
|
|
|
static void mutter_window_detach (MutterWindow *self);
|
|
static gboolean mutter_window_has_shadow (MutterWindow *self);
|
|
|
|
static void mutter_window_clear_shape_region (MutterWindow *self);
|
|
static void mutter_window_clear_bounding_region (MutterWindow *self);
|
|
|
|
static gboolean is_shaped (MetaDisplay *display,
|
|
Window xwindow);
|
|
/*
|
|
* Register GType wrapper for XWindowAttributes, so we do not have to
|
|
* query window attributes in the MutterWindow constructor but can pass
|
|
* them as a property to the constructor (so we can gracefully handle the case
|
|
* where no attributes can be retrieved).
|
|
*
|
|
* NB -- we only need a subset of the attributes; at some point we might want
|
|
* to just store the relevant values rather than the whole struct.
|
|
*/
|
|
#define META_TYPE_XATTRS (meta_xattrs_get_type ())
|
|
|
|
static GType meta_xattrs_get_type (void) G_GNUC_CONST;
|
|
|
|
static XWindowAttributes *
|
|
meta_xattrs_copy (const XWindowAttributes *attrs)
|
|
{
|
|
XWindowAttributes *result;
|
|
|
|
g_return_val_if_fail (attrs != NULL, NULL);
|
|
|
|
result = (XWindowAttributes*) g_malloc (sizeof (XWindowAttributes));
|
|
*result = *attrs;
|
|
|
|
return result;
|
|
}
|
|
|
|
static void
|
|
meta_xattrs_free (XWindowAttributes *attrs)
|
|
{
|
|
g_return_if_fail (attrs != NULL);
|
|
|
|
g_free (attrs);
|
|
}
|
|
|
|
static GType
|
|
meta_xattrs_get_type (void)
|
|
{
|
|
static GType our_type = 0;
|
|
|
|
if (!our_type)
|
|
our_type = g_boxed_type_register_static ("XWindowAttributes",
|
|
(GBoxedCopyFunc) meta_xattrs_copy,
|
|
(GBoxedFreeFunc) meta_xattrs_free);
|
|
return our_type;
|
|
}
|
|
|
|
G_DEFINE_TYPE (MutterWindow, mutter_window, CLUTTER_TYPE_GROUP);
|
|
|
|
static void
|
|
mutter_window_class_init (MutterWindowClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
GParamSpec *pspec;
|
|
|
|
g_type_class_add_private (klass, sizeof (MutterWindowPrivate));
|
|
|
|
object_class->dispose = mutter_window_dispose;
|
|
object_class->finalize = mutter_window_finalize;
|
|
object_class->set_property = mutter_window_set_property;
|
|
object_class->get_property = mutter_window_get_property;
|
|
object_class->constructed = mutter_window_constructed;
|
|
|
|
pspec = g_param_spec_object ("meta-window",
|
|
"MetaWindow",
|
|
"The displayed MetaWindow",
|
|
META_TYPE_WINDOW,
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
|
|
|
|
g_object_class_install_property (object_class,
|
|
PROP_MCW_META_WINDOW,
|
|
pspec);
|
|
|
|
pspec = g_param_spec_pointer ("meta-screen",
|
|
"MetaScreen",
|
|
"MetaScreen",
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
|
|
|
|
g_object_class_install_property (object_class,
|
|
PROP_MCW_META_SCREEN,
|
|
pspec);
|
|
|
|
pspec = g_param_spec_ulong ("x-window",
|
|
"Window",
|
|
"Window",
|
|
0,
|
|
G_MAXULONG,
|
|
0,
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
|
|
|
|
g_object_class_install_property (object_class,
|
|
PROP_MCW_X_WINDOW,
|
|
pspec);
|
|
|
|
pspec = g_param_spec_boxed ("x-window-attributes",
|
|
"XWindowAttributes",
|
|
"XWindowAttributes",
|
|
META_TYPE_XATTRS,
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
|
|
|
|
g_object_class_install_property (object_class,
|
|
PROP_MCW_X_WINDOW_ATTRIBUTES,
|
|
pspec);
|
|
|
|
pspec = g_param_spec_boolean ("no-shadow",
|
|
"No shadow",
|
|
"Do not add shaddow to this window",
|
|
FALSE,
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
|
|
|
|
g_object_class_install_property (object_class,
|
|
PROP_MCW_NO_SHADOW,
|
|
pspec);
|
|
}
|
|
|
|
static void
|
|
mutter_window_init (MutterWindow *self)
|
|
{
|
|
MutterWindowPrivate *priv;
|
|
|
|
priv = self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
|
|
MUTTER_TYPE_COMP_WINDOW,
|
|
MutterWindowPrivate);
|
|
priv->opacity = 0xff;
|
|
}
|
|
|
|
static void
|
|
mutter_meta_window_decorated_notify (MetaWindow *mw,
|
|
GParamSpec *arg1,
|
|
gpointer data)
|
|
{
|
|
MutterWindow *self = MUTTER_WINDOW (data);
|
|
MutterWindowPrivate *priv = self->priv;
|
|
MetaFrame *frame = meta_window_get_frame (mw);
|
|
MetaScreen *screen = priv->screen;
|
|
MetaDisplay *display = meta_screen_get_display (screen);
|
|
Display *xdisplay = meta_display_get_xdisplay (display);
|
|
Window new_xwindow;
|
|
MetaCompScreen *info;
|
|
XWindowAttributes attrs;
|
|
|
|
/*
|
|
* Basically, we have to reconstruct the the internals of this object
|
|
* from scratch, as everything has changed.
|
|
*/
|
|
priv->redecorating = TRUE;
|
|
|
|
if (frame)
|
|
new_xwindow = meta_frame_get_xwindow (frame);
|
|
else
|
|
new_xwindow = meta_window_get_xwindow (mw);
|
|
|
|
mutter_window_detach (self);
|
|
|
|
info = meta_screen_get_compositor_data (screen);
|
|
|
|
/*
|
|
* 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, FALSE);
|
|
priv->damage = None;
|
|
}
|
|
|
|
g_hash_table_remove (info->windows_by_xid, (gpointer) priv->xwindow);
|
|
g_hash_table_insert (info->windows_by_xid, (gpointer) new_xwindow, self);
|
|
|
|
g_free (priv->desc);
|
|
priv->desc = NULL;
|
|
|
|
priv->xwindow = new_xwindow;
|
|
|
|
if (!XGetWindowAttributes (xdisplay, new_xwindow, &attrs))
|
|
{
|
|
g_warning ("Could not obtain attributes for window 0x%x after "
|
|
"decoration change",
|
|
(guint) new_xwindow);
|
|
return;
|
|
}
|
|
|
|
g_object_set (self, "x-window-attributes", &attrs, NULL);
|
|
|
|
if (priv->shadow)
|
|
{
|
|
ClutterActor *p = clutter_actor_get_parent (priv->shadow);
|
|
|
|
if (CLUTTER_IS_CONTAINER (p))
|
|
clutter_container_remove_actor (CLUTTER_CONTAINER (p), priv->shadow);
|
|
else
|
|
clutter_actor_unparent (priv->shadow);
|
|
|
|
priv->shadow = NULL;
|
|
}
|
|
|
|
/*
|
|
* Recreate the contents.
|
|
*/
|
|
mutter_window_constructed (G_OBJECT (self));
|
|
}
|
|
|
|
static void
|
|
mutter_window_constructed (GObject *object)
|
|
{
|
|
MutterWindow *self = MUTTER_WINDOW (object);
|
|
MutterWindowPrivate *priv = self->priv;
|
|
MetaScreen *screen = priv->screen;
|
|
MetaDisplay *display = meta_screen_get_display (screen);
|
|
Window xwindow = priv->xwindow;
|
|
Display *xdisplay = meta_display_get_xdisplay (display);
|
|
XRenderPictFormat *format;
|
|
MetaCompositor *compositor;
|
|
|
|
compositor = meta_display_get_compositor (display);
|
|
|
|
mutter_window_update_window_type (self);
|
|
|
|
#ifdef HAVE_SHAPE
|
|
/* Listen for ShapeNotify events on the window */
|
|
if (meta_display_has_shape (display))
|
|
XShapeSelectInput (xdisplay, xwindow, ShapeNotifyMask);
|
|
#endif
|
|
|
|
priv->shaped = is_shaped (display, xwindow);
|
|
|
|
if (priv->attrs.class == InputOnly)
|
|
priv->damage = None;
|
|
else
|
|
priv->damage = XDamageCreate (xdisplay, xwindow, XDamageReportNonEmpty);
|
|
|
|
format = XRenderFindVisualFormat (xdisplay, priv->attrs.visual);
|
|
|
|
if (format && format->type == PictTypeDirect && format->direct.alphaMask)
|
|
priv->argb32 = TRUE;
|
|
|
|
mutter_window_update_opacity (self);
|
|
|
|
if (mutter_window_has_shadow (self))
|
|
{
|
|
priv->shadow = mutter_create_shadow_frame (compositor);
|
|
|
|
clutter_container_add_actor (CLUTTER_CONTAINER (self), priv->shadow);
|
|
}
|
|
|
|
if (!priv->actor)
|
|
{
|
|
priv->actor = mutter_shaped_texture_new ();
|
|
|
|
if (!clutter_glx_texture_pixmap_using_extension (
|
|
CLUTTER_GLX_TEXTURE_PIXMAP (priv->actor)))
|
|
g_warning ("NOTE: Not using GLX TFP!\n");
|
|
|
|
clutter_container_add_actor (CLUTTER_CONTAINER (self), priv->actor);
|
|
|
|
g_signal_connect (priv->window, "notify::decorated",
|
|
G_CALLBACK (mutter_meta_window_decorated_notify), self);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* This is the case where existing window is gaining/loosing frame.
|
|
* Just ensure the actor is top most (i.e., above shadow).
|
|
*/
|
|
clutter_actor_raise_top (priv->actor);
|
|
}
|
|
|
|
|
|
mutter_window_update_shape (self, priv->shaped);
|
|
}
|
|
|
|
static void
|
|
mutter_window_dispose (GObject *object)
|
|
{
|
|
MutterWindow *self = MUTTER_WINDOW (object);
|
|
MutterWindowPrivate *priv = self->priv;
|
|
MetaScreen *screen;
|
|
MetaDisplay *display;
|
|
Display *xdisplay;
|
|
MetaCompScreen *info;
|
|
|
|
if (priv->disposed)
|
|
return;
|
|
|
|
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);
|
|
|
|
mutter_window_detach (self);
|
|
|
|
mutter_window_clear_shape_region (self);
|
|
mutter_window_clear_bounding_region (self);
|
|
|
|
if (priv->damage != None)
|
|
{
|
|
meta_error_trap_push (display);
|
|
XDamageDestroy (xdisplay, priv->damage);
|
|
meta_error_trap_pop (display, FALSE);
|
|
|
|
priv->damage = None;
|
|
}
|
|
|
|
info->windows = g_list_remove (info->windows, (gconstpointer) self);
|
|
g_hash_table_remove (info->windows_by_xid, (gpointer) priv->xwindow);
|
|
|
|
g_free (priv->desc);
|
|
|
|
G_OBJECT_CLASS (mutter_window_parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
mutter_window_finalize (GObject *object)
|
|
{
|
|
G_OBJECT_CLASS (mutter_window_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
mutter_window_set_property (GObject *object,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
MutterWindow *self = MUTTER_WINDOW (object);
|
|
MutterWindowPrivate *priv = self->priv;
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_MCW_META_WINDOW:
|
|
priv->window = g_value_get_object (value);
|
|
break;
|
|
case PROP_MCW_META_SCREEN:
|
|
priv->screen = g_value_get_pointer (value);
|
|
break;
|
|
case PROP_MCW_X_WINDOW:
|
|
priv->xwindow = g_value_get_ulong (value);
|
|
break;
|
|
case PROP_MCW_X_WINDOW_ATTRIBUTES:
|
|
priv->attrs = *((XWindowAttributes*)g_value_get_boxed (value));
|
|
break;
|
|
case PROP_MCW_NO_SHADOW:
|
|
{
|
|
gboolean oldv = priv->no_shadow ? TRUE : FALSE;
|
|
gboolean newv = g_value_get_boolean (value);
|
|
|
|
if (oldv == newv)
|
|
return;
|
|
|
|
priv->no_shadow = newv;
|
|
|
|
if (newv && priv->shadow)
|
|
{
|
|
clutter_container_remove_actor (CLUTTER_CONTAINER (object),
|
|
priv->shadow);
|
|
priv->shadow = NULL;
|
|
}
|
|
else if (!newv && !priv->shadow && mutter_window_has_shadow (self))
|
|
{
|
|
gfloat w, h;
|
|
MetaDisplay *display = meta_screen_get_display (priv->screen);
|
|
MetaCompositor *compositor;
|
|
|
|
compositor = meta_display_get_compositor (display);
|
|
|
|
clutter_actor_get_size (CLUTTER_ACTOR (self), &w, &h);
|
|
|
|
priv->shadow = mutter_create_shadow_frame (compositor);
|
|
|
|
clutter_actor_set_size (priv->shadow, w, h);
|
|
|
|
clutter_container_add_actor (CLUTTER_CONTAINER (self), priv->shadow);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
mutter_window_get_property (GObject *object,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
MutterWindowPrivate *priv = MUTTER_WINDOW (object)->priv;
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_MCW_META_WINDOW:
|
|
g_value_set_object (value, priv->window);
|
|
break;
|
|
case PROP_MCW_META_SCREEN:
|
|
g_value_set_pointer (value, priv->screen);
|
|
break;
|
|
case PROP_MCW_X_WINDOW:
|
|
g_value_set_ulong (value, priv->xwindow);
|
|
break;
|
|
case PROP_MCW_X_WINDOW_ATTRIBUTES:
|
|
g_value_set_boxed (value, &priv->attrs);
|
|
break;
|
|
case PROP_MCW_NO_SHADOW:
|
|
g_value_set_boolean (value, priv->no_shadow);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
mutter_window_update_window_type (MutterWindow *self)
|
|
{
|
|
MutterWindowPrivate *priv = self->priv;
|
|
priv->type = (MetaCompWindowType) meta_window_get_window_type (priv->window);
|
|
}
|
|
|
|
static gboolean
|
|
is_shaped (MetaDisplay *display, Window xwindow)
|
|
{
|
|
Display *xdisplay = meta_display_get_xdisplay (display);
|
|
gint xws, yws, xbs, ybs;
|
|
guint wws, hws, wbs, hbs;
|
|
gint bounding_shaped, clip_shaped;
|
|
|
|
if (meta_display_has_shape (display))
|
|
{
|
|
XShapeQueryExtents (xdisplay, xwindow, &bounding_shaped,
|
|
&xws, &yws, &wws, &hws, &clip_shaped,
|
|
&xbs, &ybs, &wbs, &hbs);
|
|
return (bounding_shaped != 0);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
mutter_window_has_shadow (MutterWindow *self)
|
|
{
|
|
MutterWindowPrivate * priv = self->priv;
|
|
|
|
if (priv->no_shadow)
|
|
return FALSE;
|
|
|
|
/*
|
|
* Always put a shadow around windows with a frame - This should override
|
|
* the restriction about not putting a shadow around shaped windows
|
|
* as the frame might be the reason the window is shaped
|
|
*/
|
|
if (priv->window)
|
|
{
|
|
if (meta_window_get_frame (priv->window))
|
|
{
|
|
meta_verbose ("Window 0x%x has shadow because it has a frame\n",
|
|
(guint)priv->xwindow);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Do not add shadows to ARGB windows (since they are probably transparent)
|
|
*/
|
|
if (priv->argb32 || priv->opacity != 0xff)
|
|
{
|
|
meta_verbose ("Window 0x%x has no shadow as it is ARGB\n",
|
|
(guint)priv->xwindow);
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Never put a shadow around shaped windows
|
|
*/
|
|
if (priv->shaped)
|
|
{
|
|
meta_verbose ("Window 0x%x has no shadow as it is shaped\n",
|
|
(guint)priv->xwindow);
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Add shadows to override redirect windows (e.g., Gtk menus).
|
|
* This must have lower priority than window shape test.
|
|
*/
|
|
if (priv->attrs.override_redirect)
|
|
{
|
|
meta_verbose ("Window 0x%x has shadow because it is override redirect.\n",
|
|
(guint)priv->xwindow);
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* Don't put shadow around DND icon windows
|
|
*/
|
|
if (priv->type == META_COMP_WINDOW_DND ||
|
|
priv->type == META_COMP_WINDOW_DESKTOP)
|
|
{
|
|
meta_verbose ("Window 0x%x has no shadow as it is DND or Desktop\n",
|
|
(guint)priv->xwindow);
|
|
return FALSE;
|
|
}
|
|
|
|
if (priv->type == META_COMP_WINDOW_MENU
|
|
#if 0
|
|
|| priv->type == META_COMP_WINDOW_DROPDOWN_MENU
|
|
#endif
|
|
)
|
|
{
|
|
meta_verbose ("Window 0x%x has shadow as it is a menu\n",
|
|
(guint)priv->xwindow);
|
|
return TRUE;
|
|
}
|
|
|
|
#if 0
|
|
if (priv->type == META_COMP_WINDOW_TOOLTIP)
|
|
{
|
|
meta_verbose ("Window 0x%x has shadow as it is a tooltip\n",
|
|
(guint)priv->xwindow);
|
|
return TRUE;
|
|
}
|
|
#endif
|
|
|
|
meta_verbose ("Window 0x%x has no shadow as it fell through\n",
|
|
(guint)priv->xwindow);
|
|
return FALSE;
|
|
}
|
|
|
|
Window
|
|
mutter_window_get_x_window (MutterWindow *self)
|
|
{
|
|
if (!self)
|
|
return None;
|
|
|
|
return self->priv->xwindow;
|
|
}
|
|
|
|
/**
|
|
* mutter_window_get_meta_window:
|
|
*
|
|
* Gets the MetaWindow object that the the MutterWindow is displaying
|
|
*
|
|
* Return value: (transfer none): the displayed MetaWindow
|
|
*/
|
|
MetaWindow *
|
|
mutter_window_get_meta_window (MutterWindow *self)
|
|
{
|
|
return self->priv->window;
|
|
}
|
|
|
|
/**
|
|
* mutter_window_get_texture:
|
|
*
|
|
* Gets the ClutterActor that is used to display the contents of the window
|
|
*
|
|
* Return value: (transfer none): the ClutterActor for the contents
|
|
*/
|
|
ClutterActor *
|
|
mutter_window_get_texture (MutterWindow *self)
|
|
{
|
|
return self->priv->actor;
|
|
}
|
|
|
|
MetaCompWindowType
|
|
mutter_window_get_window_type (MutterWindow *self)
|
|
{
|
|
if (!self)
|
|
return 0;
|
|
|
|
return self->priv->type;
|
|
}
|
|
|
|
gboolean
|
|
mutter_window_is_override_redirect (MutterWindow *self)
|
|
{
|
|
if (self->priv->window->override_redirect)
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
const char *mutter_window_get_description (MutterWindow *self)
|
|
{
|
|
/*
|
|
* For windows managed by the WM, we just defer to the WM for the window
|
|
* description. For override-redirect windows, we create the description
|
|
* ourselves, but only on demand.
|
|
*/
|
|
if (self->priv->window)
|
|
return meta_window_get_description (self->priv->window);
|
|
|
|
if (G_UNLIKELY (self->priv->desc == NULL))
|
|
{
|
|
self->priv->desc = g_strdup_printf ("Override Redirect (0x%x)",
|
|
(guint) self->priv->xwindow);
|
|
}
|
|
|
|
return self->priv->desc;
|
|
}
|
|
|
|
gint
|
|
mutter_window_get_workspace (MutterWindow *self)
|
|
{
|
|
MutterWindowPrivate *priv;
|
|
MetaWorkspace *workspace;
|
|
|
|
if (!self)
|
|
return -1;
|
|
|
|
priv = self->priv;
|
|
|
|
if (!priv->window || meta_window_is_on_all_workspaces (priv->window))
|
|
return -1;
|
|
|
|
workspace = meta_window_get_workspace (priv->window);
|
|
|
|
return meta_workspace_index (workspace);
|
|
}
|
|
|
|
gboolean
|
|
mutter_window_showing_on_its_workspace (MutterWindow *self)
|
|
{
|
|
if (!self)
|
|
return FALSE;
|
|
|
|
/* If override redirect: */
|
|
if (!self->priv->window)
|
|
return TRUE;
|
|
|
|
return meta_window_showing_on_its_workspace (self->priv->window);
|
|
}
|
|
|
|
gboolean
|
|
mutter_window_effect_in_progress (MutterWindow *self)
|
|
{
|
|
return (self->priv->minimize_in_progress ||
|
|
self->priv->maximize_in_progress ||
|
|
self->priv->unmaximize_in_progress ||
|
|
self->priv->map_in_progress ||
|
|
self->priv->destroy_in_progress);
|
|
}
|
|
|
|
static void
|
|
mutter_window_mark_for_repair (MutterWindow *self)
|
|
{
|
|
MutterWindowPrivate *priv = self->priv;
|
|
|
|
priv->needs_repair = TRUE;
|
|
|
|
if (!priv->mapped)
|
|
return;
|
|
|
|
/* This will cause the compositor paint function to be run
|
|
* if the actor is visible or a clone of the actor is visible.
|
|
* if the actor isn't visible in any way, then we don't
|
|
* need to repair the window anyways, and can wait until
|
|
* the stage is redrawn for some other reason
|
|
*
|
|
* The compositor paint function repairs all windows.
|
|
*/
|
|
clutter_actor_queue_redraw (priv->actor);
|
|
}
|
|
|
|
static gboolean
|
|
start_simple_effect (MutterWindow *self,
|
|
gulong event)
|
|
{
|
|
MutterWindowPrivate *priv = self->priv;
|
|
MetaCompScreen *info = meta_screen_get_compositor_data (priv->screen);
|
|
gint *counter = NULL;
|
|
|
|
if (!info->plugin_mgr)
|
|
return FALSE;
|
|
|
|
switch (event)
|
|
{
|
|
case MUTTER_PLUGIN_MINIMIZE:
|
|
counter = &priv->minimize_in_progress;
|
|
break;
|
|
case MUTTER_PLUGIN_MAP:
|
|
counter = &priv->map_in_progress;
|
|
break;
|
|
case MUTTER_PLUGIN_DESTROY:
|
|
counter = &priv->destroy_in_progress;
|
|
break;
|
|
case MUTTER_PLUGIN_UNMAXIMIZE:
|
|
case MUTTER_PLUGIN_MAXIMIZE:
|
|
case MUTTER_PLUGIN_SWITCH_WORKSPACE:
|
|
g_assert_not_reached ();
|
|
break;
|
|
}
|
|
|
|
g_assert (counter);
|
|
|
|
(*counter)++;
|
|
|
|
if (!mutter_plugin_manager_event_simple (info->plugin_mgr,
|
|
self,
|
|
event))
|
|
{
|
|
(*counter)--;
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
mutter_window_after_effects (MutterWindow *self)
|
|
{
|
|
MutterWindowPrivate *priv = self->priv;
|
|
|
|
if (priv->needs_destroy)
|
|
{
|
|
clutter_actor_destroy (CLUTTER_ACTOR (self));
|
|
return;
|
|
}
|
|
|
|
mutter_window_sync_visibility (self);
|
|
mutter_window_sync_actor_position (self);
|
|
|
|
if (!priv->window->mapped)
|
|
mutter_window_detach (self);
|
|
|
|
if (priv->needs_repair)
|
|
clutter_actor_queue_redraw (priv->actor);
|
|
}
|
|
|
|
void
|
|
mutter_window_effect_completed (MutterWindow *self,
|
|
gulong event)
|
|
{
|
|
MutterWindowPrivate *priv = self->priv;
|
|
|
|
/* NB: Keep in mind that when effects get completed it possible
|
|
* that the corresponding MetaWindow may have be been destroyed.
|
|
* In this case priv->window will == NULL */
|
|
|
|
switch (event)
|
|
{
|
|
case MUTTER_PLUGIN_MINIMIZE:
|
|
{
|
|
priv->minimize_in_progress--;
|
|
if (priv->minimize_in_progress < 0)
|
|
{
|
|
g_warning ("Error in minimize accounting.");
|
|
priv->minimize_in_progress = 0;
|
|
}
|
|
}
|
|
break;
|
|
case MUTTER_PLUGIN_MAP:
|
|
/*
|
|
* Make sure that the actor is at the correct place in case
|
|
* the plugin fscked.
|
|
*/
|
|
priv->map_in_progress--;
|
|
|
|
if (priv->map_in_progress < 0)
|
|
{
|
|
g_warning ("Error in map accounting.");
|
|
priv->map_in_progress = 0;
|
|
}
|
|
break;
|
|
case MUTTER_PLUGIN_DESTROY:
|
|
priv->destroy_in_progress--;
|
|
|
|
if (priv->destroy_in_progress < 0)
|
|
{
|
|
g_warning ("Error in destroy accounting.");
|
|
priv->destroy_in_progress = 0;
|
|
}
|
|
break;
|
|
case MUTTER_PLUGIN_UNMAXIMIZE:
|
|
priv->unmaximize_in_progress--;
|
|
if (priv->unmaximize_in_progress < 0)
|
|
{
|
|
g_warning ("Error in unmaximize accounting.");
|
|
priv->unmaximize_in_progress = 0;
|
|
}
|
|
break;
|
|
case MUTTER_PLUGIN_MAXIMIZE:
|
|
priv->maximize_in_progress--;
|
|
if (priv->maximize_in_progress < 0)
|
|
{
|
|
g_warning ("Error in maximize accounting.");
|
|
priv->maximize_in_progress = 0;
|
|
}
|
|
break;
|
|
case MUTTER_PLUGIN_SWITCH_WORKSPACE:
|
|
g_assert_not_reached ();
|
|
break;
|
|
}
|
|
|
|
if (!mutter_window_effect_in_progress (self))
|
|
mutter_window_after_effects (self);
|
|
}
|
|
|
|
/* Called to drop our reference to a window backing pixmap that we
|
|
* previously obtained with XCompositeNameWindowPixmap. We do this
|
|
* when the window is unmapped or when we want to update to a new
|
|
* pixmap for a new size.
|
|
*/
|
|
static void
|
|
mutter_window_detach (MutterWindow *self)
|
|
{
|
|
MutterWindowPrivate *priv = self->priv;
|
|
MetaScreen *screen = priv->screen;
|
|
MetaDisplay *display = meta_screen_get_display (screen);
|
|
Display *xdisplay = meta_display_get_xdisplay (display);
|
|
|
|
if (!priv->back_pixmap)
|
|
return;
|
|
|
|
XFreePixmap (xdisplay, priv->back_pixmap);
|
|
priv->back_pixmap = None;
|
|
clutter_x11_texture_pixmap_set_pixmap (CLUTTER_X11_TEXTURE_PIXMAP (priv->actor),
|
|
None);
|
|
|
|
mutter_window_mark_for_repair (self);
|
|
}
|
|
|
|
void
|
|
mutter_window_destroy (MutterWindow *self)
|
|
{
|
|
MetaWindow *window;
|
|
MetaCompScreen *info;
|
|
MutterWindowPrivate *priv;
|
|
|
|
priv = self->priv;
|
|
|
|
window = priv->window;
|
|
meta_window_set_compositor_private (window, NULL);
|
|
|
|
/*
|
|
* We remove the window from internal lookup hashes and thus any other
|
|
* unmap events etc fail
|
|
*/
|
|
info = meta_screen_get_compositor_data (priv->screen);
|
|
info->windows = g_list_remove (info->windows, (gconstpointer) self);
|
|
g_hash_table_remove (info->windows_by_xid, (gpointer)priv->xwindow);
|
|
|
|
if (priv->type == META_COMP_WINDOW_DROPDOWN_MENU ||
|
|
priv->type == META_COMP_WINDOW_POPUP_MENU ||
|
|
priv->type == META_COMP_WINDOW_TOOLTIP ||
|
|
priv->type == META_COMP_WINDOW_NOTIFICATION ||
|
|
priv->type == META_COMP_WINDOW_COMBO ||
|
|
priv->type == META_COMP_WINDOW_DND ||
|
|
priv->type == META_COMP_WINDOW_OVERRIDE_OTHER)
|
|
{
|
|
/*
|
|
* No effects, just kill it.
|
|
*/
|
|
clutter_actor_destroy (CLUTTER_ACTOR (self));
|
|
return;
|
|
}
|
|
|
|
priv->needs_destroy = TRUE;
|
|
|
|
/*
|
|
* Once the window destruction is initiated we can no longer perform any
|
|
* furter X-based operations. For example, if we have a Map effect running,
|
|
* we cannot query the window geometry once the effect completes. So, flag
|
|
* this.
|
|
*/
|
|
priv->no_more_x_calls = TRUE;
|
|
|
|
if (!mutter_window_effect_in_progress (self))
|
|
clutter_actor_destroy (CLUTTER_ACTOR (self));
|
|
}
|
|
|
|
void
|
|
mutter_window_sync_actor_position (MutterWindow *self)
|
|
{
|
|
MutterWindowPrivate *priv = self->priv;
|
|
MetaRectangle window_rect;
|
|
|
|
meta_window_get_outer_rect (priv->window, &window_rect);
|
|
|
|
if (priv->attrs.width != window_rect.width ||
|
|
priv->attrs.height != window_rect.height)
|
|
{
|
|
priv->size_changed = TRUE;
|
|
mutter_window_mark_for_repair (self);
|
|
}
|
|
|
|
/* XXX deprecated: please use meta_window_get_outer_rect instead */
|
|
priv->attrs.width = window_rect.width;
|
|
priv->attrs.height = window_rect.height;
|
|
priv->attrs.x = window_rect.x;
|
|
priv->attrs.y = window_rect.y;
|
|
|
|
if (mutter_window_effect_in_progress (self))
|
|
return;
|
|
|
|
clutter_actor_set_position (CLUTTER_ACTOR (self),
|
|
window_rect.x, window_rect.y);
|
|
clutter_actor_set_size (CLUTTER_ACTOR (self),
|
|
window_rect.width, window_rect.height);
|
|
}
|
|
|
|
void
|
|
mutter_window_show (MutterWindow *self,
|
|
MetaCompEffect effect)
|
|
{
|
|
MutterWindowPrivate *priv;
|
|
MetaCompScreen *info;
|
|
gulong event;
|
|
|
|
priv = self->priv;
|
|
info = meta_screen_get_compositor_data (priv->screen);
|
|
|
|
g_return_if_fail (!priv->visible);
|
|
|
|
self->priv->visible = TRUE;
|
|
|
|
event = 0;
|
|
switch (effect)
|
|
{
|
|
case META_COMP_EFFECT_CREATE:
|
|
event = MUTTER_PLUGIN_MAP;
|
|
break;
|
|
case META_COMP_EFFECT_UNMINIMIZE:
|
|
/* FIXME: should have MUTTER_PLUGIN_UNMINIMIZE */
|
|
event = MUTTER_PLUGIN_MAP;
|
|
break;
|
|
case META_COMP_EFFECT_NONE:
|
|
break;
|
|
case META_COMP_EFFECT_DESTROY:
|
|
case META_COMP_EFFECT_MINIMIZE:
|
|
g_assert_not_reached();
|
|
}
|
|
|
|
if (priv->redecorating ||
|
|
info->switch_workspace_in_progress ||
|
|
event == 0 ||
|
|
!start_simple_effect (self, event))
|
|
{
|
|
clutter_actor_show_all (CLUTTER_ACTOR (self));
|
|
priv->redecorating = FALSE;
|
|
}
|
|
}
|
|
|
|
void
|
|
mutter_window_hide (MutterWindow *self,
|
|
MetaCompEffect effect)
|
|
{
|
|
MutterWindowPrivate *priv;
|
|
MetaCompScreen *info;
|
|
gulong event;
|
|
|
|
priv = self->priv;
|
|
info = meta_screen_get_compositor_data (priv->screen);
|
|
|
|
g_return_if_fail (priv->visible);
|
|
|
|
priv->visible = FALSE;
|
|
|
|
/* If a plugin is animating a workspace transition, we have to
|
|
* hold off on hiding the window, and do it after the workspace
|
|
* switch completes
|
|
*/
|
|
if (info->switch_workspace_in_progress)
|
|
return;
|
|
|
|
event = 0;
|
|
switch (effect)
|
|
{
|
|
case META_COMP_EFFECT_DESTROY:
|
|
event = MUTTER_PLUGIN_DESTROY;
|
|
break;
|
|
case META_COMP_EFFECT_MINIMIZE:
|
|
event = MUTTER_PLUGIN_MINIMIZE;
|
|
break;
|
|
case META_COMP_EFFECT_NONE:
|
|
break;
|
|
case META_COMP_EFFECT_UNMINIMIZE:
|
|
case META_COMP_EFFECT_CREATE:
|
|
g_assert_not_reached();
|
|
}
|
|
|
|
if (event == 0 ||
|
|
!start_simple_effect (self, event))
|
|
clutter_actor_hide (CLUTTER_ACTOR (self));
|
|
}
|
|
|
|
void
|
|
mutter_window_maximize (MutterWindow *self,
|
|
MetaRectangle *old_rect,
|
|
MetaRectangle *new_rect)
|
|
{
|
|
MetaCompScreen *info = meta_screen_get_compositor_data (self->priv->screen);
|
|
|
|
/* The window has already been resized (in order to compute new_rect),
|
|
* which by side effect caused the actor to be resized. Restore it to the
|
|
* old size and position */
|
|
clutter_actor_set_position (CLUTTER_ACTOR (self), old_rect->x, old_rect->y);
|
|
clutter_actor_set_size (CLUTTER_ACTOR (self), old_rect->width, old_rect->height);
|
|
|
|
self->priv->maximize_in_progress++;
|
|
|
|
if (!info->plugin_mgr ||
|
|
!mutter_plugin_manager_event_maximize (info->plugin_mgr,
|
|
self,
|
|
MUTTER_PLUGIN_MAXIMIZE,
|
|
new_rect->x, new_rect->y,
|
|
new_rect->width, new_rect->height))
|
|
|
|
{
|
|
self->priv->maximize_in_progress--;
|
|
}
|
|
}
|
|
|
|
void
|
|
mutter_window_unmaximize (MutterWindow *self,
|
|
MetaRectangle *old_rect,
|
|
MetaRectangle *new_rect)
|
|
{
|
|
MetaCompScreen *info = meta_screen_get_compositor_data (self->priv->screen);
|
|
|
|
/* The window has already been resized (in order to compute new_rect),
|
|
* which by side effect caused the actor to be resized. Restore it to the
|
|
* old size and position */
|
|
clutter_actor_set_position (CLUTTER_ACTOR (self), old_rect->x, old_rect->y);
|
|
clutter_actor_set_size (CLUTTER_ACTOR (self), old_rect->width, old_rect->height);
|
|
|
|
self->priv->unmaximize_in_progress++;
|
|
|
|
if (!info->plugin_mgr ||
|
|
!mutter_plugin_manager_event_maximize (info->plugin_mgr,
|
|
self,
|
|
MUTTER_PLUGIN_UNMAXIMIZE,
|
|
new_rect->x, new_rect->y,
|
|
new_rect->width, new_rect->height))
|
|
{
|
|
self->priv->unmaximize_in_progress--;
|
|
}
|
|
}
|
|
|
|
MutterWindow *
|
|
mutter_window_new (MetaWindow *window)
|
|
{
|
|
MetaScreen *screen = meta_window_get_screen (window);
|
|
MetaDisplay *display = meta_screen_get_display (screen);
|
|
MetaCompScreen *info = meta_screen_get_compositor_data (screen);
|
|
MutterWindow *self;
|
|
MutterWindowPrivate *priv;
|
|
MetaFrame *frame;
|
|
Window top_window;
|
|
XWindowAttributes attrs;
|
|
|
|
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);
|
|
|
|
/* FIXME: Remove the redundant data we store in self->priv->attrs, and
|
|
* simply query metacity core for the data. */
|
|
if (!XGetWindowAttributes (display->xdisplay, top_window, &attrs))
|
|
return NULL;
|
|
|
|
self = g_object_new (MUTTER_TYPE_COMP_WINDOW,
|
|
"meta-window", window,
|
|
"x-window", top_window,
|
|
"meta-screen", screen,
|
|
"x-window-attributes", &attrs,
|
|
NULL);
|
|
|
|
priv = self->priv;
|
|
|
|
priv->mapped = meta_window_toplevel_is_mapped (priv->window);
|
|
|
|
mutter_window_sync_actor_position (self);
|
|
|
|
/* Hang our compositor window state off the MetaWindow for fast retrieval */
|
|
meta_window_set_compositor_private (window, G_OBJECT (self));
|
|
|
|
clutter_container_add_actor (CLUTTER_CONTAINER (info->window_group),
|
|
CLUTTER_ACTOR (self));
|
|
clutter_actor_hide (CLUTTER_ACTOR (self));
|
|
|
|
/* Initial position in the stack is arbitrary; stacking will be synced
|
|
* before we first paint.
|
|
*/
|
|
info->windows = g_list_append (info->windows, self);
|
|
g_hash_table_insert (info->windows_by_xid, (gpointer) top_window, self);
|
|
|
|
return self;
|
|
}
|
|
|
|
void
|
|
mutter_window_mapped (MutterWindow *self)
|
|
{
|
|
MutterWindowPrivate *priv = self->priv;
|
|
|
|
g_return_if_fail (!priv->mapped);
|
|
|
|
priv->mapped = TRUE;
|
|
|
|
mutter_window_mark_for_repair (self);
|
|
}
|
|
|
|
void
|
|
mutter_window_unmapped (MutterWindow *self)
|
|
{
|
|
MutterWindowPrivate *priv = self->priv;
|
|
|
|
g_return_if_fail (priv->mapped);
|
|
|
|
priv->mapped = FALSE;
|
|
|
|
if (mutter_window_effect_in_progress (self))
|
|
return;
|
|
|
|
mutter_window_detach (self);
|
|
priv->needs_repair = FALSE;
|
|
}
|
|
|
|
static void
|
|
mutter_window_clear_shape_region (MutterWindow *self)
|
|
{
|
|
MutterWindowPrivate *priv = self->priv;
|
|
|
|
if (priv->shape_region)
|
|
{
|
|
gdk_region_destroy (priv->shape_region);
|
|
priv->shape_region = NULL;
|
|
}
|
|
}
|
|
|
|
static void
|
|
mutter_window_clear_bounding_region (MutterWindow *self)
|
|
{
|
|
MutterWindowPrivate *priv = self->priv;
|
|
|
|
if (priv->bounding_region)
|
|
{
|
|
gdk_region_destroy (priv->bounding_region);
|
|
priv->bounding_region = NULL;
|
|
}
|
|
}
|
|
|
|
static void
|
|
mutter_window_update_bounding_region (MutterWindow *self,
|
|
int width,
|
|
int height)
|
|
{
|
|
MutterWindowPrivate *priv = self->priv;
|
|
GdkRectangle bounding_rectangle = { 0, 0, width, height };
|
|
|
|
mutter_window_clear_bounding_region (self);
|
|
|
|
priv->bounding_region = gdk_region_rectangle (&bounding_rectangle);
|
|
}
|
|
|
|
static void
|
|
mutter_window_update_shape_region (MutterWindow *self,
|
|
int n_rects,
|
|
XRectangle *rects)
|
|
{
|
|
MutterWindowPrivate *priv = self->priv;
|
|
int i;
|
|
|
|
mutter_window_clear_shape_region (self);
|
|
|
|
priv->shape_region = gdk_region_new ();
|
|
for (i = 0; i < n_rects; i++)
|
|
{
|
|
GdkRectangle rect = { rects[i].x, rects[i].y, rects[i].width, rects[i].height };
|
|
gdk_region_union_with_rect (priv->shape_region, &rect);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* mutter_window_get_obscured_region:
|
|
* @self: a #MutterWindow
|
|
*
|
|
* Gets the region that is completely obscured by the window. Coordinates
|
|
* are relative to the upper-left of the window.
|
|
*
|
|
* Return value: (transfer none): the area obscured by the window,
|
|
* %NULL is the same as an empty region.
|
|
*/
|
|
GdkRegion *
|
|
mutter_window_get_obscured_region (MutterWindow *self)
|
|
{
|
|
MutterWindowPrivate *priv = self->priv;
|
|
|
|
if (!priv->argb32 && priv->back_pixmap)
|
|
{
|
|
if (priv->shaped)
|
|
return priv->shape_region;
|
|
else
|
|
return priv->bounding_region;
|
|
}
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
#if 0
|
|
/* Print out a region; useful for debugging */
|
|
static void
|
|
dump_region (GdkRegion *region)
|
|
{
|
|
GdkRectangle *rects;
|
|
int n_rects;
|
|
int i;
|
|
|
|
gdk_region_get_rectangles (region, &rects, &n_rects);
|
|
g_print ("[");
|
|
for (i = 0; i < n_rects; i++)
|
|
{
|
|
g_print ("+%d+%dx%dx%d ",
|
|
rects[i].x, rects[i].y, rects[i].width, rects[i].height);
|
|
}
|
|
g_print ("]\n");
|
|
g_free (rects);
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* mutter_window_set_visible_region:
|
|
* @self: a #MutterWindow
|
|
* @visible_region: the region of the screen that isn't completely
|
|
* obscured.
|
|
*
|
|
* Provides a hint as to what areas of the window need to be
|
|
* drawn. Regions not in @visible_region are completely obscured.
|
|
* This will be set before painting then unset afterwards.
|
|
*/
|
|
void
|
|
mutter_window_set_visible_region (MutterWindow *self,
|
|
GdkRegion *visible_region)
|
|
{
|
|
MutterWindowPrivate *priv = self->priv;
|
|
GdkRegion *texture_clip_region = NULL;
|
|
|
|
/* Get the area of the window texture that would be drawn if
|
|
* we weren't obscured at all
|
|
*/
|
|
if (priv->shaped)
|
|
{
|
|
if (priv->shape_region)
|
|
texture_clip_region = gdk_region_copy (priv->shape_region);
|
|
}
|
|
else
|
|
{
|
|
if (priv->bounding_region)
|
|
texture_clip_region = gdk_region_copy (priv->bounding_region);
|
|
}
|
|
|
|
if (!texture_clip_region)
|
|
texture_clip_region = gdk_region_new ();
|
|
|
|
/* Then intersect that with the visible region to get the region
|
|
* that we actually need to redraw.
|
|
*/
|
|
gdk_region_intersect (texture_clip_region, visible_region);
|
|
|
|
/* Assumes ownership */
|
|
mutter_shaped_texture_set_clip_region (MUTTER_SHAPED_TEXTURE (priv->actor),
|
|
texture_clip_region);
|
|
}
|
|
|
|
/**
|
|
* mutter_window_set_visible_region_beneath:
|
|
* @self: a #MutterWindow
|
|
* @visible_region: the region of the screen that isn't completely
|
|
* obscured beneath the main window texture.
|
|
*
|
|
* Provides a hint as to what areas need to be drawn *beneath*
|
|
* the main window texture. This is the relevant visible region
|
|
* when drawing the shadow, properly accounting for areas of the
|
|
* shadow hid by the window itself. This will be set before painting
|
|
* then unset afterwards.
|
|
*/
|
|
void
|
|
mutter_window_set_visible_region_beneath (MutterWindow *self,
|
|
GdkRegion *beneath_region)
|
|
{
|
|
MutterWindowPrivate *priv = self->priv;
|
|
|
|
if (priv->shadow)
|
|
{
|
|
GdkRectangle shadow_rect;
|
|
ClutterActorBox box;
|
|
GdkOverlapType overlap;
|
|
|
|
/* We could compute an full clip region as we do for the window
|
|
* texture, but the shadow is relatively cheap to draw, and
|
|
* a little more complex to clip, so we just catch the case where
|
|
* the shadow is completely obscured and doesn't need to be drawn
|
|
* at all.
|
|
*/
|
|
clutter_actor_get_allocation_box (priv->shadow, &box);
|
|
|
|
shadow_rect.x = roundf (box.x1);
|
|
shadow_rect.y = roundf (box.y1);
|
|
shadow_rect.width = roundf (box.x2 - box.x1);
|
|
shadow_rect.height = roundf (box.y2 - box.y1);
|
|
|
|
overlap = gdk_region_rect_in (beneath_region, &shadow_rect);
|
|
|
|
tidy_texture_frame_set_needs_paint (TIDY_TEXTURE_FRAME (priv->shadow),
|
|
overlap != GDK_OVERLAP_RECTANGLE_OUT);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* mutter_window_reset_visible_regions:
|
|
* @self: a #MutterWindow
|
|
*
|
|
* Unsets the regions set by mutter_window_reset_visible_region() and
|
|
*mutter_window_reset_visible_region_beneath()
|
|
*/
|
|
void
|
|
mutter_window_reset_visible_regions (MutterWindow *self)
|
|
{
|
|
MutterWindowPrivate *priv = self->priv;
|
|
|
|
mutter_shaped_texture_set_clip_region (MUTTER_SHAPED_TEXTURE (priv->actor),
|
|
NULL);
|
|
if (priv->shadow)
|
|
tidy_texture_frame_set_needs_paint (TIDY_TEXTURE_FRAME (priv->shadow), TRUE);
|
|
}
|
|
|
|
static void
|
|
check_needs_repair (MutterWindow *self)
|
|
{
|
|
MutterWindowPrivate *priv = self->priv;
|
|
MetaScreen *screen = priv->screen;
|
|
MetaDisplay *display = meta_screen_get_display (screen);
|
|
Display *xdisplay = meta_display_get_xdisplay (display);
|
|
MetaCompScreen *info = meta_screen_get_compositor_data (screen);
|
|
MetaCompositor *compositor;
|
|
Window xwindow = priv->xwindow;
|
|
gboolean full = FALSE;
|
|
|
|
if (!priv->needs_repair)
|
|
return;
|
|
|
|
if (!priv->mapped)
|
|
return;
|
|
|
|
if (xwindow == meta_screen_get_xroot (screen) ||
|
|
xwindow == clutter_x11_get_stage_window (CLUTTER_STAGE (info->stage)))
|
|
return;
|
|
|
|
compositor = meta_display_get_compositor (display);
|
|
|
|
if (priv->size_changed)
|
|
{
|
|
mutter_window_detach (self);
|
|
priv->size_changed = FALSE;
|
|
}
|
|
|
|
meta_error_trap_push (display);
|
|
|
|
if (priv->back_pixmap == None)
|
|
{
|
|
gint pxm_width, pxm_height;
|
|
|
|
meta_error_trap_push (display);
|
|
|
|
priv->back_pixmap = XCompositeNameWindowPixmap (xdisplay, xwindow);
|
|
|
|
if (meta_error_trap_pop_with_return (display, FALSE) != 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.)
|
|
*/
|
|
priv->back_pixmap = None;
|
|
}
|
|
|
|
if (priv->back_pixmap == None)
|
|
{
|
|
meta_verbose ("Unable to get named pixmap for %p\n", self);
|
|
mutter_window_update_bounding_region (self, 0, 0);
|
|
return;
|
|
}
|
|
|
|
/* MUST call before setting pixmap or serious performance issues
|
|
* seemingly caused by cogl_texture_set_filters() in set_filter
|
|
* Not sure if that call is actually needed.
|
|
*/
|
|
if (!compositor->no_mipmaps)
|
|
clutter_texture_set_filter_quality (CLUTTER_TEXTURE (priv->actor),
|
|
CLUTTER_TEXTURE_QUALITY_HIGH );
|
|
|
|
clutter_x11_texture_pixmap_set_pixmap
|
|
(CLUTTER_X11_TEXTURE_PIXMAP (priv->actor),
|
|
priv->back_pixmap);
|
|
|
|
g_object_get (priv->actor,
|
|
"pixmap-width", &pxm_width,
|
|
"pixmap-height", &pxm_height,
|
|
NULL);
|
|
|
|
if (priv->shadow)
|
|
clutter_actor_set_size (priv->shadow, pxm_width, pxm_height);
|
|
|
|
mutter_window_update_bounding_region (self, pxm_width, pxm_height);
|
|
|
|
full = TRUE;
|
|
}
|
|
|
|
/*
|
|
* TODO -- on some gfx hardware updating the whole texture instead of
|
|
* the individual rectangles is actually quicker, so we might want to
|
|
* make this a configurable option (on desktop HW with multiple pipelines
|
|
* it is usually quicker to just update the damaged parts).
|
|
*
|
|
* If we are using TFP we update the whole texture (this simply trigers
|
|
* the texture rebind).
|
|
*/
|
|
if (full
|
|
#ifdef HAVE_GLX_TEXTURE_PIXMAP
|
|
|| (CLUTTER_GLX_IS_TEXTURE_PIXMAP (priv->actor) &&
|
|
clutter_glx_texture_pixmap_using_extension
|
|
(CLUTTER_GLX_TEXTURE_PIXMAP (priv->actor)))
|
|
#endif /* HAVE_GLX_TEXTURE_PIXMAP */
|
|
)
|
|
{
|
|
XDamageSubtract (xdisplay, priv->damage, None, None);
|
|
|
|
clutter_x11_texture_pixmap_update_area
|
|
(CLUTTER_X11_TEXTURE_PIXMAP (priv->actor),
|
|
0,
|
|
0,
|
|
clutter_actor_get_width (priv->actor),
|
|
clutter_actor_get_height (priv->actor));
|
|
}
|
|
else
|
|
{
|
|
XRectangle *r_damage;
|
|
XRectangle r_bounds;
|
|
XserverRegion parts;
|
|
int i, r_count;
|
|
|
|
parts = XFixesCreateRegion (xdisplay, 0, 0);
|
|
XDamageSubtract (xdisplay, priv->damage, None, parts);
|
|
|
|
r_damage = XFixesFetchRegionAndBounds (xdisplay,
|
|
parts,
|
|
&r_count,
|
|
&r_bounds);
|
|
|
|
if (r_damage)
|
|
{
|
|
for (i = 0; i < r_count; ++i)
|
|
{
|
|
clutter_x11_texture_pixmap_update_area
|
|
(CLUTTER_X11_TEXTURE_PIXMAP (priv->actor),
|
|
r_damage[i].x,
|
|
r_damage[i].y,
|
|
r_damage[i].width,
|
|
r_damage[i].height);
|
|
}
|
|
}
|
|
|
|
XFree (r_damage);
|
|
XFixesDestroyRegion (xdisplay, parts);
|
|
}
|
|
|
|
meta_error_trap_pop (display, FALSE);
|
|
|
|
priv->needs_repair = FALSE;
|
|
}
|
|
|
|
void
|
|
mutter_window_process_damage (MutterWindow *self,
|
|
XDamageNotifyEvent *event)
|
|
{
|
|
mutter_window_mark_for_repair (self);
|
|
}
|
|
|
|
void
|
|
mutter_window_sync_visibility (MutterWindow *self)
|
|
{
|
|
MutterWindowPrivate *priv = self->priv;
|
|
|
|
if (CLUTTER_ACTOR_IS_VISIBLE (self) != priv->visible)
|
|
{
|
|
if (priv->visible)
|
|
clutter_actor_show (CLUTTER_ACTOR (self));
|
|
else
|
|
clutter_actor_hide (CLUTTER_ACTOR (self));
|
|
}
|
|
}
|
|
|
|
static void
|
|
check_needs_reshape (MutterWindow *self)
|
|
{
|
|
MutterWindowPrivate *priv = self->priv;
|
|
|
|
if (!priv->needs_reshape)
|
|
return;
|
|
|
|
mutter_shaped_texture_clear_rectangles (MUTTER_SHAPED_TEXTURE (priv->actor));
|
|
mutter_window_clear_shape_region (self);
|
|
|
|
#ifdef HAVE_SHAPE
|
|
if (priv->shaped)
|
|
{
|
|
Display *xdisplay = meta_display_get_xdisplay (priv->window->display);
|
|
XRectangle *rects;
|
|
int n_rects, ordering;
|
|
|
|
rects = XShapeGetRectangles (xdisplay,
|
|
priv->xwindow,
|
|
ShapeBounding,
|
|
&n_rects,
|
|
&ordering);
|
|
|
|
if (rects)
|
|
{
|
|
mutter_shaped_texture_add_rectangles (MUTTER_SHAPED_TEXTURE (priv->actor),
|
|
n_rects, rects);
|
|
|
|
mutter_window_update_shape_region (self, n_rects, rects);
|
|
|
|
XFree (rects);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
priv->needs_reshape = FALSE;
|
|
}
|
|
|
|
void
|
|
mutter_window_update_shape (MutterWindow *self,
|
|
gboolean shaped)
|
|
{
|
|
MutterWindowPrivate *priv = self->priv;
|
|
|
|
priv->shaped = shaped;
|
|
priv->needs_reshape = TRUE;
|
|
|
|
clutter_actor_queue_redraw (priv->actor);
|
|
}
|
|
|
|
void
|
|
mutter_window_pre_paint (MutterWindow *self)
|
|
{
|
|
MutterWindowPrivate *priv = self->priv;
|
|
|
|
/* The window is frozen due to a pending animation: we'll wait until
|
|
* the animation finishes to reshape and repair the window */
|
|
if (priv->destroy_in_progress ||
|
|
priv->maximize_in_progress ||
|
|
priv->unmaximize_in_progress)
|
|
return;
|
|
|
|
check_needs_reshape (self);
|
|
check_needs_repair (self);
|
|
}
|
|
|
|
void
|
|
mutter_window_update_opacity (MutterWindow *self)
|
|
{
|
|
MutterWindowPrivate *priv = self->priv;
|
|
MetaDisplay *display = meta_screen_get_display (priv->screen);
|
|
MetaCompositor *compositor = meta_display_get_compositor (display);
|
|
Window xwin = meta_window_get_xwindow (priv->window);
|
|
gulong value;
|
|
guint8 opacity;
|
|
|
|
if (meta_prop_get_cardinal (display, xwin,
|
|
compositor->atom_net_wm_window_opacity,
|
|
&value))
|
|
{
|
|
opacity = (guint8)((gfloat)value * 255.0 / ((gfloat)0xffffffff));
|
|
}
|
|
else
|
|
opacity = 255;
|
|
|
|
self->priv->opacity = opacity;
|
|
clutter_actor_set_opacity (CLUTTER_ACTOR (self), opacity);
|
|
}
|