diff options
Diffstat (limited to 'matchbox/comp-mgr/mb-wm-comp-mgr-clutter.c')
-rw-r--r-- | matchbox/comp-mgr/mb-wm-comp-mgr-clutter.c | 1357 |
1 files changed, 1357 insertions, 0 deletions
diff --git a/matchbox/comp-mgr/mb-wm-comp-mgr-clutter.c b/matchbox/comp-mgr/mb-wm-comp-mgr-clutter.c new file mode 100644 index 0000000..bfc7dba --- /dev/null +++ b/matchbox/comp-mgr/mb-wm-comp-mgr-clutter.c @@ -0,0 +1,1357 @@ +/* + * Matchbox Window Manager - A lightweight window manager not for the + * desktop. + * + * Authored By Tomas Frydrych <tf@o-hand.com> + * + * Copyright (c) 2008 OpenedHand Ltd - http://o-hand.com + * + * 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, 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. + * + */ + +#include "mb-wm.h" +#include "mb-wm-client.h" +#include "mb-wm-comp-mgr.h" +#include "mb-wm-comp-mgr-clutter.h" +#include "mb-wm-theme.h" + +#include <clutter/clutter.h> +#include <clutter/clutter-x11.h> +#include <clutter/clutter-x11-texture-pixmap.h> +#include <clutter/clutter-glx-texture-pixmap.h> + +#include <X11/Xresource.h> +#include <X11/extensions/Xdamage.h> +#include <X11/extensions/Xrender.h> +#include <X11/extensions/Xcomposite.h> +#include <X11/extensions/shape.h> + +static void +mb_wm_comp_mgr_clutter_add_actor (MBWMCompMgrClutter * , ClutterActor *); + +/* + * A helper object to store manager's per-client data + */ +struct MBWMCompMgrClutterClient +{ + MBWMCompMgrClient parent; + ClutterActor * actor; + Pixmap pixmap; + int pxm_width; + int pxm_height; + int pxm_depth; + Bool mapped; + Bool dont_update; + Damage damage; + + /* 1-based array holding timelines for effect events */ + ClutterTimeline * timelines[_MBWMCompMgrEffectEventLast-1]; +}; + +static void +mb_wm_comp_mgr_clutter_client_show_real (MBWMCompMgrClient * client); + +static void +mb_wm_comp_mgr_clutter_client_hide_real (MBWMCompMgrClient * client); + +static void +mb_wm_comp_mgr_clutter_client_repair_real (MBWMCompMgrClient * client); + +static void +mb_wm_comp_mgr_clutter_client_configure_real (MBWMCompMgrClient * client); + +static MBWMCompMgrEffect * +mb_wm_comp_mgr_clutter_client_effect_new_real (MBWMCompMgrClient *client, + MBWMCompMgrEffectEvent event, + MBWMCompMgrEffectType type, + unsigned long duration, + MBWMGravity gravity); +static void +mb_wm_comp_mgr_clutter_client_class_init (MBWMObjectClass *klass) +{ + MBWMCompMgrClientClass *c_klass = MB_WM_COMP_MGR_CLIENT_CLASS (klass); + + c_klass->show = mb_wm_comp_mgr_clutter_client_show_real; + c_klass->hide = mb_wm_comp_mgr_clutter_client_hide_real; + c_klass->repair = mb_wm_comp_mgr_clutter_client_repair_real; + c_klass->configure = mb_wm_comp_mgr_clutter_client_configure_real; + c_klass->effect_new = mb_wm_comp_mgr_clutter_client_effect_new_real; + +#ifdef MBWM_WANT_DEBUG + klass->klass_name = "MBWMCompMgrClutterClient"; +#endif +} + +/* + * Fetch the entire texture for our client + */ +static void +mb_wm_comp_mgr_clutter_fetch_texture (MBWMCompMgrClient *client) +{ + MBWMCompMgrClutterClient *cclient = MB_WM_COMP_MGR_CLUTTER_CLIENT (client); + MBWindowManagerClient *wm_client = client->wm_client; + MBWindowManager *wm = wm_client->wmref; + Window xwin; + Window root; + int x, y, w, h, bw, depth; + + if (!cclient->mapped) + return; + + xwin = + wm_client->xwin_frame ? wm_client->xwin_frame : wm_client->window->xwindow; + + if (cclient->pixmap) + XFreePixmap (wm->xdpy, cclient->pixmap); + + cclient->pixmap = XCompositeNameWindowPixmap (wm->xdpy, xwin); + + if (!cclient->pixmap) + return; + + XGetGeometry (wm->xdpy, cclient->pixmap, &root, &x, &y, &w, &h, &bw, &depth); + + cclient->pxm_width = w; + cclient->pxm_height = h; + cclient->pxm_depth = depth; + + clutter_actor_set_size (cclient->actor, w, h); + + clutter_x11_texture_pixmap_set_pixmap ( + CLUTTER_X11_TEXTURE_PIXMAP (cclient->actor), + cclient->pixmap, + w, h, depth); +} + +/* + * Update region x,y,width x height in our client texture. + */ +static void +mb_wm_comp_mgr_clutter_update_texture (MBWMCompMgrClient *client, + int x, int y, int width, int height) +{ + MBWMCompMgrClutterClient *cclient = MB_WM_COMP_MGR_CLUTTER_CLIENT (client); + MBWindowManagerClient *wm_client = client->wm_client; + MBWindowManager *wm = wm_client->wmref; + + if (!cclient->mapped) + return; + + if (!cclient->pixmap) + { + mb_wm_comp_mgr_clutter_fetch_texture (client); + return; + } + + clutter_x11_texture_pixmap_update_area ( + CLUTTER_X11_TEXTURE_PIXMAP (cclient->actor), + x, y, width, height); +} + +static int +mb_wm_comp_mgr_clutter_client_init (MBWMObject *obj, va_list vap) +{ + return 1; +} + +static void +mb_wm_comp_mgr_clutter_client_destroy (MBWMObject* obj) +{ + MBWMCompMgrClient * c = MB_WM_COMP_MGR_CLIENT (obj); + MBWMCompMgrClutterClient * cc = MB_WM_COMP_MGR_CLUTTER_CLIENT (obj); + MBWindowManager * wm = c->wm_client->wmref; + + mb_wm_comp_mgr_client_hide (c); + + if (cc->actor) + g_object_unref (cc->actor); + + if (cc->pixmap) + XFreePixmap (wm->xdpy, cc->pixmap); + + if (cc->damage) + XDamageDestroy (wm->xdpy, cc->damage); +} + +int +mb_wm_comp_mgr_clutter_client_class_type () +{ + static int type = 0; + + if (UNLIKELY(type == 0)) + { + static MBWMObjectClassInfo info = { + sizeof (MBWMCompMgrClutterClientClass), + sizeof (MBWMCompMgrClutterClient), + mb_wm_comp_mgr_clutter_client_init, + mb_wm_comp_mgr_clutter_client_destroy, + mb_wm_comp_mgr_clutter_client_class_init + }; + + type = + mb_wm_object_register_class (&info, MB_WM_TYPE_COMP_MGR_CLIENT, 0); + } + + return type; +} + +/* + * This is a private method, hence static (all instances of this class are + * created automatically by the composite manager). + */ +static MBWMCompMgrClient * +mb_wm_comp_mgr_clutter_client_new (MBWindowManagerClient * client) +{ + MBWMObject *c; + + c = mb_wm_object_new (MB_WM_TYPE_COMP_MGR_CLUTTER_CLIENT, + MBWMObjectPropClient, client, + NULL); + + return MB_WM_COMP_MGR_CLIENT (c); +} + +static void +mb_wm_comp_mgr_clutter_client_hide_real (MBWMCompMgrClient * client) +{ + MBWMCompMgrClutterClient * cclient = MB_WM_COMP_MGR_CLUTTER_CLIENT (client); + + clutter_actor_hide (cclient->actor); +} + +/* + * Helper function for manipulating damage. Gets the extents of this client, + * expressed as XserverRegion. + */ +static XserverRegion +mb_wm_comp_mgr_clutter_client_extents (MBWMCompMgrClient *client) +{ + MBWindowManagerClient *wm_client = client->wm_client; + MBWindowManager *wm = wm_client->wmref; + MBGeometry geom; + XRectangle r; + XserverRegion extents; + + mb_wm_client_get_coverage (wm_client, &geom); + + r.x = geom.x; + r.y = geom.y; + r.width = geom.width; + r.height = geom.height; + + extents = XFixesCreateRegion (wm->xdpy, &r, 1); + + return extents; +} + +/* + * Adds region damage to the overall damage of the client. + */ +static void +mb_wm_comp_mgr_clutter_client_add_damage (MBWMCompMgrClutterClient * cclient, + XserverRegion damage) +{ + MBWindowManagerClient *wm_client = + MB_WM_COMP_MGR_CLIENT (cclient)->wm_client; + MBWindowManager *wm = wm_client->wmref; + + XFixesUnionRegion (wm->xdpy, + cclient->damage, + cclient->damage, + damage); + + XFixesDestroyRegion (wm->xdpy, damage); + + mb_wm_display_sync_queue (wm, MBWMSyncVisibility); +} + +/* + * Does all the work we need to show a client, except for calling + * clutter_actor_show () + */ +static void +mb_wm_comp_mgr_clutter_client_show_internal (MBWMCompMgrClient * client) +{ + MBWMCompMgrClutterClient * cclient = MB_WM_COMP_MGR_CLUTTER_CLIENT (client); + MBGeometry geom; + XserverRegion region; + MBWindowManagerClient *wm_client = + MB_WM_COMP_MGR_CLIENT (cclient)->wm_client; + MBWindowManager *wm = wm_client->wmref; + + MBWM_NOTE (COMPOSITOR, "showing client"); + + if (!cclient->actor) + { + /* + * This can happen if show() is called on our client before it is + * actually mapped (we only alocate the actor in response to map + * notification. + */ + return; + } + + if (!cclient->damage) + cclient->damage = XDamageCreate (wm->xdpy, + wm_client->xwin_frame ? + wm_client->xwin_frame : + wm_client->window->xwindow, + XDamageReportNonEmpty); + + /* + * FIXME -- is it really necessary to invalidate the entire client area + * here ? + */ + region = mb_wm_comp_mgr_clutter_client_extents (client); + + mb_wm_comp_mgr_clutter_client_add_damage (cclient, region); + +} + +static void +mb_wm_comp_mgr_clutter_client_show_real (MBWMCompMgrClient * client) +{ + MBWMCompMgrClutterClient * cclient = MB_WM_COMP_MGR_CLUTTER_CLIENT (client); + + if (!cclient->actor) + { + /* + * This can happen if show() is called on our client before it is + * actually mapped (we only alocate the actor in response to map + * notification. + */ + return; + } + + mb_wm_comp_mgr_clutter_client_show_internal (client); + clutter_actor_show (cclient->actor); +} + +static MBWMCompMgrEffect * +mb_wm_comp_mgr_clutter_effect_new (MBWMCompMgrEffectType type, + unsigned long duration, + MBWMGravity gravity, + ClutterTimeline * timeline, + ClutterBehaviour * behaviour); + +/* + * Helper method to get a timeline for given event (all effects associated with + * the same event share a single timeline. + */ +static ClutterTimeline * +mb_wm_comp_mgr_clutter_client_get_timeline (MBWMCompMgrClient *client, + MBWMCompMgrEffectEvent event, + unsigned long duration) +{ + MBWMCompMgrClutterClient * cclient = MB_WM_COMP_MGR_CLUTTER_CLIENT (client); + + if (!client || event >= _MBWMCompMgrEffectEventLast) + return NULL; + + if (!cclient->timelines[event-1]) + { + cclient->timelines[event-1] = + clutter_timeline_new_for_duration (duration); + } + + return cclient->timelines[event-1]; +} + +/* + * Implementation of the MBWMCompMgrClient->effect_new() method. + */ +static MBWMCompMgrEffect * +mb_wm_comp_mgr_clutter_client_effect_new_real (MBWMCompMgrClient *client, + MBWMCompMgrEffectEvent event, + MBWMCompMgrEffectType type, + unsigned long duration, + MBWMGravity gravity) +{ + MBWMCompMgrEffect * eff; + ClutterTimeline * timeline; + ClutterBehaviour * behaviour; + ClutterAlpha * alpha; + MBWMCompMgrClutterClient * cclient = MB_WM_COMP_MGR_CLUTTER_CLIENT (client); + MBWindowManagerClient * wm_client = client->wm_client; + MBWindowManager * wm = wm_client->wmref; + ClutterKnot knots[2]; + MBGeometry geom; + + timeline = + mb_wm_comp_mgr_clutter_client_get_timeline (client, event, duration); + + if (!timeline) + return NULL; + + alpha = clutter_alpha_new_full (timeline, + CLUTTER_ALPHA_RAMP_INC, NULL, NULL); + + mb_wm_client_get_coverage (wm_client, &geom); + + switch (type) + { + case MBWMCompMgrEffectScaleUp: + behaviour = + clutter_behaviour_scale_newx (alpha, 0, 0, CFX_ONE, CFX_ONE); + break; + case MBWMCompMgrEffectScaleDown: + behaviour = + clutter_behaviour_scale_newx (alpha, CFX_ONE, CFX_ONE, 0, 0); + break; + case MBWMCompMgrEffectSpinXCW: + behaviour = clutter_behaviour_rotate_newx (alpha, + CLUTTER_X_AXIS, + CLUTTER_ROTATE_CW, + 0, + CLUTTER_INT_TO_FIXED (360)); + break; + case MBWMCompMgrEffectSpinXCCW: + behaviour = clutter_behaviour_rotate_newx (alpha, + CLUTTER_X_AXIS, + CLUTTER_ROTATE_CCW, + 0, + CLUTTER_INT_TO_FIXED (360)); + break; + case MBWMCompMgrEffectSpinYCW: + behaviour = clutter_behaviour_rotate_newx (alpha, + CLUTTER_Y_AXIS, + CLUTTER_ROTATE_CW, + 0, + CLUTTER_INT_TO_FIXED (360)); + break; + case MBWMCompMgrEffectSpinYCCW: + behaviour = clutter_behaviour_rotate_newx (alpha, + CLUTTER_Y_AXIS, + CLUTTER_ROTATE_CCW, + 0, + CLUTTER_INT_TO_FIXED (360)); + break; + case MBWMCompMgrEffectSpinZCW: + behaviour = clutter_behaviour_rotate_newx (alpha, + CLUTTER_Z_AXIS, + CLUTTER_ROTATE_CW, + 0, + CLUTTER_INT_TO_FIXED (360)); + break; + case MBWMCompMgrEffectSpinZCCW: + behaviour = clutter_behaviour_rotate_newx (alpha, + CLUTTER_Z_AXIS, + CLUTTER_ROTATE_CCW, + 0, + CLUTTER_INT_TO_FIXED (360)); + break; + case MBWMCompMgrEffectFade: + behaviour = clutter_behaviour_opacity_new (alpha, 0xff, 0); + break; + case MBWMCompMgrEffectUnfade: + behaviour = clutter_behaviour_opacity_new (alpha, 0, 0xff); + break; + + /* + * Currently ClutterBehaviourPath does not handle negative coords, + * so anything here that needs them is broken. + */ + case MBWMCompMgrEffectSlideIn: + switch (gravity) + { + case MBWMGravityNorth: + knots[0].x = geom.x; + knots[0].y = wm->xdpy_height; + knots[1].x = geom.x; + knots[1].y = geom.y; + break; + case MBWMGravitySouth: + knots[0].x = geom.x; + knots[0].y = -geom.height; + knots[1].x = geom.x; + knots[1].y = geom.y; + break; + case MBWMGravityWest: + knots[0].x = wm->xdpy_width; + knots[0].y = geom.y; + knots[1].x = geom.x; + knots[1].y = geom.y; + break; + case MBWMGravityEast: + knots[0].x = -geom.width; + knots[0].y = geom.y; + knots[1].x = geom.x; + knots[1].y = geom.y; + break; + case MBWMGravityNorthWest: + case MBWMGravityNone: + default: + knots[0].x = wm->xdpy_width; + knots[0].y = wm->xdpy_height; + knots[1].x = geom.x; + knots[1].y = geom.y; + break; + case MBWMGravityNorthEast: + knots[0].x = -geom.width; + knots[0].y = wm->xdpy_height; + knots[1].x = geom.x; + knots[1].y = geom.y; + break; + case MBWMGravitySouthWest: + knots[0].x = wm->xdpy_width; + knots[0].y = -geom.height; + knots[1].x = geom.x; + knots[1].y = geom.y; + break; + case MBWMGravitySouthEast: + knots[0].x = -geom.width; + knots[0].y = -geom.height; + knots[1].x = geom.x; + knots[1].y = geom.y; + break; + } + + behaviour = clutter_behaviour_path_new (alpha, &knots[0], 2); + break; + case MBWMCompMgrEffectSlideOut: + switch (gravity) + { + case MBWMGravityNorth: + knots[0].x = geom.x; + knots[0].y = geom.y; + knots[1].x = geom.x; + knots[1].y = -(geom.y + geom.height); + break; + case MBWMGravitySouth: + knots[0].x = geom.x; + knots[0].y = geom.y; + knots[1].x = geom.x; + knots[1].y = wm->xdpy_height; + break; + case MBWMGravityEast: + knots[0].x = geom.x; + knots[0].y = geom.y; + knots[1].x = wm->xdpy_width; + knots[1].y = geom.y; + break; + case MBWMGravityWest: + knots[0].x = geom.x; + knots[0].y = geom.y; + knots[1].x = -(geom.x + geom.width); + knots[1].y = geom.y; + break; + case MBWMGravityNorthWest: + case MBWMGravityNone: + default: + knots[0].x = geom.x; + knots[0].y = geom.y; + knots[1].x = -(geom.x + geom.width); + knots[1].y = -(geom.y + geom.height); + break; + case MBWMGravityNorthEast: + knots[0].x = geom.x; + knots[0].y = geom.y; + knots[1].x = wm->xdpy_width; + knots[1].y = -(geom.y + geom.height); + break; + case MBWMGravitySouthWest: + knots[0].x = geom.x; + knots[0].y = geom.y; + knots[1].x = -(geom.x + geom.width); + knots[1].y = wm->xdpy_height; + break; + case MBWMGravitySouthEast: + knots[0].x = geom.x; + knots[0].y = geom.y; + knots[1].x = wm->xdpy_width; + knots[1].y = wm->xdpy_height; + break; + } + + behaviour = clutter_behaviour_path_new (alpha, &knots[0], 2); + } + + eff = + mb_wm_comp_mgr_clutter_effect_new (type, duration, gravity, + timeline, behaviour); + + if (eff) + { + /* + * We assume that the actor already exists -- if not, clutter will + * issue a warning here + */ + clutter_behaviour_apply (behaviour, cclient->actor); + } + + return eff; +} + +/* + * Implementation of MBWMCompMgrClutter + */ +struct MBWMCompMgrClutterPrivate +{ + ClutterActor * stage; + Window overlay_window; + int damage_event; +}; + +static void +mb_wm_comp_mgr_clutter_private_free (MBWMCompMgrClutter *mgr) +{ + MBWMCompMgrClutterPrivate * priv = mgr->priv; + free (priv); +} + +static void +mb_wm_comp_mgr_clutter_register_client_real (MBWMCompMgr * mgr, + MBWindowManagerClient * c) +{ + MBWMCompMgrClient *cc; + MBWMCompMgrClutter *cmgr = MB_WM_COMP_MGR_CLUTTER (mgr); + + if (c->cm_client) + return; + + cc = mb_wm_comp_mgr_clutter_client_new (c); + c->cm_client = cc; +} + +static void +mb_wm_comp_mgr_clutter_unregister_client_real (MBWMCompMgr * mgr, + MBWindowManagerClient * client) +{ + if (!client || !client->cm_client) + return; + + mb_wm_object_unref (MB_WM_OBJECT (client->cm_client)); + client->cm_client = NULL; +} + +static void +mb_wm_comp_mgr_clutter_turn_on_real (MBWMCompMgr *mgr); + +static void +mb_wm_comp_mgr_clutter_turn_off_real (MBWMCompMgr *mgr); + +static void +mb_wm_comp_mgr_clutter_render_real (MBWMCompMgr *mgr); + +static void +mb_wm_comp_mgr_clutter_map_notify_real (MBWMCompMgr *mgr, + MBWindowManagerClient *c); + +static Bool +mb_wm_comp_mgr_clutter_handle_events_real (MBWMCompMgr * mgr, XEvent *ev); + +static Bool +mb_wm_comp_mgr_is_my_window_real (MBWMCompMgr * mgr, Window xwin); + +static void +mb_wm_comp_mgr_clutter_class_init (MBWMObjectClass *klass) +{ + MBWMCompMgrClass *cm_klass = MB_WM_COMP_MGR_CLASS (klass); + +#ifdef MBWM_WANT_DEBUG + klass->klass_name = "MBWMCompMgrClutter"; +#endif + + cm_klass->register_client = mb_wm_comp_mgr_clutter_register_client_real; + cm_klass->unregister_client = mb_wm_comp_mgr_clutter_unregister_client_real; + cm_klass->turn_on = mb_wm_comp_mgr_clutter_turn_on_real; + cm_klass->turn_off = mb_wm_comp_mgr_clutter_turn_off_real; + cm_klass->render = mb_wm_comp_mgr_clutter_render_real; + cm_klass->map_notify = mb_wm_comp_mgr_clutter_map_notify_real; + cm_klass->handle_events = mb_wm_comp_mgr_clutter_handle_events_real; + cm_klass->my_window = mb_wm_comp_mgr_is_my_window_real; +} + +/* + * Initializes the extension require by the manager + */ +static Bool +mb_wm_comp_mgr_clutter_init_extensions (MBWMCompMgr *mgr) +{ + MBWindowManager * wm = mgr->wm; + MBWMCompMgrClutterPrivate * priv = MB_WM_COMP_MGR_CLUTTER (mgr)->priv; + int event_base, error_base; + int damage_error; + int xfixes_event, xfixes_error; + + if (!XCompositeQueryExtension (wm->xdpy, &event_base, &error_base)) + { + fprintf (stderr, "matchbox: No composite extension\n"); + return False; + } + + if (!XDamageQueryExtension (wm->xdpy, &priv->damage_event, &damage_error)) + { + fprintf (stderr, "matchbox: No damage extension\n"); + return False; + } + + if (!XFixesQueryExtension (wm->xdpy, &xfixes_event, &xfixes_error)) + { + fprintf (stderr, "matchbox: No XFixes extension\n"); + return False; + } + + XCompositeRedirectSubwindows (wm->xdpy, wm->root_win->xwindow, + CompositeRedirectManual); + + return True; +} + +static int +mb_wm_comp_mgr_clutter_init (MBWMObject *obj, va_list vap) +{ + MBWMCompMgr * mgr = MB_WM_COMP_MGR (obj); + MBWMCompMgrClutter * cmgr = MB_WM_COMP_MGR_CLUTTER (obj); + MBWMCompMgrClutterPrivate * priv; + + priv = mb_wm_util_malloc0 (sizeof (MBWMCompMgrClutterPrivate)); + cmgr->priv = priv; + + if (!mb_wm_comp_mgr_clutter_init_extensions (mgr)) + return 0; + + priv->stage = clutter_stage_get_default (); + + return 1; +} + +static void +mb_wm_comp_mgr_clutter_destroy (MBWMObject * obj) +{ + MBWMCompMgr * mgr = MB_WM_COMP_MGR (obj); + MBWMCompMgrClutter * cmgr = MB_WM_COMP_MGR_CLUTTER (obj); + + mb_wm_comp_mgr_turn_off (mgr); + mb_wm_comp_mgr_clutter_private_free (cmgr); +} + +int +mb_wm_comp_mgr_clutter_class_type () +{ + static int type = 0; + + if (UNLIKELY(type == 0)) + { + static MBWMObjectClassInfo info = { + sizeof (MBWMCompMgrClutterClass), + sizeof (MBWMCompMgrClutter), + mb_wm_comp_mgr_clutter_init, + mb_wm_comp_mgr_clutter_destroy, + mb_wm_comp_mgr_clutter_class_init + }; + + type = mb_wm_object_register_class (&info, MB_WM_TYPE_COMP_MGR, 0); + } + + return type; +} + +/* Shuts the compositing down */ +static void +mb_wm_comp_mgr_clutter_turn_off_real (MBWMCompMgr *mgr) +{ + MBWindowManager * wm = mgr->wm; + MBWMCompMgrClutterPrivate * priv; + + if (!mgr) + return; + + priv = MB_WM_COMP_MGR_CLUTTER (mgr)->priv; + + if (mgr->disabled) + return; + + if (!mb_wm_stack_empty (wm)) + { + MBWindowManagerClient * c; + + mb_wm_stack_enumerate (wm, c) + { + mb_wm_comp_mgr_clutter_unregister_client_real (mgr, c); + } + } + + XCompositeReleaseOverlayWindow (wm->xdpy, wm->root_win->xwindow); + priv->overlay_window = None; + + mgr->disabled = True; +} + +static void +mb_wm_comp_mgr_clutter_turn_on_real (MBWMCompMgr *mgr) +{ + MBWindowManager * wm; + MBWMCompMgrClutterPrivate * priv; + + if (!mgr || !mgr->disabled) + return; + + priv = MB_WM_COMP_MGR_CLUTTER (mgr)->priv; + wm = mgr->wm; + + mgr->disabled = False; + + if (priv->overlay_window == None) + { + ClutterActor * stage = priv->stage; +#ifdef MBWM_WANT_DEBUG + /* + * Special colour to please Iain's eyes ;) + */ + ClutterColor clr = {0xff, 0, 0xff, 0xff }; +#else + ClutterColor clr = {0, 0, 0, 0xff }; +#endif + Window xwin; + XserverRegion region; + + /* + * Fetch the overlay window + */ + xwin = clutter_x11_get_stage_window (CLUTTER_STAGE (stage)); + + priv->overlay_window = + XCompositeGetOverlayWindow (wm->xdpy, wm->root_win->xwindow); + + /* + * Reparent the stage window to the overlay window, this makes it + * magically work :) + */ + XReparentWindow (wm->xdpy, xwin, priv->overlay_window, 0, 0); + + /* + * Use xfixes shape to make events pass through the overlay window + * + * TODO -- this has certain drawbacks, notably when our client is + * tranformed (rotated, scaled, etc), the events will not be landing in + * the right place. The answer to that is event forwarding with + * translation. + */ + region = XFixesCreateRegion (wm->xdpy, NULL, 0); + + XFixesSetWindowShapeRegion (wm->xdpy, priv->overlay_window, + ShapeBounding, 0, 0, 0); + XFixesSetWindowShapeRegion (wm->xdpy, priv->overlay_window, + ShapeInput, 0, 0, region); + + XFixesDestroyRegion (wm->xdpy, region); + + clutter_actor_set_size (stage, wm->xdpy_width, wm->xdpy_height); + clutter_stage_set_color (CLUTTER_STAGE (stage), &clr); + + clutter_actor_show (stage); + } + + /* + * Take care of any pre-existing windows + */ + if (!mb_wm_stack_empty (wm)) + { + MBWindowManagerClient * c; + + mb_wm_stack_enumerate (wm, c) + { + mb_wm_comp_mgr_clutter_register_client_real (mgr, c); + + /* + * Need to call map_notify here manually, since we will have missed + * the original map notification when this client mapped, and + * we relly on it to create our actor. + */ + mb_wm_comp_mgr_clutter_map_notify_real (mgr, c); + } + } +} + +static void +mb_wm_comp_mgr_clutter_client_repair_real (MBWMCompMgrClient * client) +{ + MBWindowManagerClient * wm_client = client->wm_client; + MBWMCompMgrClutterClient * cclient = MB_WM_COMP_MGR_CLUTTER_CLIENT (client); + + MBWM_NOTE (COMPOSITOR, "REPAIRING %x", wm_client->window->xwindow); + + if (!cclient->actor) + return; + + XDamageSubtract (wm_client->wmref->xdpy, + cclient->damage, None, None ); + + mb_wm_comp_mgr_clutter_fetch_texture (client); +} + +static void +mb_wm_comp_mgr_clutter_client_configure_real (MBWMCompMgrClient * client) +{ + MBWindowManagerClient * wm_client = client->wm_client; + MBWMCompMgrClutterClient * cclient = MB_WM_COMP_MGR_CLUTTER_CLIENT (client); + + MBWM_NOTE (COMPOSITOR, "CONFIGURE request"); + + mb_wm_comp_mgr_clutter_fetch_texture (client); +} + +static Bool +mb_wm_comp_mgr_clutter_handle_events_real (MBWMCompMgr * mgr, XEvent *ev) +{ + MBWMCompMgrClutterPrivate * priv = MB_WM_COMP_MGR_CLUTTER (mgr)->priv; + MBWindowManager * wm = mgr->wm; + + if (ev->type == priv->damage_event + XDamageNotify) + { + XDamageNotifyEvent * de = (XDamageNotifyEvent*) ev; + MBWindowManagerClient * c; + + c = mb_wm_managed_client_from_frame (wm, de->drawable); + + if (c && c->cm_client) + { + XserverRegion parts; + int i, r_count; + XRectangle * r_damage; + XRectangle r_bounds; + + MBWMCompMgrClutterClient *cclient = + MB_WM_COMP_MGR_CLUTTER_CLIENT (c->cm_client); + + if (!cclient->actor || cclient->dont_update) + return False; + + /* FIXME -- see if we can make some use of the 'more' parameter + * to compress the damage events + */ + MBWM_NOTE (COMPOSITOR, + "Reparing window %x, geometry %d,%d;%dx%d; more %d\n", + de->drawable, + de->geometry.x, + de->geometry.y, + de->geometry.width, + de->geometry.height, + de->more); + + /* + * Retrieve the damaged region and break it down into individual + * rectangles so we do not have to update the whole shebang. + */ + parts = XFixesCreateRegion (wm->xdpy, 0, 0); + XDamageSubtract (wm->xdpy, de->damage, None, parts); + + r_damage = XFixesFetchRegionAndBounds (wm->xdpy, parts, + &r_count, + &r_bounds); + + if (r_damage) + { + for (i = 0; i < r_count; ++i) + { + mb_wm_comp_mgr_clutter_update_texture (c->cm_client, + r_damage[i].x, + r_damage[i].y, + r_damage[i].width, + r_damage[i].height); + } + + XFree (r_damage); + } + + XFixesDestroyRegion (wm->xdpy, parts); + } + else + { + MBWM_NOTE (COMPOSITOR, "Failed to find client for window %x\n", + de->drawable); + } + } + + return False; +} + +static void +mb_wm_comp_mgr_clutter_render_real (MBWMCompMgr *mgr) +{ + MBWMCompMgrClutterPrivate * priv = MB_WM_COMP_MGR_CLUTTER (mgr)->priv; + MBWindowManagerClient * wm_client; + MBWindowManager * wm = mgr->wm; + + MBWM_NOTE (COMPOSITOR, "Rendering"); + + /* + * We do not need to do anything, as rendering is done automatically for us + * by clutter stage. + */ +} + +static void +mb_wm_map_effect_completed (void *data) +{ + ClutterActor * a = data; + + clutter_actor_show (a); +} + +static void +mb_wm_comp_mgr_clutter_map_notify_real (MBWMCompMgr *mgr, + MBWindowManagerClient *c) +{ + MBWMCompMgrClutter * cmgr = MB_WM_COMP_MGR_CLUTTER (mgr); + MBWMCompMgrClient * client = c->cm_client; + MBWMCompMgrClutterClient * cclient = MB_WM_COMP_MGR_CLUTTER_CLIENT (client); + ClutterActor *actor; + MBGeometry geom; + const MBWMList * l; + + /* + * We get called for windows as well as their children, so once we are + * mapped do nothing. + */ + if (cclient->mapped) + return; + + cclient->mapped = True; + actor = g_object_ref (clutter_x11_texture_pixmap_new ()); + cclient->actor = actor; + + l = + mb_wm_theme_get_client_effects (c->wmref->theme, c); + + while (l) + { + MBWMThemeEffects * t_effects = l->data; + MBWMList * m_effects; + + m_effects = mb_wm_comp_mgr_client_get_effects (client, + t_effects->event, + t_effects->type, + t_effects->duration, + t_effects->gravity); + + mb_wm_comp_mgr_client_add_effects (client, t_effects->event, m_effects); + l = l->next; + } + + + g_object_set_data (G_OBJECT (actor), "MBWMCompMgrClutterClient", cclient); + + mb_wm_comp_mgr_clutter_fetch_texture (MB_WM_COMP_MGR_CLIENT (cclient)); + + mb_wm_client_get_coverage (c, &geom); + clutter_actor_set_position (actor, geom.x, geom.y); + + mb_wm_comp_mgr_clutter_add_actor (cmgr, actor); + + mb_wm_comp_mgr_clutter_client_show_internal (c->cm_client); + + /* + * Run map event effect *before* we call show() on the actor + * (the effect will take care of showing the actor, and if not, show() gets + * called from the completed callback). + */ + mb_wm_comp_mgr_client_run_effect (c->cm_client, + MBWMCompMgrEffectEventMap, + mb_wm_map_effect_completed, + actor); +} + +/* + * Our windows which we need the WM to ingore are the overlay and the stage + * window. + */ +static Bool +mb_wm_comp_mgr_is_my_window_real (MBWMCompMgr * mgr, Window xwin) +{ + MBWMCompMgrClutterPrivate * priv = MB_WM_COMP_MGR_CLUTTER (mgr)->priv; + + if (priv->overlay_window == xwin) + return True; + + if (priv->stage && + (xwin == clutter_x11_get_stage_window (CLUTTER_STAGE (priv->stage)))) + return True; + + return False; +} + +static void +mb_wm_comp_mgr_clutter_remove_actor (MBWMCompMgrClutter * cmgr, + ClutterActor * a) +{ + clutter_container_remove_actor (CLUTTER_CONTAINER (cmgr->priv->stage), a); +} + +static void +mb_wm_comp_mgr_clutter_add_actor (MBWMCompMgrClutter * cmgr, ClutterActor * a) +{ + clutter_container_add_actor (CLUTTER_CONTAINER (cmgr->priv->stage), a); +} + +MBWMCompMgr * +mb_wm_comp_mgr_clutter_new (MBWindowManager *wm) +{ + MBWMObject *mgr; + + mgr = mb_wm_object_new (MB_WM_TYPE_COMP_MGR_CLUTTER, + MBWMObjectPropWm, wm, + NULL); + + return MB_WM_COMP_MGR (mgr); +} + +/* + * MBWMCompMgrClutterEffect + */ +struct MBWMCompMgrClutterEffect +{ + MBWMCompMgrEffect parent; + ClutterTimeline *timeline; + ClutterBehaviour *behaviour; /* can be either behaviour or effect */ +}; + +struct completed_cb_data +{ + MBWMCompMgrEffectCallback completed_cb; + void * cb_data; + gulong my_id; +}; + + +/* + * Callback for ClutterTimeline::completed signal. + * + * One-off; get connected when the timeline is started, and disconnected + * again when it finishes. + */ +static void +mb_wm_comp_mgr_clutter_effect_completed_cb (ClutterTimeline * t, void * data) +{ + struct completed_cb_data * d = data; + + if (d->completed_cb) + d->completed_cb (d->cb_data); + + g_signal_handler_disconnect (t, d->my_id); + + free (d); +} + +static void +mb_wm_comp_mgr_clutter_effect_run_real (MBWMList * effects, + MBWMCompMgrClient * cm_client, + MBWMCompMgrEffectEvent event, + MBWMCompMgrEffectCallback completed_cb, + void * data) +{ + /* + * Since the entire effect group for a single event type shares + * a timeline, we just need to start it for one of the behaviours. + * + * TODO -- there is no need for the effect objects to carry the timeline + * pointer, so remove it. + */ + if (effects && effects->data) + { + MBWMCompMgrEffect * eff = effects->data; + MBWMCompMgrClutterEffect * ceff = MB_WM_COMP_MGR_CLUTTER_EFFECT (eff); + + /* + * Don't attempt to start the timeline if it is already playing + */ + if (ceff->timeline && + !clutter_timeline_is_playing (ceff->timeline)) + { + GSList * actors; + ClutterActor *a; + + if (completed_cb) + { + struct completed_cb_data * d = + mb_wm_util_malloc0 (sizeof (struct completed_cb_data)); + + d->completed_cb = completed_cb; + d->cb_data = data; + + d->my_id = g_signal_connect (ceff->timeline, "completed", + G_CALLBACK (mb_wm_comp_mgr_clutter_effect_completed_cb), + d); + } + + /* + * This is bit of a pain; we know that our actor is attached to + * a single actor, but the current API only provides us with a copy + * of the actor list; ideally, we would like to be able to access + * the first actor directly. + */ + actors = clutter_behaviour_get_actors (ceff->behaviour); + a = actors->data; + + /* + * Deal with gravity, but not for path behaviours (there the + * gravity translates into the path itself, and is already + * set up). + */ + if (eff->gravity != CLUTTER_GRAVITY_NONE && + !CLUTTER_IS_BEHAVIOUR_PATH (ceff->behaviour)) + { + clutter_actor_move_anchor_point_from_gravity (a, eff->gravity); + } + else if (CLUTTER_IS_BEHAVIOUR_PATH (ceff->behaviour) && + !CLUTTER_ACTOR_IS_VISIBLE (ceff->behaviour)) + { + /* + * At this stage, if the actor is not yet visible, move it to + * the starting point of the path (this is mostly because of + * 'map' effects, where the clutter_actor_show () is delayed + * until this point, so that the actor can be positioned in the + * correct location without visible artefacts). + * + * FIXME -- this is very clumsy; we need clutter API to query + * the first knot of the path to avoid messing about with copies + * of the list. + */ + GSList * knots = + clutter_behaviour_path_get_knots ( + CLUTTER_BEHAVIOUR_PATH (ceff->behaviour)); + + if (knots) + { + ClutterKnot * k = knots->data; + clutter_actor_set_position (a, k->x, k->y); + + g_slist_free (knots); + } + } + + if (event == MBWMCompMgrEffectEventUnmap) + { + MBWMCompMgrClutterClient *c = + MB_WM_COMP_MGR_CLUTTER_CLIENT (cm_client); + + c->dont_update = True; + } + + /* + * Make sure the actor is showing (for example with 'map' effects, + * the show() is delayed until the effect had chance to + * set up the actor postion). + */ + clutter_actor_show (a); + + g_slist_free (actors); + + clutter_timeline_start (ceff->timeline); + } + } +} + +static void +mb_wm_comp_mgr_clutter_effect_class_init (MBWMObjectClass *klass) +{ + MBWMCompMgrEffectClass *c_klass = MB_WM_COMP_MGR_EFFECT_CLASS (klass); + + c_klass->run = mb_wm_comp_mgr_clutter_effect_run_real; + +#ifdef MBWM_WANT_DEBUG + klass->klass_name = "MBWMCompMgrClutterEffect"; +#endif +} + +static int +mb_wm_comp_mgr_clutter_effect_init (MBWMObject *obj, va_list vap) +{ + MBWMObjectProp prop; + ClutterTimeline *timeline; + ClutterBehaviour *behaviour; + + prop = va_arg(vap, MBWMObjectProp); + while (prop) + { + switch (prop) + { + case MBWMObjectPropCompMgrClutterEffectTimeline: + timeline = va_arg(vap, ClutterTimeline *); + break; + case MBWMObjectPropCompMgrClutterEffectBehaviour: + behaviour = va_arg(vap, ClutterBehaviour *); + break; + default: + MBWMO_PROP_EAT (vap, prop); + } + + prop = va_arg(vap, MBWMObjectProp); + } + + if (!timeline || !behaviour) + return 0; + + MB_WM_COMP_MGR_CLUTTER_EFFECT (obj)->timeline = timeline; + MB_WM_COMP_MGR_CLUTTER_EFFECT (obj)->behaviour = behaviour; + + return 1; +} + +static void +mb_wm_comp_mgr_clutter_effect_destroy (MBWMObject* obj) +{ + MBWMCompMgrClutterEffect * e = MB_WM_COMP_MGR_CLUTTER_EFFECT (obj); + + if (!e || !e->behaviour) + return; + + g_object_unref (e->behaviour); + g_object_unref (e->timeline); +} + +int +mb_wm_comp_mgr_clutter_effect_class_type () +{ + static int type = 0; + + if (UNLIKELY(type == 0)) + { + static MBWMObjectClassInfo info = { + sizeof (MBWMCompMgrClutterEffectClass), + sizeof (MBWMCompMgrClutterEffect), + mb_wm_comp_mgr_clutter_effect_init, + mb_wm_comp_mgr_clutter_effect_destroy, + mb_wm_comp_mgr_clutter_effect_class_init + }; + + type = + mb_wm_object_register_class (&info, MB_WM_TYPE_COMP_MGR_EFFECT, 0); + } + + return type; +} + +/* + * This is private method for use by the manager, hence static. + */ +static MBWMCompMgrEffect * +mb_wm_comp_mgr_clutter_effect_new (MBWMCompMgrEffectType type, + unsigned long duration, + MBWMGravity gravity, + ClutterTimeline * timeline, + ClutterBehaviour * behaviour) +{ + MBWMObject *e; + + e = + mb_wm_object_new (MB_WM_TYPE_COMP_MGR_CLUTTER_EFFECT, + MBWMObjectPropCompMgrEffectType, type, + MBWMObjectPropCompMgrEffectDuration, duration, + MBWMObjectPropCompMgrEffectGravity, gravity, + MBWMObjectPropCompMgrClutterEffectTimeline, timeline, + MBWMObjectPropCompMgrClutterEffectBehaviour, behaviour, + NULL); + + return MB_WM_COMP_MGR_EFFECT (e); +} |