update to 1.10.4
[profile/ivi/clutter.git] / clutter / x11 / clutter-x11-texture-pixmap.c
1 /*
2  * Clutter.
3  *
4  * An OpenGL based 'interactive canvas' library.
5  *
6  * Copyright (C) 2007 OpenedHand
7  * Copyright (C) 2010 Intel Corporation.
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
21  *
22  * Authors:
23  *  Johan Bilien   <johan.bilien@nokia.com>
24  *  Neil Roberts   <neil@linux.intel.com>
25  */
26
27 /**
28  * SECTION:clutter-x11-texture-pixmap
29  * @Title: ClutterX11TexturePixmap
30  * @short_description: A texture which displays the content of an X Pixmap.
31  *
32  * #ClutterX11TexturePixmap is a class for displaying the content of an
33  * X Pixmap as a ClutterActor. Used together with the X Composite extension,
34  * it allows to display the content of X Windows inside Clutter.
35  *
36  * The class uses the GLX_EXT_texture_from_pixmap OpenGL extension
37  * (http://people.freedesktop.org/~davidr/GLX_EXT_texture_from_pixmap.txt)
38  * if available
39  */
40
41 #ifdef HAVE_CONFIG_H
42 #include "config.h"
43 #endif
44
45 #define CLUTTER_ENABLE_EXPERIMENTAL_API
46
47 #include "clutter-x11-texture-pixmap.h"
48 #include "clutter-x11.h"
49 #include "clutter-backend-x11.h"
50
51 #include "clutter-actor-private.h"
52 #include "clutter-marshal.h"
53 #include "clutter-paint-volume-private.h"
54 #include "clutter-private.h"
55
56 #include <cogl/cogl.h>
57
58 #include <cogl/cogl-texture-pixmap-x11.h>
59
60 #include <X11/extensions/Xdamage.h>
61
62 #if HAVE_XCOMPOSITE
63 #include <X11/extensions/Xcomposite.h>
64 #endif
65
66 enum
67 {
68   PROP_PIXMAP = 1,
69   PROP_PIXMAP_WIDTH,
70   PROP_PIXMAP_HEIGHT,
71   PROP_DEPTH,
72   PROP_AUTO,
73   PROP_WINDOW,
74   PROP_WINDOW_REDIRECT_AUTOMATIC,
75   PROP_WINDOW_MAPPED,
76   PROP_DESTROYED,
77   PROP_WINDOW_X,
78   PROP_WINDOW_Y,
79   PROP_WINDOW_OVERRIDE_REDIRECT
80 };
81
82 enum
83 {
84   UPDATE_AREA,
85   QUEUE_DAMAGE_REDRAW,
86
87   /* FIXME: Pixmap lost signal? */
88   LAST_SIGNAL
89 };
90
91 static ClutterX11FilterReturn
92 on_x_event_filter (XEvent *xev, ClutterEvent *cev, gpointer data);
93
94 static void
95 clutter_x11_texture_pixmap_update_area_real (ClutterX11TexturePixmap *texture,
96                                              gint                     x,
97                                              gint                     y,
98                                              gint                     width,
99                                              gint                     height);
100 static void
101 clutter_x11_texture_pixmap_sync_window_internal (ClutterX11TexturePixmap *texture,
102                                                  int                      x,
103                                                  int                      y,
104                                                  int                      width,
105                                                  int                      height,
106                                                  gboolean                 override_redirect);
107 static void
108 clutter_x11_texture_pixmap_set_mapped (ClutterX11TexturePixmap *texture, gboolean mapped);
109 static void
110 clutter_x11_texture_pixmap_destroyed (ClutterX11TexturePixmap *texture);
111
112 static guint signals[LAST_SIGNAL] = { 0, };
113
114 struct _ClutterX11TexturePixmapPrivate
115 {
116   Window        window;
117   Pixmap        pixmap;
118   guint         pixmap_width, pixmap_height;
119   guint         depth;
120
121   Damage        damage;
122
123   gint          window_x, window_y;
124   gint          window_width, window_height;
125
126   guint window_redirect_automatic : 1;
127   /* FIXME: this is inconsistently either whether the window is mapped or whether
128    * it is viewable, and isn't updated correctly. */
129   guint window_mapped             : 1;
130   guint destroyed                 : 1;
131   guint owns_pixmap               : 1;
132   guint override_redirect         : 1;
133   guint automatic_updates         : 1;
134 };
135
136 static int _damage_event_base = 0;
137
138 G_DEFINE_TYPE (ClutterX11TexturePixmap,
139                clutter_x11_texture_pixmap,
140                CLUTTER_TYPE_TEXTURE);
141
142 static gboolean
143 check_extensions (ClutterX11TexturePixmap *texture)
144 {
145   int damage_error;
146   Display *dpy;
147
148   if (_damage_event_base)
149     return TRUE;
150
151   dpy = clutter_x11_get_default_display();
152
153   if (!XDamageQueryExtension (dpy, &_damage_event_base, &damage_error))
154     {
155       g_warning ("No Damage extension");
156       return FALSE;
157     }
158
159   return TRUE;
160 }
161
162 static void
163 process_damage_event (ClutterX11TexturePixmap *texture,
164                       XDamageNotifyEvent *damage_event)
165 {
166   /* Cogl will deal with updating the texture and subtracting from the
167      damage region so we only need to queue a redraw */
168   g_signal_emit (texture, signals[QUEUE_DAMAGE_REDRAW],
169                  0,
170                  damage_event->area.x,
171                  damage_event->area.y,
172                  damage_event->area.width,
173                  damage_event->area.height);
174 }
175
176 static ClutterX11FilterReturn
177 on_x_event_filter (XEvent *xev, ClutterEvent *cev, gpointer data)
178 {
179   ClutterX11TexturePixmap        *texture;
180   ClutterX11TexturePixmapPrivate *priv;
181
182   texture = CLUTTER_X11_TEXTURE_PIXMAP (data);
183
184   g_return_val_if_fail (CLUTTER_X11_IS_TEXTURE_PIXMAP (texture), \
185                         CLUTTER_X11_FILTER_CONTINUE);
186
187   priv = texture->priv;
188
189   if (xev->type == _damage_event_base + XDamageNotify)
190     {
191       XDamageNotifyEvent *dev = (XDamageNotifyEvent*)xev;
192
193       if (dev->damage != priv->damage)
194         return CLUTTER_X11_FILTER_CONTINUE;
195
196       process_damage_event (texture, dev);
197     }
198
199   return  CLUTTER_X11_FILTER_CONTINUE;
200 }
201
202 static ClutterX11FilterReturn
203 on_x_event_filter_too (XEvent *xev, ClutterEvent *cev, gpointer data)
204 {
205   ClutterX11TexturePixmap        *texture;
206   ClutterX11TexturePixmapPrivate *priv;
207
208   texture = CLUTTER_X11_TEXTURE_PIXMAP (data);
209
210   g_return_val_if_fail (CLUTTER_X11_IS_TEXTURE_PIXMAP (texture), \
211                         CLUTTER_X11_FILTER_CONTINUE);
212
213   priv = texture->priv;
214
215   if (xev->xany.window != priv->window)
216     return CLUTTER_X11_FILTER_CONTINUE;
217
218   switch (xev->type) {
219   case MapNotify:
220     clutter_x11_texture_pixmap_sync_window_internal (texture,
221                                                      priv->window_x,
222                                                      priv->window_y,
223                                                      priv->window_width,
224                                                      priv->window_height,
225                                                      priv->override_redirect);
226     break;
227   case ConfigureNotify:
228     clutter_x11_texture_pixmap_sync_window_internal (texture,
229                                                      xev->xconfigure.x,
230                                                      xev->xconfigure.y,
231                                                      xev->xconfigure.width,
232                                                      xev->xconfigure.height,
233                                                      xev->xconfigure.override_redirect);
234     break;
235   case UnmapNotify:
236     clutter_x11_texture_pixmap_set_mapped (texture, FALSE);
237     break;
238   case DestroyNotify:
239     clutter_x11_texture_pixmap_destroyed (texture);
240     break;
241   default:
242     break;
243   }
244
245   return CLUTTER_X11_FILTER_CONTINUE;
246 }
247
248 static void
249 update_pixmap_damage_object (ClutterX11TexturePixmap *texture)
250 {
251   ClutterX11TexturePixmapPrivate *priv = texture->priv;
252   CoglHandle cogl_texture;
253
254   /* If we already have a CoglTexturePixmapX11 then update its
255      damage object */
256   cogl_texture =
257     clutter_texture_get_cogl_texture (CLUTTER_TEXTURE (texture));
258
259   if (cogl_texture && cogl_is_texture_pixmap_x11 (cogl_texture))
260     {
261       if (priv->damage)
262         {
263           const CoglTexturePixmapX11ReportLevel report_level =
264             COGL_TEXTURE_PIXMAP_X11_DAMAGE_BOUNDING_BOX;
265           cogl_texture_pixmap_x11_set_damage_object (cogl_texture,
266                                                      priv->damage,
267                                                      report_level);
268         }
269       else
270         cogl_texture_pixmap_x11_set_damage_object (cogl_texture, 0, 0);
271     }
272 }
273
274 static void
275 create_damage_resources (ClutterX11TexturePixmap *texture)
276 {
277   ClutterX11TexturePixmapPrivate *priv;
278   Display                        *dpy;
279
280   priv = texture->priv;
281   dpy = clutter_x11_get_default_display();
282
283   if (!priv->pixmap)
284     return;
285
286   clutter_x11_trap_x_errors ();
287
288   priv->damage = XDamageCreate (dpy,
289                                 priv->pixmap,
290                                 XDamageReportBoundingBox);
291
292   /* Errors here might occur if the window is already destroyed, we
293    * simply skip processing damage and assume that the texture pixmap
294    * will be cleaned up by the app when it gets a DestroyNotify.
295    */
296   XSync (dpy, FALSE);
297   clutter_x11_untrap_x_errors ();
298
299   if (priv->damage)
300     {
301       clutter_x11_add_filter (on_x_event_filter, (gpointer)texture);
302
303       update_pixmap_damage_object (texture);
304     }
305 }
306
307 static void
308 free_damage_resources (ClutterX11TexturePixmap *texture)
309 {
310   ClutterX11TexturePixmapPrivate *priv;
311   Display                        *dpy;
312
313   priv = texture->priv;
314   dpy = clutter_x11_get_default_display();
315
316   if (priv->damage)
317     {
318       clutter_x11_trap_x_errors ();
319       XDamageDestroy (dpy, priv->damage);
320       XSync (dpy, FALSE);
321       clutter_x11_untrap_x_errors ();
322       priv->damage = None;
323
324       clutter_x11_remove_filter (on_x_event_filter, (gpointer)texture);
325
326       update_pixmap_damage_object (texture);
327     }
328 }
329
330 static gboolean
331 clutter_x11_texture_pixmap_get_paint_volume (ClutterActor       *self,
332                                              ClutterPaintVolume *volume)
333 {
334   return clutter_paint_volume_set_from_allocation (volume, self);
335 }
336
337 static void
338 clutter_x11_texture_pixmap_real_queue_damage_redraw (
339                                               ClutterX11TexturePixmap *texture,
340                                               gint x,
341                                               gint y,
342                                               gint width,
343                                               gint height)
344 {
345   ClutterX11TexturePixmapPrivate *priv = texture->priv;
346   ClutterActor *self = CLUTTER_ACTOR (texture);
347   ClutterActorBox allocation;
348   float scale_x, scale_y;
349   cairo_rectangle_int_t clip;
350
351   /* NB: clutter_actor_queue_clipped_redraw expects a box in the actor's
352    * coordinate space so we need to convert from pixmap coordinates to
353    * actor coordinates...
354    */
355
356   /* Calling clutter_actor_get_allocation_box() is enormously expensive
357    * if the actor has an out-of-date allocation, since it triggers
358    * a full redraw. clutter_actor_queue_clipped_redraw() would redraw
359    * the whole stage anyways in that case, so just go ahead and do
360    * it here.
361    */
362   if (!clutter_actor_has_allocation (self))
363     {
364       clutter_actor_queue_redraw (self);
365       return;
366     }
367
368   if (priv->pixmap_width == 0 || priv->pixmap_height == 0)
369     return;
370
371   clutter_actor_get_allocation_box (self, &allocation);
372
373   scale_x = (allocation.x2 - allocation.x1) / priv->pixmap_width;
374   scale_y = (allocation.y2 - allocation.y1) / priv->pixmap_height;
375
376   clip.x = x * scale_x;
377   clip.y = y * scale_y;
378   clip.width = width * scale_x;
379   clip.height = height * scale_y;
380   clutter_actor_queue_redraw_with_clip (self, &clip);
381 }
382
383 static void
384 clutter_x11_texture_pixmap_init (ClutterX11TexturePixmap *self)
385 {
386   self->priv =
387       G_TYPE_INSTANCE_GET_PRIVATE (self,
388                                    CLUTTER_X11_TYPE_TEXTURE_PIXMAP,
389                                    ClutterX11TexturePixmapPrivate);
390
391   if (!check_extensions (self))
392     {
393       /* FIMXE: means display lacks needed extensions for at least auto.
394        *        - a _can_autoupdate() method ?
395       */
396     }
397
398   self->priv->automatic_updates = FALSE;
399   self->priv->damage = None;
400   self->priv->window = None;
401   self->priv->pixmap = None;
402   self->priv->pixmap_height = 0;
403   self->priv->pixmap_width = 0;
404   self->priv->window_redirect_automatic = TRUE;
405   self->priv->window_mapped = FALSE;
406   self->priv->destroyed = FALSE;
407   self->priv->override_redirect = FALSE;
408   self->priv->window_x = 0;
409   self->priv->window_y = 0;
410 }
411
412 static void
413 clutter_x11_texture_pixmap_dispose (GObject *object)
414 {
415   ClutterX11TexturePixmap *texture = CLUTTER_X11_TEXTURE_PIXMAP (object);
416
417   free_damage_resources (texture);
418
419   clutter_x11_remove_filter (on_x_event_filter_too, (gpointer)texture);
420   clutter_x11_texture_pixmap_set_pixmap (texture, None);
421
422   G_OBJECT_CLASS (clutter_x11_texture_pixmap_parent_class)->dispose (object);
423 }
424
425 static void
426 clutter_x11_texture_pixmap_set_property (GObject      *object,
427                                          guint         prop_id,
428                                          const GValue *value,
429                                          GParamSpec   *pspec)
430 {
431   ClutterX11TexturePixmap  *texture = CLUTTER_X11_TEXTURE_PIXMAP (object);
432   ClutterX11TexturePixmapPrivate *priv = texture->priv;
433
434   switch (prop_id)
435     {
436     case PROP_PIXMAP:
437       clutter_x11_texture_pixmap_set_pixmap (texture,
438                                              g_value_get_ulong (value));
439       break;
440     case PROP_AUTO:
441       clutter_x11_texture_pixmap_set_automatic (texture,
442                                                 g_value_get_boolean (value));
443       break;
444     case PROP_WINDOW:
445       clutter_x11_texture_pixmap_set_window (texture,
446                                              g_value_get_ulong (value),
447                                              priv->window_redirect_automatic);
448       break;
449     case PROP_WINDOW_REDIRECT_AUTOMATIC:
450       {
451         gboolean new;
452         new = g_value_get_boolean (value);
453
454         /* Change the update mode.. */
455         if (new != priv->window_redirect_automatic && priv->window)
456           clutter_x11_texture_pixmap_set_window (texture, priv->window, new);
457
458         priv->window_redirect_automatic = new;
459       }
460       break;
461     default:
462       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
463       break;
464     }
465 }
466
467 static void
468 clutter_x11_texture_pixmap_get_property (GObject      *object,
469                                          guint         prop_id,
470                                          GValue       *value,
471                                          GParamSpec   *pspec)
472 {
473   ClutterX11TexturePixmap *texture = CLUTTER_X11_TEXTURE_PIXMAP (object);
474   ClutterX11TexturePixmapPrivate *priv = texture->priv;
475
476   switch (prop_id)
477     {
478     case PROP_PIXMAP:
479       g_value_set_ulong (value, priv->pixmap);
480       break;
481     case PROP_PIXMAP_WIDTH:
482       g_value_set_uint (value, priv->pixmap_width);
483       break;
484     case PROP_PIXMAP_HEIGHT:
485       g_value_set_uint (value, priv->pixmap_height);
486       break;
487     case PROP_DEPTH:
488       g_value_set_uint (value, priv->depth);
489       break;
490     case PROP_AUTO:
491       g_value_set_boolean (value, priv->automatic_updates);
492       break;
493     case PROP_WINDOW:
494       g_value_set_ulong (value, priv->window);
495       break;
496     case PROP_WINDOW_REDIRECT_AUTOMATIC:
497       g_value_set_boolean (value, priv->window_redirect_automatic);
498       break;
499     case PROP_WINDOW_MAPPED:
500       g_value_set_boolean (value, priv->window_mapped);
501       break;
502     case PROP_DESTROYED:
503       g_value_set_boolean (value, priv->destroyed);
504       break;
505     case PROP_WINDOW_X:
506       g_value_set_int (value, priv->window_x);
507       break;
508     case PROP_WINDOW_Y:
509       g_value_set_int (value, priv->window_y);
510       break;
511     case PROP_WINDOW_OVERRIDE_REDIRECT:
512       g_value_set_boolean (value, priv->override_redirect);
513       break;
514     default:
515       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
516       break;
517     }
518 }
519
520 static void
521 clutter_x11_texture_pixmap_class_init (ClutterX11TexturePixmapClass *klass)
522 {
523   GObjectClass      *object_class = G_OBJECT_CLASS (klass);
524   ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
525   GParamSpec        *pspec;
526
527   g_type_class_add_private (klass, sizeof (ClutterX11TexturePixmapPrivate));
528
529   actor_class->get_paint_volume = clutter_x11_texture_pixmap_get_paint_volume;
530
531   object_class->dispose      = clutter_x11_texture_pixmap_dispose;
532   object_class->set_property = clutter_x11_texture_pixmap_set_property;
533   object_class->get_property = clutter_x11_texture_pixmap_get_property;
534
535   klass->update_area         = clutter_x11_texture_pixmap_update_area_real;
536
537   pspec = g_param_spec_ulong ("pixmap",
538                               P_("Pixmap"),
539                               P_("The X11 Pixmap to be bound"),
540                               0, G_MAXULONG,
541                               None,
542                               G_PARAM_READWRITE);
543
544   g_object_class_install_property (object_class, PROP_PIXMAP, pspec);
545
546   pspec = g_param_spec_uint ("pixmap-width",
547                              P_("Pixmap width"),
548                              P_("The width of the pixmap bound to this texture"),
549                              0, G_MAXUINT,
550                              0,
551                              G_PARAM_READABLE);
552
553   g_object_class_install_property (object_class, PROP_PIXMAP_WIDTH, pspec);
554
555   pspec = g_param_spec_uint ("pixmap-height",
556                              P_("Pixmap height"),
557                              P_("The height of the pixmap bound to this texture"),
558                              0, G_MAXUINT,
559                              0,
560                              G_PARAM_READABLE);
561
562   g_object_class_install_property (object_class, PROP_PIXMAP_HEIGHT, pspec);
563
564   pspec = g_param_spec_uint ("pixmap-depth",
565                              P_("Pixmap Depth"),
566                              P_("The depth (in number of bits) of the pixmap bound to this texture"),
567                              0, G_MAXUINT,
568                              0,
569                              G_PARAM_READABLE);
570
571   g_object_class_install_property (object_class, PROP_DEPTH, pspec);
572
573   pspec = g_param_spec_boolean ("automatic-updates",
574                                 P_("Automatic Updates"),
575                                 P_("If the texture should be kept in "
576                                    "sync with any pixmap changes."),
577                                 FALSE,
578                                 G_PARAM_READWRITE);
579
580   g_object_class_install_property (object_class, PROP_AUTO, pspec);
581
582   pspec = g_param_spec_ulong ("window",
583                               P_("Window"),
584                               P_("The X11 Window to be bound"),
585                               0, G_MAXULONG,
586                               None,
587                               G_PARAM_READWRITE);
588
589   g_object_class_install_property (object_class, PROP_WINDOW, pspec);
590
591   pspec = g_param_spec_boolean ("window-redirect-automatic",
592                                 P_("Window Redirect Automatic"),
593                                 P_("If composite window redirects are set to "
594                                    "Automatic (or Manual if false)"),
595                                 TRUE,
596                                 G_PARAM_READWRITE);
597
598   g_object_class_install_property (object_class,
599                                    PROP_WINDOW_REDIRECT_AUTOMATIC, pspec);
600
601
602   pspec = g_param_spec_boolean ("window-mapped",
603                                 P_("Window Mapped"),
604                                 P_("If window is mapped"),
605                                 FALSE,
606                                 G_PARAM_READABLE);
607
608   g_object_class_install_property (object_class,
609                                    PROP_WINDOW_MAPPED, pspec);
610
611
612   pspec = g_param_spec_boolean ("destroyed",
613                                 P_("Destroyed"),
614                                 P_("If window has been destroyed"),
615                                 FALSE,
616                                 G_PARAM_READABLE);
617
618   g_object_class_install_property (object_class,
619                                    PROP_DESTROYED, pspec);
620
621   pspec = g_param_spec_int ("window-x",
622                             P_("Window X"),
623                             P_("X position of window on screen according to X11"),
624                             G_MININT, G_MAXINT, 0, G_PARAM_READABLE);
625
626   g_object_class_install_property (object_class,
627                                    PROP_WINDOW_X, pspec);
628
629
630   pspec = g_param_spec_int ("window-y",
631                             P_("Window Y"),
632                             P_("Y position of window on screen according to X11"),
633                             G_MININT, G_MAXINT, 0, G_PARAM_READABLE);
634
635   g_object_class_install_property (object_class,
636                                    PROP_WINDOW_Y, pspec);
637
638   pspec = g_param_spec_boolean ("window-override-redirect",
639                                 P_("Window Override Redirect"),
640                                 P_("If this is an override-redirect window"),
641                                 FALSE,
642                                 G_PARAM_READABLE);
643
644   g_object_class_install_property (object_class,
645                                    PROP_WINDOW_OVERRIDE_REDIRECT, pspec);
646
647
648   /**
649    * ClutterX11TexturePixmap::update-area:
650    * @texture: the object which received the signal
651    *
652    * The ::update-area signal is emitted to ask the texture to update its
653    * content from its source pixmap.
654    *
655    * Since: 0.8
656    */
657   signals[UPDATE_AREA] =
658       g_signal_new (g_intern_static_string ("update-area"),
659                     G_TYPE_FROM_CLASS (object_class),
660                     G_SIGNAL_RUN_FIRST,
661                     G_STRUCT_OFFSET (ClutterX11TexturePixmapClass, \
662                                      update_area),
663                     NULL, NULL,
664                     _clutter_marshal_VOID__INT_INT_INT_INT,
665                     G_TYPE_NONE, 4,
666                     G_TYPE_INT,
667                     G_TYPE_INT,
668                     G_TYPE_INT,
669                     G_TYPE_INT);
670
671   /**
672    * ClutterX11TexturePixmap::queue-damage-redraw:
673    * @texture: the object which received the signal
674    * @x: The top left x position of the damage region
675    * @y: The top left y position of the damage region
676    * @width: The width of the damage region
677    * @height: The height of the damage region
678    *
679    * ::queue-damage-redraw is emitted to notify that some sub-region
680    * of the texture has been changed (either by an automatic damage
681    * update or by an explicit call to
682    * clutter_x11_texture_pixmap_update_area). This usually means a
683    * redraw needs to be queued for the actor.
684    *
685    * The default handler will queue a clipped redraw in response to
686    * the damage, using the assumption that the pixmap is being painted
687    * to a rectangle covering the transformed allocation of the actor.
688    * If you sub-class and change the paint method so this isn't true
689    * then you must also provide your own damage signal handler to
690    * queue a redraw that blocks this default behaviour.
691    *
692    * Since: 1.2
693    */
694   signals[QUEUE_DAMAGE_REDRAW] =
695     g_signal_new (g_intern_static_string ("queue-damage-redraw"),
696                   G_TYPE_FROM_CLASS (object_class),
697                   G_SIGNAL_RUN_FIRST,
698                   0,
699                   NULL, NULL,
700                   _clutter_marshal_VOID__INT_INT_INT_INT,
701                   G_TYPE_NONE, 4,
702                   G_TYPE_INT,
703                   G_TYPE_INT,
704                   G_TYPE_INT,
705                   G_TYPE_INT);
706
707   g_signal_override_class_handler ("queue-damage-redraw",
708                                    CLUTTER_X11_TYPE_TEXTURE_PIXMAP,
709                                    G_CALLBACK (clutter_x11_texture_pixmap_real_queue_damage_redraw));
710 }
711
712 static void
713 clutter_x11_texture_pixmap_update_area_real (ClutterX11TexturePixmap *texture,
714                                              gint                     x,
715                                              gint                     y,
716                                              gint                     width,
717                                              gint                     height)
718 {
719   CoglHandle cogl_texture;
720
721   cogl_texture = clutter_texture_get_cogl_texture (CLUTTER_TEXTURE (texture));
722
723   if (cogl_texture)
724     cogl_texture_pixmap_x11_update_area (cogl_texture, x, y, width, height);
725 }
726
727 /**
728  * clutter_x11_texture_pixmap_new:
729  *
730  * Creates a new #ClutterX11TexturePixmap which can be used to display the
731  * contents of an X11 Pixmap inside a Clutter scene graph
732  *
733  * Return value: A new #ClutterX11TexturePixmap
734  *
735  * Since: 0.8
736  */
737 ClutterActor *
738 clutter_x11_texture_pixmap_new (void)
739 {
740   ClutterActor *actor;
741
742   actor = g_object_new (CLUTTER_X11_TYPE_TEXTURE_PIXMAP, NULL);
743
744   return actor;
745 }
746
747 /**
748  * clutter_x11_texture_pixmap_new_with_pixmap:
749  * @pixmap: the X Pixmap to which this texture should be bound
750  *
751  * Creates a new #ClutterX11TexturePixmap for @pixmap
752  *
753  * Return value: A new #ClutterX11TexturePixmap bound to the given X Pixmap
754  *
755  * Since: 0.8
756  */
757 ClutterActor *
758 clutter_x11_texture_pixmap_new_with_pixmap (Pixmap pixmap)
759 {
760   ClutterActor *actor;
761
762   actor = g_object_new (CLUTTER_X11_TYPE_TEXTURE_PIXMAP,
763                         "pixmap", pixmap,
764                         NULL);
765
766   return actor;
767 }
768
769 /**
770  * clutter_x11_texture_pixmap_new_with_window:
771  * @window: the X window to which this texture should be bound
772  *
773  * Creates a new #ClutterX11TexturePixmap for @window
774  *
775  * Return value: A new #ClutterX11TexturePixmap bound to the given X window.
776  *
777  * Since: 0.8
778  **/
779 ClutterActor *
780 clutter_x11_texture_pixmap_new_with_window (Window window)
781 {
782   ClutterActor *actor;
783
784   actor = g_object_new (CLUTTER_X11_TYPE_TEXTURE_PIXMAP,
785                         "window", window,
786                         NULL);
787
788   return actor;
789 }
790
791 /**
792  * clutter_x11_texture_pixmap_set_pixmap:
793  * @texture: the texture to bind
794  * @pixmap: the X Pixmap to which the texture should be bound
795  *
796  * Sets the X Pixmap to which the texture should be bound.
797  *
798  * Since: 0.8
799  */
800 void
801 clutter_x11_texture_pixmap_set_pixmap (ClutterX11TexturePixmap *texture,
802                                        Pixmap                   pixmap)
803 {
804   Window        root;
805   int           x, y;
806   unsigned int  width, height, border_width, depth;
807   Status        status = 0;
808   gboolean      new_pixmap = FALSE, new_pixmap_width = FALSE;
809   gboolean      new_pixmap_height = FALSE, new_pixmap_depth = FALSE;
810   CoglPipeline *pipeline;
811
812   ClutterX11TexturePixmapPrivate *priv;
813
814   g_return_if_fail (CLUTTER_X11_IS_TEXTURE_PIXMAP (texture));
815
816   priv = texture->priv;
817
818   /* Get rid of the existing Cogl texture early because it may try to
819      use the pixmap which we might destroy */
820   pipeline = (CoglPipeline *)
821     clutter_texture_get_cogl_material (CLUTTER_TEXTURE (texture));
822   if (pipeline)
823     cogl_pipeline_set_layer_texture (pipeline, 0, NULL);
824
825   if (pixmap != None)
826     {
827       clutter_x11_trap_x_errors ();
828       status = XGetGeometry (clutter_x11_get_default_display(),
829                              (Drawable)pixmap,
830                              &root,
831                              &x,
832                              &y,
833                              &width,
834                              &height,
835                              &border_width,
836                              &depth);
837
838       if (clutter_x11_untrap_x_errors () || status == 0)
839         {
840           g_warning ("Unable to query pixmap: %lx", pixmap);
841           pixmap = None;
842           width = height = depth = 0;
843         }
844     }
845   else
846     {
847       width = height = depth = 0;
848     }
849
850   if (priv->pixmap != pixmap)
851     {
852       if (priv->pixmap && priv->owns_pixmap)
853         XFreePixmap (clutter_x11_get_default_display (), priv->pixmap);
854
855       priv->pixmap = pixmap;
856       new_pixmap = TRUE;
857
858       /* The damage object is created on the pixmap, so it needs to be
859        * recreated with a change in pixmap.
860        */
861       if (priv->automatic_updates)
862         {
863           free_damage_resources (texture);
864           create_damage_resources (texture);
865         }
866     }
867
868   if (priv->pixmap_width != width)
869     {
870       priv->pixmap_width = width;
871       new_pixmap_width = TRUE;
872     }
873
874   if (priv->pixmap_height != height)
875     {
876       priv->pixmap_height = height;
877       new_pixmap_height = TRUE;
878     }
879
880   if (priv->depth != depth)
881     {
882       priv->depth = depth;
883       new_pixmap_depth = TRUE;
884     }
885
886   /* NB: We defer sending the signals until updating all the
887    * above members so the values are all available to the
888    * signal handlers. */
889   g_object_ref (texture);
890
891   if (new_pixmap)
892     g_object_notify (G_OBJECT (texture), "pixmap");
893   if (new_pixmap_width)
894     g_object_notify (G_OBJECT (texture), "pixmap-width");
895   if (new_pixmap_height)
896     g_object_notify (G_OBJECT (texture), "pixmap-height");
897   if (new_pixmap_depth)
898     g_object_notify (G_OBJECT (texture), "pixmap-depth");
899
900   if (pixmap)
901     {
902       CoglContext *ctx =
903         clutter_backend_get_cogl_context (clutter_get_default_backend ());
904       GError *error = NULL;
905       CoglTexturePixmapX11 *texture_pixmap =
906         cogl_texture_pixmap_x11_new (ctx, pixmap, FALSE, &error);
907       if (texture_pixmap)
908         {
909           clutter_texture_set_cogl_texture (CLUTTER_TEXTURE (texture),
910                                             COGL_TEXTURE (texture_pixmap));
911           cogl_object_unref (texture_pixmap);
912           update_pixmap_damage_object (texture);
913         }
914       else
915         {
916           g_warning ("Failed to create CoglTexturePixmapX11: %s",
917                      error->message);
918           g_error_free (error);
919         }
920     }
921
922   /*
923    * Keep ref until here in case a notify causes removal from the scene; can't
924    * lower the notifies because glx's notify handler needs to run before
925    * update_area
926    */
927   g_object_unref (texture);
928 }
929
930 /**
931  * clutter_x11_texture_pixmap_set_window:
932  * @texture: the texture to bind
933  * @window: the X window to which the texture should be bound
934  * @automatic: %TRUE for automatic window updates, %FALSE for manual.
935  *
936  * Sets up a suitable pixmap for the window, using the composite and damage
937  * extensions if possible, and then calls
938  * clutter_x11_texture_pixmap_set_pixmap().
939  *
940  * If you want to display a window in a #ClutterTexture, you probably want
941  * this function, or its older sister, clutter_glx_texture_pixmap_set_window().
942  *
943  * This function has no effect unless the XComposite extension is available.
944  *
945  * Since: 0.8
946  */
947 void
948 clutter_x11_texture_pixmap_set_window (ClutterX11TexturePixmap *texture,
949                                        Window                   window,
950                                        gboolean                 automatic)
951 {
952   ClutterX11TexturePixmapPrivate *priv;
953   XWindowAttributes attr;
954   Display *dpy;
955
956   g_return_if_fail (CLUTTER_X11_IS_TEXTURE_PIXMAP (texture));
957
958   if (!clutter_x11_has_composite_extension ())
959     return;
960
961   dpy = clutter_x11_get_default_display ();
962   if (dpy == NULL)
963     return;
964
965 #if HAVE_XCOMPOSITE
966   priv = texture->priv;
967
968   if (priv->window == window && automatic == priv->window_redirect_automatic)
969     return;
970
971   if (priv->window)
972     {
973       clutter_x11_remove_filter (on_x_event_filter_too, (gpointer)texture);
974       clutter_x11_trap_x_errors ();
975       XCompositeUnredirectWindow(clutter_x11_get_default_display (),
976                                   priv->window,
977                                   priv->window_redirect_automatic ?
978                                   CompositeRedirectAutomatic : CompositeRedirectManual);
979       XSync (clutter_x11_get_default_display (), False);
980       clutter_x11_untrap_x_errors ();
981
982       clutter_x11_texture_pixmap_set_pixmap (texture, None);
983     }
984
985   priv->window = window;
986   priv->window_redirect_automatic = automatic;
987   priv->window_mapped = FALSE;
988   priv->destroyed = FALSE;
989
990   if (window == None)
991     return;
992
993   clutter_x11_trap_x_errors ();
994   {
995     if (!XGetWindowAttributes (dpy, window, &attr))
996       {
997         XSync (dpy, False);
998         clutter_x11_untrap_x_errors ();
999         g_warning ("bad window 0x%x", (guint32)window);
1000         priv->window = None;
1001         return;
1002       }
1003
1004     XCompositeRedirectWindow
1005                        (dpy,
1006                         window,
1007                         automatic ?
1008                         CompositeRedirectAutomatic : CompositeRedirectManual);
1009     XSync (dpy, False);
1010   }
1011
1012   clutter_x11_untrap_x_errors ();
1013
1014   XSelectInput (dpy, priv->window,
1015                 attr.your_event_mask | StructureNotifyMask);
1016   clutter_x11_add_filter (on_x_event_filter_too, (gpointer)texture);
1017
1018   g_object_ref (texture);
1019   g_object_notify (G_OBJECT (texture), "window");
1020
1021   clutter_x11_texture_pixmap_set_mapped (texture,
1022                                          attr.map_state == IsViewable);
1023
1024   clutter_x11_texture_pixmap_sync_window_internal (texture,
1025                                                    attr.x, attr.y,
1026                                                    attr.width, attr.height,
1027                                                    attr.override_redirect);
1028   g_object_unref (texture);
1029
1030 #endif /* HAVE_XCOMPOSITE */
1031 }
1032
1033 static void
1034 clutter_x11_texture_pixmap_sync_window_internal (ClutterX11TexturePixmap *texture,
1035                                                  int                      x,
1036                                                  int                      y,
1037                                                  int                      width,
1038                                                  int                      height,
1039                                                  gboolean                 override_redirect)
1040 {
1041   ClutterX11TexturePixmapPrivate *priv;
1042   Pixmap pixmap = None;
1043   gboolean mapped = FALSE;
1044   gboolean notify_x = FALSE;
1045   gboolean notify_y = FALSE;
1046   gboolean notify_override_redirect = FALSE;
1047
1048   priv = texture->priv;
1049
1050   if (priv->destroyed)
1051     return;
1052
1053   notify_x = x != priv->window_x;
1054   notify_y = y != priv->window_y;
1055   notify_override_redirect = override_redirect != priv->override_redirect;
1056   priv->window_x = x;
1057   priv->window_y = y;
1058   priv->window_width = width;
1059   priv->window_height = height;
1060   priv->override_redirect = override_redirect;
1061
1062   if (!clutter_x11_has_composite_extension ())
1063     {
1064       /* FIXME: this should just be an error, this is unlikely to work worth anything */
1065       clutter_x11_texture_pixmap_set_pixmap (texture, priv->window);
1066       return;
1067     }
1068
1069   if (priv->pixmap == None || width != priv->pixmap_width || height != priv->pixmap_height)
1070     {
1071       /* Note that we're checking the size from the event against the size we obtained
1072        * from the last XCompositeNameWindowPixmap/XGetGeometry pair. This will always
1073        * end up right in the end, but we can have a series like:
1074        *
1075        *  Window sized to 100x100
1076        *  Window sized to 110x110
1077        *  Window sized to 120x120
1078        *                          Configure received for 100x100 - NameWindowPixmap
1079        *                          Configure received for 110x110 - NameWindowPixmap
1080        *                          Configure received for 120x120 - last size OK
1081        *
1082        * Where we NameWindowPixmap several times in a row. (Using pixmap_width/pixmap_height
1083        * rather than window_width/window_height saves the last one.)
1084        */
1085
1086       Display *dpy = clutter_x11_get_default_display ();
1087
1088       /* NB: It's only valid to name a pixmap if the window is viewable.
1089        *
1090        * We don't explicitly check this though since there would be a race
1091        * between checking and naming unless we use a server grab which is
1092        * undesireable.
1093        *
1094        * Instead we gracefully handle any error with naming the pixmap.
1095        */
1096       clutter_x11_trap_x_errors ();
1097
1098       pixmap = XCompositeNameWindowPixmap (dpy, priv->window);
1099
1100       /* Possible improvement: combine with the XGetGeometry in
1101        * clutter_x11_texture_pixmap_set_pixmap() */
1102       XSync(dpy, False);
1103
1104       if (clutter_x11_untrap_x_errors ())
1105         pixmap = None;
1106     }
1107
1108   g_object_ref (texture); /* guard against unparent */
1109   g_object_freeze_notify (G_OBJECT (texture));
1110
1111   /* FIXME: mapped is always FALSE */
1112   clutter_x11_texture_pixmap_set_mapped (texture, mapped);
1113
1114   if (pixmap)
1115     {
1116       clutter_x11_texture_pixmap_set_pixmap (texture, pixmap);
1117       priv->owns_pixmap = TRUE;
1118     }
1119
1120   if (notify_override_redirect)
1121     g_object_notify (G_OBJECT (texture), "window-override-redirect");
1122   if (notify_x)
1123     g_object_notify (G_OBJECT (texture), "window-x");
1124   if (notify_y)
1125     g_object_notify (G_OBJECT (texture), "window-y");
1126
1127   g_object_thaw_notify (G_OBJECT (texture));
1128   g_object_unref (texture);
1129 }
1130
1131 /**
1132  * clutter_x11_texture_pixmap_sync_window:
1133  * @texture: the texture to bind
1134  *
1135  * Resets the texture's pixmap from its window, perhaps in response to the
1136  * pixmap's invalidation as the window changed size.
1137  *
1138  * Since: 0.8
1139  */
1140 void
1141 clutter_x11_texture_pixmap_sync_window (ClutterX11TexturePixmap *texture)
1142 {
1143   ClutterX11TexturePixmapPrivate *priv;
1144
1145   g_return_if_fail (CLUTTER_X11_IS_TEXTURE_PIXMAP (texture));
1146
1147   priv = texture->priv;
1148
1149   if (priv->destroyed)
1150     return;
1151
1152   if (priv->window != None)
1153     {
1154       Display *dpy = clutter_x11_get_default_display ();
1155       XWindowAttributes attr;
1156       Status status;
1157
1158       if (dpy == NULL)
1159         return;
1160
1161       clutter_x11_trap_x_errors ();
1162
1163       status = XGetWindowAttributes (dpy, priv->window, &attr);
1164       if (status != 0)
1165         clutter_x11_texture_pixmap_sync_window_internal (texture,
1166                                                          attr.x, attr.y,
1167                                                          attr.width, attr.height,
1168                                                          attr.override_redirect);
1169
1170       clutter_x11_untrap_x_errors ();
1171     }
1172 }
1173
1174 static void
1175 clutter_x11_texture_pixmap_set_mapped (ClutterX11TexturePixmap *texture,
1176                                        gboolean mapped)
1177 {
1178   ClutterX11TexturePixmapPrivate *priv;
1179
1180   priv = texture->priv;
1181
1182   if (mapped != priv->window_mapped)
1183     {
1184       priv->window_mapped = mapped;
1185       g_object_notify (G_OBJECT (texture), "window-mapped");
1186     }
1187 }
1188
1189 static void
1190 clutter_x11_texture_pixmap_destroyed (ClutterX11TexturePixmap *texture)
1191 {
1192   ClutterX11TexturePixmapPrivate *priv;
1193
1194   priv = texture->priv;
1195
1196   if (!priv->destroyed)
1197     {
1198       priv->destroyed = TRUE;
1199       g_object_notify (G_OBJECT (texture), "destroyed");
1200     }
1201
1202   /*
1203    * Don't set window to None, that would destroy the pixmap, which might still
1204    * be useful e.g. for destroy animations -- app's responsibility.
1205    */
1206 }
1207
1208 /**
1209  * clutter_x11_texture_pixmap_update_area:
1210  * @texture: The texture whose content shall be updated.
1211  * @x: the X coordinate of the area to update
1212  * @y: the Y coordinate of the area to update
1213  * @width: the width of the area to update
1214  * @height: the height of the area to update
1215  *
1216  * Performs the actual binding of texture to the current content of
1217  * the pixmap. Can be called to update the texture if the pixmap
1218  * content has changed.
1219  *
1220  * Since: 0.8
1221  **/
1222 void
1223 clutter_x11_texture_pixmap_update_area (ClutterX11TexturePixmap *texture,
1224                                         gint                     x,
1225                                         gint                     y,
1226                                         gint                     width,
1227                                         gint                     height)
1228 {
1229   g_return_if_fail (CLUTTER_X11_IS_TEXTURE_PIXMAP (texture));
1230
1231   g_signal_emit (texture, signals[UPDATE_AREA], 0, x, y, width, height);
1232
1233   /* The default handler for the "queue-damage-redraw" signal is
1234    * clutter_x11_texture_pixmap_real_queue_damage_redraw which will queue a
1235    * clipped redraw. */
1236   g_signal_emit (texture, signals[QUEUE_DAMAGE_REDRAW],
1237                  0, x, y, width, height);
1238 }
1239
1240 /**
1241  * clutter_x11_texture_pixmap_set_automatic:
1242  * @texture: a #ClutterX11TexturePixmap
1243  * @setting: %TRUE to enable automatic updates
1244  *
1245  * Enables or disables the automatic updates ot @texture in case the backing
1246  * pixmap or window is damaged
1247  *
1248  * Since: 0.8
1249  */
1250 void
1251 clutter_x11_texture_pixmap_set_automatic (ClutterX11TexturePixmap *texture,
1252                                           gboolean                 setting)
1253 {
1254   ClutterX11TexturePixmapPrivate *priv;
1255
1256   g_return_if_fail (CLUTTER_X11_IS_TEXTURE_PIXMAP (texture));
1257
1258   priv = texture->priv;
1259
1260   setting = !!setting;
1261   if (setting == priv->automatic_updates)
1262     return;
1263
1264   if (setting)
1265     create_damage_resources (texture);
1266   else
1267     free_damage_resources (texture);
1268
1269   priv->automatic_updates = setting;
1270 }