added overlay and stereo input
[geeqie.git] / src / renderer-clutter.c
1 /*
2  * Geeqie
3  * (C) 2006 John Ellis
4  * Copyright (C) 2008 - 2012 The Geeqie Team
5  *
6  * Author: John Ellis
7  * Author: Vladimir Nadvornik
8  *
9  * This software is released under the GNU General Public License (GNU GPL).
10  * Please read the included file COPYING for more information.
11  * This software comes with no warranty of any kind, use at your own risk!
12  */
13
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <math.h>
18
19 #include "main.h"
20 #include "pixbuf-renderer.h"
21 #include "renderer-clutter.h"
22
23 #include "intl.h"
24 #include "layout.h"
25
26 #include <gtk/gtk.h>
27
28 #ifdef HAVE_CLUTTER
29
30 #include <clutter/clutter.h>
31
32 #include <clutter-gtk/clutter-gtk.h>
33
34
35
36 #define GQ_BUILD 1
37
38 #ifdef GQ_BUILD
39 #include "main.h"
40 #include "pixbuf_util.h"
41 #include "exif.h"
42 #else
43 typedef enum {
44         EXIF_ORIENTATION_UNKNOWN        = 0,
45         EXIF_ORIENTATION_TOP_LEFT       = 1,
46         EXIF_ORIENTATION_TOP_RIGHT      = 2,
47         EXIF_ORIENTATION_BOTTOM_RIGHT   = 3,
48         EXIF_ORIENTATION_BOTTOM_LEFT    = 4,
49         EXIF_ORIENTATION_LEFT_TOP       = 5,
50         EXIF_ORIENTATION_RIGHT_TOP      = 6,
51         EXIF_ORIENTATION_RIGHT_BOTTOM   = 7,
52         EXIF_ORIENTATION_LEFT_BOTTOM    = 8
53 } ExifOrientationType;
54 #endif
55
56 #define GET_RIGHT_PIXBUF_OFFSET(rc) \
57         (( (rc->stereo_mode & PR_STEREO_RIGHT) && !(rc->stereo_mode & PR_STEREO_SWAP)) || \
58          (!(rc->stereo_mode & PR_STEREO_RIGHT) &&  (rc->stereo_mode & PR_STEREO_SWAP)) ?  \
59           rc->pr->stereo_pixbuf_offset_right : rc->pr->stereo_pixbuf_offset_left )
60
61 #define GET_LEFT_PIXBUF_OFFSET(rc) \
62         ((!(rc->stereo_mode & PR_STEREO_RIGHT) && !(rc->stereo_mode & PR_STEREO_SWAP)) || \
63          ( (rc->stereo_mode & PR_STEREO_RIGHT) &&  (rc->stereo_mode & PR_STEREO_SWAP)) ?  \
64           rc->pr->stereo_pixbuf_offset_right : rc->pr->stereo_pixbuf_offset_left )
65
66
67 typedef struct _OverlayData OverlayData;
68 struct _OverlayData
69 {
70         gint id;
71
72         GdkPixbuf *pixbuf;
73         ClutterActor *actor;
74
75         gint x;
76         gint y;
77
78         OverlayRendererFlags flags;
79 };
80
81 typedef struct _RendererClutter RendererClutter;
82
83 struct _RendererClutter
84 {
85         RendererFuncs f;
86         PixbufRenderer *pr;
87
88         
89         gint stereo_mode;
90         gint stereo_off_x;
91         gint stereo_off_y;
92         
93         
94         GList *pending_updates;
95         gint idle_update;
96         
97         GList *overlay_list;
98         
99         GtkWidget *widget; /* widget and stage may be shared with other renderers */
100         ClutterActor *stage;
101         ClutterActor *texture;
102         ClutterActor *group;
103 };
104
105 typedef struct _RendererClutterAreaParam RendererClutterAreaParam;
106 struct _RendererClutterAreaParam {
107         RendererClutter *rc;
108         gint x;
109         gint y;
110         gint w;
111         gint h;
112 };
113
114
115 static void rc_sync_actor(RendererClutter *rc)
116 {
117         PixbufRenderer *pr = rc->pr;
118         gint anchor_x = 0;
119         gint anchor_y = 0;
120         
121         clutter_actor_set_anchor_point(CLUTTER_ACTOR(rc->texture), 0, 0);
122
123         printf("scale %d %d\n", rc->pr->width, rc->pr->height);
124         printf("pos   %d %d\n", rc->pr->x_offset, rc->pr->y_offset);
125         
126         switch (pr->orientation)
127                 {
128                 case EXIF_ORIENTATION_TOP_LEFT:
129                         /* normal  */
130                         clutter_actor_set_rotation(CLUTTER_ACTOR(rc->texture),
131                                                 CLUTTER_Z_AXIS,
132                                                 0, 0, 0, 0);
133                         clutter_actor_set_rotation(CLUTTER_ACTOR(rc->texture),
134                                                 CLUTTER_Y_AXIS,
135                                                 0, 0, 0, 0);
136                         clutter_actor_set_size(CLUTTER_ACTOR(rc->texture), pr->width, pr->height);
137                         anchor_x = 0;
138                         anchor_y = 0;
139                         break;
140                 case EXIF_ORIENTATION_TOP_RIGHT:
141                         /* mirrored */
142                         clutter_actor_set_rotation(CLUTTER_ACTOR(rc->texture),
143                                                 CLUTTER_Z_AXIS,
144                                                 0, 0, 0, 0);
145                         clutter_actor_set_rotation(CLUTTER_ACTOR(rc->texture),
146                                                 CLUTTER_Y_AXIS,
147                                                 180, 0, 0, 0);
148                         clutter_actor_set_size(CLUTTER_ACTOR(rc->texture), pr->width, pr->height);
149                         anchor_x = pr->width;
150                         anchor_y = 0;
151                         break;
152                 case EXIF_ORIENTATION_BOTTOM_RIGHT:
153                         /* upside down */
154                         clutter_actor_set_rotation(CLUTTER_ACTOR(rc->texture),
155                                                 CLUTTER_Z_AXIS,
156                                                 180, 0, 0, 0);
157                         clutter_actor_set_rotation(CLUTTER_ACTOR(rc->texture),
158                                                 CLUTTER_Y_AXIS,
159                                                 0, 0, 0, 0);
160                         clutter_actor_set_size(CLUTTER_ACTOR(rc->texture), pr->width, pr->height);
161                         anchor_x = pr->width;
162                         anchor_y = pr->height;
163                         break;
164                 case EXIF_ORIENTATION_BOTTOM_LEFT:
165                         /* flipped */
166                         clutter_actor_set_rotation(CLUTTER_ACTOR(rc->texture),
167                                                 CLUTTER_Z_AXIS,
168                                                 180, 0, 0, 0);
169                         clutter_actor_set_rotation(CLUTTER_ACTOR(rc->texture),
170                                                 CLUTTER_Y_AXIS,
171                                                 180, 0, 0, 0);
172                         clutter_actor_set_size(CLUTTER_ACTOR(rc->texture), pr->width, pr->height);
173                         anchor_x = 0;
174                         anchor_y = pr->height;
175                         break;
176                 case EXIF_ORIENTATION_LEFT_TOP:
177                         clutter_actor_set_rotation(CLUTTER_ACTOR(rc->texture),
178                                                 CLUTTER_Z_AXIS,
179                                                 -90, 0, 0, 0);
180                         clutter_actor_set_rotation(CLUTTER_ACTOR(rc->texture),
181                                                 CLUTTER_Y_AXIS,
182                                                 180, 0, 0, 0);
183                         clutter_actor_set_size(CLUTTER_ACTOR(rc->texture), pr->height, pr->width);
184                         anchor_x = 0;
185                         anchor_y = 0;
186                         break;
187                 case EXIF_ORIENTATION_RIGHT_TOP:
188                         /* rotated -90 (270) */
189                         clutter_actor_set_rotation(CLUTTER_ACTOR(rc->texture),
190                                                 CLUTTER_Z_AXIS,
191                                                 -90, 0, 0, 0);
192                         clutter_actor_set_rotation(CLUTTER_ACTOR(rc->texture),
193                                                 CLUTTER_Y_AXIS,
194                                                 0, 0, 0, 0);
195                         clutter_actor_set_size(CLUTTER_ACTOR(rc->texture), pr->height, pr->width);
196                         anchor_x = 0;
197                         anchor_y = pr->height;
198                         break;
199                 case EXIF_ORIENTATION_RIGHT_BOTTOM:
200                         clutter_actor_set_rotation(CLUTTER_ACTOR(rc->texture),
201                                                 CLUTTER_Z_AXIS,
202                                                 90, 0, 0, 0);
203                         clutter_actor_set_rotation(CLUTTER_ACTOR(rc->texture),
204                                                 CLUTTER_Y_AXIS,
205                                                 180, 0, 0, 0);
206                         clutter_actor_set_size(CLUTTER_ACTOR(rc->texture), pr->height, pr->width);
207                         anchor_x = pr->width;
208                         anchor_y = pr->height;
209                         break;
210                 case EXIF_ORIENTATION_LEFT_BOTTOM:
211                         /* rotated 90 */
212                         clutter_actor_set_rotation(CLUTTER_ACTOR(rc->texture),
213                                                 CLUTTER_Z_AXIS,
214                                                 90, 0, 0, 0);
215                         clutter_actor_set_rotation(CLUTTER_ACTOR(rc->texture),
216                                                 CLUTTER_Y_AXIS,
217                                                 0, 0, 0, 0);
218                         clutter_actor_set_size(CLUTTER_ACTOR(rc->texture), pr->height, pr->width);
219                         anchor_x = pr->width;
220                         anchor_y = 0;
221                         break;
222                 default:
223                         /* The other values are out of range */
224                         break;
225                 }
226         
227         clutter_actor_set_position(CLUTTER_ACTOR(rc->texture), 
228                                 pr->x_offset - pr->x_scroll + anchor_x, 
229                                 pr->y_offset - pr->y_scroll + anchor_y);
230
231 }
232
233 #define MAX_REGION_AREA (8192 * 1024)
234
235 static gboolean renderer_area_changed_cb(gpointer data)
236 {
237         RendererClutter *rc = (RendererClutter *)data;
238         PixbufRenderer *pr = rc->pr;
239         
240         RendererClutterAreaParam *par = rc->pending_updates->data;
241         
242         gint h = MAX_REGION_AREA / par->w;
243         if (h == 0) h = 1;
244         if (h > par->h) h = par->h;
245         
246         
247         printf("renderer_area_changed_cb %d %d %d %d  (%d)\n", par->x, par->y, par->w, h, par->h);
248         if (pr->pixbuf)
249                 {
250                 CoglHandle texture = clutter_texture_get_cogl_texture(CLUTTER_TEXTURE(rc->texture));
251                 
252                 cogl_texture_set_region(texture,
253                                         par->x + GET_RIGHT_PIXBUF_OFFSET(rc),
254                                         par->y,
255                                         par->x,
256                                         par->y,
257                                         par->w,
258                                         h,
259                                         par->w,
260                                         h,
261                                         gdk_pixbuf_get_has_alpha(pr->pixbuf) ? COGL_PIXEL_FORMAT_RGBA_8888 : COGL_PIXEL_FORMAT_RGB_888,
262                                         gdk_pixbuf_get_rowstride(pr->pixbuf),
263                                         gdk_pixbuf_get_pixels(pr->pixbuf));
264                 }
265                 
266         par->y += h;
267         par->h -= h;
268         
269         if (par->h == 0)
270                 {
271                 rc->pending_updates = g_list_remove(rc->pending_updates, par);
272                 g_free(par);
273                 }
274         if (!rc->pending_updates)
275                 {
276                 clutter_actor_queue_redraw(CLUTTER_ACTOR(rc->texture));
277                 rc->idle_update = 0;
278                 return FALSE;
279                 }
280         return TRUE;
281 }
282
283
284 static void renderer_area_changed(void *renderer, gint src_x, gint src_y, gint src_w, gint src_h)
285 {
286         RendererClutter *rc = (RendererClutter *)renderer;
287         PixbufRenderer *pr = rc->pr;
288         RendererClutterAreaParam *par;
289
290         gint width = gdk_pixbuf_get_width(pr->pixbuf);
291         gint height = gdk_pixbuf_get_height(pr->pixbuf);
292                 
293         if (pr->stereo_data == STEREO_PIXBUF_SBS || pr->stereo_data == STEREO_PIXBUF_CROSS) 
294                         {
295                         width /= 2;
296                         }
297         
298         if (!pr_clip_region(src_x, src_y, src_w, src_h,
299                             GET_RIGHT_PIXBUF_OFFSET(rc), 0, width, height,
300                             &src_x, &src_y, &src_w, &src_h)) return;
301         
302         par = g_new0(RendererClutterAreaParam, 1);
303         par->rc = rc;
304         par->x = src_x - GET_RIGHT_PIXBUF_OFFSET(rc);
305         par->y = src_y;
306         par->w = src_w;
307         par->h = src_h;
308         rc->pending_updates = g_list_append(rc->pending_updates, par);
309         if (!rc->idle_update) 
310                 {
311                 rc->idle_update = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, renderer_area_changed_cb, rc, NULL);
312                 }
313 }
314
315 static void renderer_remove_pending_updates(RendererClutter *rc)
316 {
317         if (rc->idle_update) g_idle_remove_by_data(rc);
318         rc->idle_update = 0;
319         while (rc->pending_updates)
320                 {
321                 RendererClutterAreaParam *par = rc->pending_updates->data;
322                 rc->pending_updates = g_list_remove(rc->pending_updates, par);
323                 g_free(par);
324                 }
325 }
326
327 static void renderer_update_pixbuf(void *renderer, gboolean lazy)
328 {
329         RendererClutter *rc = (RendererClutter *)renderer;
330         PixbufRenderer *pr = rc->pr;
331         
332         renderer_remove_pending_updates(rc);
333         
334         if (pr->pixbuf)
335                 {
336                 gint width = gdk_pixbuf_get_width(pr->pixbuf);
337                 gint height = gdk_pixbuf_get_height(pr->pixbuf);
338                 
339                 gint prev_width, prev_height;
340                 
341                 if (pr->stereo_data == STEREO_PIXBUF_SBS || pr->stereo_data == STEREO_PIXBUF_CROSS) 
342                         {
343                         width /= 2;
344                         }
345
346                 
347                 printf("renderer_update_pixbuf\n");
348                 clutter_texture_get_base_size(CLUTTER_TEXTURE(rc->texture), &prev_width, &prev_height);
349                 printf("change from %d %d to %d %d\n", prev_width, prev_height, width, height);
350                 
351                 if (width != prev_width || height != prev_height)
352                         {
353                         /* FIXME use CoglMaterial with multiple textures for background, color management, anaglyph, ... */
354                         CoglHandle texture = cogl_texture_new_with_size(width,
355                                                                         height,
356                                                                         COGL_TEXTURE_NONE,
357                                                                         gdk_pixbuf_get_has_alpha(pr->pixbuf) ? COGL_PIXEL_FORMAT_RGBA_8888 : COGL_PIXEL_FORMAT_RGB_888);
358
359                         if (texture != COGL_INVALID_HANDLE)
360                                 {
361                                 clutter_texture_set_cogl_texture(CLUTTER_TEXTURE(rc->texture), texture);
362                                 cogl_handle_unref(texture);
363                                 }
364                         }
365                 if (!lazy)
366                         {
367                         renderer_area_changed(renderer, GET_RIGHT_PIXBUF_OFFSET(rc), 0, width, height);
368                         }
369                 }
370
371
372         printf("renderer_update_pixbuf\n");
373         rc_sync_actor(rc);
374 }
375
376
377
378 static void renderer_update_zoom(void *renderer, gboolean lazy)
379 {
380         RendererClutter *rc = (RendererClutter *)renderer;
381         PixbufRenderer *pr = rc->pr;
382
383         printf("renderer_update_zoom\n");
384         rc_sync_actor(rc);
385 }
386
387 static void renderer_invalidate_region(void *renderer, gint x, gint y, gint w, gint h)
388 {
389 }
390
391 static OverlayData *rc_overlay_find(RendererClutter *rc, gint id)
392 {
393         GList *work;
394
395         work = rc->overlay_list;
396         while (work)
397                 {
398                 OverlayData *od = work->data;
399                 work = work->next;
400
401                 if (od->id == id) return od;
402                 }
403
404         return NULL;
405 }
406
407 static void rc_overlay_actor_destroy_cb(ClutterActor *actor, gpointer user_data)
408 {
409         OverlayData *od = user_data;
410         od->actor = NULL;
411 }
412
413 static void rc_overlay_free(RendererClutter *rc, OverlayData *od)
414 {
415         rc->overlay_list = g_list_remove(rc->overlay_list, od);
416
417         if (od->pixbuf) g_object_unref(G_OBJECT(od->pixbuf));
418         if (od->actor) clutter_actor_destroy(od->actor);
419         g_free(od);
420 }
421
422 static void rc_overlay_update_position(RendererClutter *rc, OverlayData *od)
423 {
424         gint px, py, pw, ph;
425
426         pw = gdk_pixbuf_get_width(od->pixbuf);
427         ph = gdk_pixbuf_get_height(od->pixbuf);
428         px = od->x;
429         py = od->y;
430
431         if (od->flags & OVL_RELATIVE)
432                 {
433                 if (px < 0) px = rc->pr->viewport_width - pw + px;
434                 if (py < 0) py = rc->pr->viewport_height - ph + py;
435                 }
436         if (od->actor) clutter_actor_set_position(od->actor, px, py);
437 }
438
439 static void rc_overlay_update_positions(RendererClutter *rc)
440 {
441         GList *work;
442
443         work = rc->overlay_list;
444         while (work)
445                 {
446                 OverlayData *od = work->data;
447                 work = work->next;
448
449                 rc_overlay_update_position(rc, od);
450                 }
451 }
452
453 static void rc_overlay_free_all(RendererClutter *rc)
454 {
455         GList *work;
456
457         work = rc->overlay_list;
458         while (work)
459                 {
460                 OverlayData *od = work->data;
461                 work = work->next;
462
463                 rc_overlay_free(rc, od);
464                 }
465 }
466
467
468 static void renderer_overlay_draw(void *renderer, gint x, gint y, gint w, gint h)
469 {
470 }
471
472 static gint renderer_overlay_add(void *renderer, GdkPixbuf *pixbuf, gint x, gint y, OverlayRendererFlags flags)
473 {
474         RendererClutter *rc = (RendererClutter *)renderer;
475         PixbufRenderer *pr = rc->pr;
476         OverlayData *od;
477         gint id;
478
479         g_return_val_if_fail(IS_PIXBUF_RENDERER(pr), -1);
480         g_return_val_if_fail(pixbuf != NULL, -1);
481
482         id = 1;
483         while (rc_overlay_find(rc, id)) id++;
484
485         od = g_new0(OverlayData, 1);
486         od->id = id;
487         od->pixbuf = pixbuf;
488         g_object_ref(G_OBJECT(od->pixbuf));
489         od->x = x;
490         od->y = y;
491         od->flags = flags;
492         
493         od->actor = gtk_clutter_texture_new();
494         g_signal_connect (od->actor, "destroy", G_CALLBACK(rc_overlay_actor_destroy_cb), od);
495         
496         gtk_clutter_texture_set_from_pixbuf(GTK_CLUTTER_TEXTURE (od->actor), pixbuf, NULL);
497         clutter_container_add_actor(CLUTTER_CONTAINER(rc->group), od->actor);
498
499         rc->overlay_list = g_list_append(rc->overlay_list, od);
500         rc_overlay_update_position(rc, od);
501
502         return od->id;
503 }
504
505 static void renderer_overlay_set(void *renderer, gint id, GdkPixbuf *pixbuf, gint x, gint y)
506 {
507         RendererClutter *rc = (RendererClutter *)renderer;
508         PixbufRenderer *pr = rc->pr;
509         OverlayData *od;
510
511         g_return_if_fail(IS_PIXBUF_RENDERER(pr));
512
513         od = rc_overlay_find(rc, id);
514         if (!od) return;
515
516         if (pixbuf)
517                 {
518                 g_object_ref(G_OBJECT(pixbuf));
519                 g_object_unref(G_OBJECT(od->pixbuf));
520                 od->pixbuf = pixbuf;
521
522                 od->x = x;
523                 od->y = y;
524
525                 if (od->actor) gtk_clutter_texture_set_from_pixbuf(GTK_CLUTTER_TEXTURE(od->actor), pixbuf, NULL);
526                 rc_overlay_update_position(rc, od);
527                 }
528         else
529                 {
530                 rc_overlay_free(rc, od);
531                 }
532 }
533
534 static gboolean renderer_overlay_get(void *renderer, gint id, GdkPixbuf **pixbuf, gint *x, gint *y)
535 {
536         RendererClutter *rc = (RendererClutter *)renderer;
537
538         PixbufRenderer *pr = rc->pr;
539         OverlayData *od;
540
541         g_return_val_if_fail(IS_PIXBUF_RENDERER(pr), FALSE);
542
543         od = rc_overlay_find(rc, id);
544         if (!od) return FALSE;
545
546         if (pixbuf) *pixbuf = od->pixbuf;
547         if (x) *x = od->x;
548         if (y) *y = od->y;
549
550         return TRUE;
551 }
552
553
554 static void renderer_update_sizes(void *renderer)
555 {
556         RendererClutter *rc = (RendererClutter *)renderer;
557         ClutterColor stage_color = { 0x00, 0x00, 0x00, 0xff }; 
558
559         rc->stereo_off_x = 0;
560         rc->stereo_off_y = 0;
561         
562         if (rc->stereo_mode & PR_STEREO_RIGHT) 
563                 {
564                 if (rc->stereo_mode & PR_STEREO_HORIZ) 
565                         {
566                         rc->stereo_off_x = rc->pr->viewport_width;
567                         }
568                 else if (rc->stereo_mode & PR_STEREO_VERT) 
569                         {
570                         rc->stereo_off_y = rc->pr->viewport_height;
571                         }
572                 else if (rc->stereo_mode & PR_STEREO_FIXED) 
573                         {
574                         rc->stereo_off_x = rc->pr->stereo_fixed_x_right;
575                         rc->stereo_off_y = rc->pr->stereo_fixed_y_right;
576                         }
577                 }
578         else
579                 {
580                 if (rc->stereo_mode & PR_STEREO_FIXED) 
581                         {
582                         rc->stereo_off_x = rc->pr->stereo_fixed_x_left;
583                         rc->stereo_off_y = rc->pr->stereo_fixed_y_left;
584                         }
585                 }
586         DEBUG_1("update size: %p  %d %d   %d %d", rc, rc->stereo_off_x, rc->stereo_off_y, rc->pr->viewport_width, rc->pr->viewport_height);
587
588         printf("renderer_update_sizes  scale %d %d\n", rc->pr->width, rc->pr->height);
589
590         clutter_stage_set_color(CLUTTER_STAGE(rc->stage), &stage_color);
591
592
593         clutter_actor_set_size(rc->group, rc->pr->viewport_width, rc->pr->viewport_height);
594         clutter_actor_set_position(rc->group, rc->stereo_off_x, rc->stereo_off_y);
595         
596         clutter_actor_set_rotation(CLUTTER_ACTOR(rc->group),
597                                                 CLUTTER_Y_AXIS,
598                                                 (rc->stereo_mode & PR_STEREO_MIRROR) ? 180 : 0, 
599                                                 rc->pr->viewport_width / 2.0, 0, 0);
600
601         clutter_actor_set_rotation(CLUTTER_ACTOR(rc->group),
602                                                 CLUTTER_X_AXIS,
603                                                 (rc->stereo_mode & PR_STEREO_FLIP) ? 180 : 0,
604                                                 0, rc->pr->viewport_height / 2.0, 0);
605
606         rc_sync_actor(rc);
607         rc_overlay_update_positions(rc);
608 }
609
610 static void renderer_scroll(void *renderer, gint x_off, gint y_off)
611 {
612         printf("renderer_scroll\n");
613         RendererClutter *rc = (RendererClutter *)renderer;
614         PixbufRenderer *pr = rc->pr;
615
616         rc_sync_actor(rc);
617 }
618
619 static void renderer_stereo_set(void *renderer, gint stereo_mode)
620 {
621         RendererClutter *rc = (RendererClutter *)renderer;
622
623         rc->stereo_mode = stereo_mode;
624 }
625
626 static void renderer_free(void *renderer)
627 {
628         RendererClutter *rc = (RendererClutter *)renderer;
629         GtkWidget *widget = gtk_bin_get_child(GTK_BIN(rc->pr));
630
631         renderer_remove_pending_updates(rc);
632
633         rc_overlay_free_all(rc);
634         
635         if (widget)
636                 {
637                 /* widget still exists */
638                 clutter_actor_destroy(rc->group);
639                 if (clutter_group_get_n_children(CLUTTER_GROUP(rc->stage)) == 0)
640                         {
641                         printf("destroy %p\n", rc->widget);
642                         /* this was the last user */
643                         gtk_widget_destroy(rc->widget);
644                         }
645                 else
646                         {
647                         printf("keep %p\n", rc->widget);
648                         g_object_unref(G_OBJECT(rc->widget));
649                         }
650                 }
651         g_free(rc);
652 }
653
654 RendererFuncs *renderer_clutter_new(PixbufRenderer *pr)
655 {
656         RendererClutter *rc = g_new0(RendererClutter, 1);
657         
658         rc->pr = pr;
659         
660         rc->f.area_changed = renderer_area_changed;
661         rc->f.update_pixbuf = renderer_update_pixbuf;
662         rc->f.free = renderer_free;
663         rc->f.update_zoom = renderer_update_zoom;
664         rc->f.invalidate_region = renderer_invalidate_region;
665         rc->f.scroll = renderer_scroll;
666         rc->f.update_sizes = renderer_update_sizes;
667
668
669         rc->f.overlay_add = renderer_overlay_add;
670         rc->f.overlay_set = renderer_overlay_set;
671         rc->f.overlay_get = renderer_overlay_get;
672         rc->f.overlay_draw = renderer_overlay_draw;
673
674         rc->f.stereo_set = renderer_stereo_set;
675         
676         
677         rc->stereo_mode = 0;
678         rc->stereo_off_x = 0;
679         rc->stereo_off_y = 0;
680
681         rc->idle_update = 0;
682         rc->pending_updates = NULL;
683
684         rc->widget = gtk_bin_get_child(GTK_BIN(rc->pr));
685         
686         if (rc->widget)
687                 {
688                 if (!GTK_CLUTTER_IS_EMBED(rc->widget))
689                         {
690                         g_free(rc);
691                         DEBUG_0("pixbuf renderer has a child of other type than gtk_clutter_embed");
692                         return NULL;
693                         }
694                 }
695         else 
696                 {
697                 rc->widget = gtk_clutter_embed_new();
698                 gtk_container_add(GTK_CONTAINER(rc->pr), rc->widget);
699                 }
700                 
701         gtk_event_box_set_above_child (GTK_EVENT_BOX(rc->pr), TRUE);
702         rc->stage = gtk_clutter_embed_get_stage (GTK_CLUTTER_EMBED (rc->widget));
703         
704         rc->group = clutter_group_new();
705         clutter_container_add_actor(CLUTTER_CONTAINER(rc->stage), rc->group);
706         clutter_actor_set_clip_to_allocation(CLUTTER_ACTOR(rc->group), TRUE);
707   
708         rc->texture = clutter_texture_new ();
709         clutter_container_add_actor(CLUTTER_CONTAINER(rc->group), rc->texture);
710         g_object_ref(G_OBJECT(rc->widget));
711   
712         gtk_widget_show(rc->widget);
713         return (RendererFuncs *) rc;
714 }
715
716 #endif 
717 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */