mutter/src/compositor/mutter-window.c
Thomas Wood 1f90529365 Don't explicitly set the size of the MutterWindow texture
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.
2009-07-31 13:36:59 +01:00

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