Remove all internal use of deprecated cogl_clip_* API
[profile/ivi/clutter.git] / clutter / clutter-main.c
1 /*
2  * Clutter.
3  *
4  * An OpenGL based 'interactive canvas' library.
5  *
6  * Authored By Matthew Allum  <mallum@openedhand.com>
7  *
8  * Copyright (C) 2006 OpenedHand
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 2 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library; if not, write to the
22  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23  * Boston, MA 02111-1307, USA.
24  */
25 /**
26  * SECTION:clutter-main
27  * @short_description: Various 'global' clutter functions.
28  *
29  * Functions to retrieve various global Clutter resources and other utility
30  * functions for mainloops, events and threads
31  */
32
33 #ifdef HAVE_CONFIG_H
34 #include "config.h"
35 #endif
36
37 #include <stdlib.h>
38 #include <glib/gi18n-lib.h>
39 #include <locale.h>
40
41 #ifdef USE_GDKPIXBUF
42 #include <gdk-pixbuf/gdk-pixbuf.h>
43 #endif
44
45 #include "clutter-event.h"
46 #include "clutter-backend.h"
47 #include "clutter-main.h"
48 #include "clutter-master-clock.h"
49 #include "clutter-feature.h"
50 #include "clutter-actor.h"
51 #include "clutter-stage.h"
52 #include "clutter-private.h"
53 #include "clutter-debug.h"
54 #include "clutter-version.h"    /* For flavour define */
55 #include "clutter-frame-source.h"
56
57 #include "cogl/cogl.h"
58 #include "pango/cogl-pango.h"
59
60 /* main context */
61 static ClutterMainContext *ClutterCntx       = NULL;
62
63 /* main lock and locking/unlocking functions */
64 static GMutex *clutter_threads_mutex         = NULL;
65 static GCallback clutter_threads_lock        = NULL;
66 static GCallback clutter_threads_unlock      = NULL;
67
68 /* command line options */
69 static gboolean clutter_is_initialized       = FALSE;
70 static gboolean clutter_show_fps             = FALSE;
71 static gboolean clutter_fatal_warnings       = FALSE;
72 static gboolean clutter_disable_mipmap_text  = FALSE;
73 static gboolean clutter_use_fuzzy_picking    = FALSE;
74
75 static guint clutter_default_fps             = 60;
76
77 static PangoDirection clutter_text_direction = PANGO_DIRECTION_LTR;
78
79 static guint clutter_main_loop_level         = 0;
80 static GSList *main_loops                    = NULL;
81
82 guint clutter_debug_flags = 0;  /* global clutter debug flag */
83
84 const guint clutter_major_version = CLUTTER_MAJOR_VERSION;
85 const guint clutter_minor_version = CLUTTER_MINOR_VERSION;
86 const guint clutter_micro_version = CLUTTER_MICRO_VERSION;
87
88 #ifdef CLUTTER_ENABLE_DEBUG
89 static const GDebugKey clutter_debug_keys[] = {
90   { "misc", CLUTTER_DEBUG_MISC },
91   { "actor", CLUTTER_DEBUG_ACTOR },
92   { "texture", CLUTTER_DEBUG_TEXTURE },
93   { "event", CLUTTER_DEBUG_EVENT },
94   { "paint", CLUTTER_DEBUG_PAINT },
95   { "gl", CLUTTER_DEBUG_GL },
96   { "alpha", CLUTTER_DEBUG_ALPHA },
97   { "behaviour", CLUTTER_DEBUG_BEHAVIOUR },
98   { "pango", CLUTTER_DEBUG_PANGO },
99   { "backend", CLUTTER_DEBUG_BACKEND },
100   { "scheduler", CLUTTER_DEBUG_SCHEDULER },
101   { "script", CLUTTER_DEBUG_SCRIPT },
102   { "shader", CLUTTER_DEBUG_SHADER },
103   { "multistage", CLUTTER_DEBUG_MULTISTAGE },
104   { "animation", CLUTTER_DEBUG_ANIMATION },
105   { "layout", CLUTTER_DEBUG_LAYOUT },
106   { "nop-picking", CLUTTER_DEBUG_NOP_PICKING },
107   { "dump-pick-buffers", CLUTTER_DEBUG_DUMP_PICK_BUFFERS }
108 };
109 #endif /* CLUTTER_ENABLE_DEBUG */
110
111 /**
112  * clutter_get_show_fps:
113  *
114  * Returns whether Clutter should print out the frames per second on the
115  * console. You can enable this setting either using the
116  * <literal>CLUTTER_SHOW_FPS</literal> environment variable or passing
117  * the <literal>--clutter-show-fps</literal> command line argument. *
118  *
119  * Return value: %TRUE if Clutter should show the FPS.
120  *
121  * Since: 0.4
122  */
123 gboolean
124 clutter_get_show_fps (void)
125 {
126   return clutter_show_fps;
127 }
128
129 void
130 _clutter_stage_maybe_relayout (ClutterActor *stage)
131 {
132   gfloat natural_width, natural_height;
133   ClutterActorBox box = { 0, };
134
135   /* avoid reentrancy */
136   if (!(CLUTTER_PRIVATE_FLAGS (stage) & CLUTTER_ACTOR_IN_RELAYOUT))
137     {
138       CLUTTER_NOTE (ACTOR, "Recomputing layout");
139
140       CLUTTER_SET_PRIVATE_FLAGS (stage, CLUTTER_ACTOR_IN_RELAYOUT);
141
142       natural_width = natural_height = 0;
143       clutter_actor_get_preferred_size (stage,
144                                         NULL, NULL,
145                                         &natural_width, &natural_height);
146
147       box.x1 = 0;
148       box.y1 = 0;
149       box.x2 = natural_width;
150       box.y2 = natural_height;
151
152       CLUTTER_NOTE (ACTOR, "Allocating (0, 0 - %d, %d) for the stage",
153                     (int) natural_width,
154                     (int) natural_height);
155
156       clutter_actor_allocate (stage, &box, CLUTTER_ALLOCATION_NONE);
157
158       CLUTTER_UNSET_PRIVATE_FLAGS (stage, CLUTTER_ACTOR_IN_RELAYOUT);
159     }
160 }
161
162 void
163 _clutter_stage_maybe_setup_viewport (ClutterStage *stage)
164 {
165   if ((CLUTTER_PRIVATE_FLAGS (stage) & CLUTTER_ACTOR_SYNC_MATRICES) &&
166       !(CLUTTER_PRIVATE_FLAGS (stage) & CLUTTER_STAGE_IN_RESIZE))
167     {
168       ClutterPerspective perspective;
169       gfloat width, height;
170
171       clutter_actor_get_preferred_size (CLUTTER_ACTOR (stage),
172                                         NULL, NULL,
173                                         &width, &height);
174       clutter_stage_get_perspective (stage, &perspective);
175
176       CLUTTER_NOTE (PAINT,
177                     "Setting up the viewport { w:%.2f, h:%.2f }",
178                     width, height);
179
180       _cogl_setup_viewport (width, height,
181                             perspective.fovy,
182                             perspective.aspect,
183                             perspective.z_near,
184                             perspective.z_far);
185
186       CLUTTER_UNSET_PRIVATE_FLAGS (stage, CLUTTER_ACTOR_SYNC_MATRICES);
187     }
188 }
189
190 void
191 _clutter_do_redraw (ClutterStage *stage)
192 {
193   ClutterMainContext *ctx;
194   ClutterMasterClock *master_clock;
195   static GTimer *timer = NULL;
196   static guint timer_n_frames = 0;
197
198   ctx  = _clutter_context_get_default ();
199   master_clock = _clutter_master_clock_get_default ();
200
201   /* Before we can paint, we have to be sure we have the latest layout */
202   _clutter_stage_maybe_relayout (CLUTTER_ACTOR (stage));
203
204   _clutter_backend_ensure_context (ctx->backend, stage);
205
206   /* Setup FPS count - not currently across *all* stages rather than per */
207   if (G_UNLIKELY (clutter_get_show_fps ()))
208     {
209       if (!timer)
210         timer = g_timer_new ();
211     }
212
213   /* The code below can't go in stage paint as base actor_paint
214    * will get called before it (and break picking, etc)
215    */
216   _clutter_stage_maybe_setup_viewport (stage);
217
218   /* Call through to the actual backend to do the painting down from
219    * the stage. It will likely need to swap buffers, vblank sync etc
220    * which will be windowing system dependent
221   */
222   _clutter_backend_redraw (ctx->backend, stage);
223
224   /* Complete FPS info */
225   if (G_UNLIKELY (clutter_get_show_fps ()))
226     {
227       timer_n_frames++;
228
229       if (g_timer_elapsed (timer, NULL) >= 1.0)
230         {
231           g_print ("*** FPS: %i ***\n", timer_n_frames);
232           timer_n_frames = 0;
233           g_timer_start (timer);
234         }
235     }
236
237   CLUTTER_TIMESTAMP (SCHEDULER, "Redraw finish for stage:%p", stage);
238 }
239
240 /**
241  * clutter_redraw:
242  *
243  * Forces a redraw of the entire stage. Applications should never use this
244  * function, but queue a redraw using clutter_actor_queue_redraw().
245  *
246  * This function should only be used by libraries integrating Clutter from
247  * within another toolkit.
248  */
249 void
250 clutter_redraw (ClutterStage *stage)
251 {
252   g_return_if_fail (CLUTTER_IS_STAGE (stage));
253
254   clutter_stage_ensure_redraw (stage);
255 }
256
257 /**
258  * clutter_set_motion_events_enabled:
259  * @enable: %TRUE to enable per-actor motion events
260  *
261  * Sets whether per-actor motion events should be enabled or not (the
262  * default is to enable them).
263  *
264  * If @enable is %FALSE the following events will not work:
265  * <itemizedlist>
266  *   <listitem><para>ClutterActor::motion-event, unless on the
267  *     #ClutterStage</para></listitem>
268  *   <listitem><para>ClutterActor::enter-event</para></listitem>
269  *   <listitem><para>ClutterActor::leave-event</para></listitem>
270  * </itemizedlist>
271  *
272  * Since: 0.6
273  */
274 void
275 clutter_set_motion_events_enabled (gboolean enable)
276 {
277   ClutterMainContext *context = _clutter_context_get_default ();
278
279   context->motion_events_per_actor = enable;
280 }
281
282 /**
283  * clutter_get_motion_events_enabled:
284  *
285  * Gets whether the per-actor motion events are enabled.
286  *
287  * Return value: %TRUE if the motion events are enabled
288  *
289  * Since: 0.6
290  */
291 gboolean
292 clutter_get_motion_events_enabled (void)
293 {
294   ClutterMainContext *context = _clutter_context_get_default ();
295
296   return context->motion_events_per_actor;
297 }
298
299 guint _clutter_pix_to_id (guchar pixel[4]);
300
301 static inline void init_bits (void)
302 {
303   ClutterMainContext *ctx;
304
305   static gboolean done = FALSE;
306   if (G_LIKELY (done))
307     return;
308
309   ctx = _clutter_context_get_default ();
310
311   done = TRUE;
312 }
313
314 void
315 _clutter_id_to_color (guint id, ClutterColor *col)
316 {
317   ClutterMainContext *ctx;
318   gint red, green, blue;
319
320   ctx = _clutter_context_get_default ();
321
322   /* compute the numbers we'll store in the components */
323   red   = (id >> (ctx->fb_g_mask_used+ctx->fb_b_mask_used))
324                 & (0xff >> (8-ctx->fb_r_mask_used));
325   green = (id >> ctx->fb_b_mask_used) & (0xff >> (8-ctx->fb_g_mask_used));
326   blue  = (id)  & (0xff >> (8-ctx->fb_b_mask_used));
327
328   /* shift left bits a bit and add one, this circumvents
329    * at least some potential rounding errors in GL/GLES
330    * driver / hw implementation.
331    */
332   if (ctx->fb_r_mask_used != ctx->fb_r_mask)
333     red = red * 2;
334   if (ctx->fb_g_mask_used != ctx->fb_g_mask)
335     green = green * 2;
336   if (ctx->fb_b_mask_used != ctx->fb_b_mask)
337     blue  = blue  * 2;
338
339   /* shift up to be full 8bit values */
340   red   = (red   << (8 - ctx->fb_r_mask)) | (0x7f >> (ctx->fb_r_mask_used));
341   green = (green << (8 - ctx->fb_g_mask)) | (0x7f >> (ctx->fb_g_mask_used));
342   blue  = (blue  << (8 - ctx->fb_b_mask)) | (0x7f >> (ctx->fb_b_mask_used));
343
344   col->red   = red;
345   col->green = green;
346   col->blue  = blue;
347   col->alpha = 0xff;
348
349   /* XXX: We rotate the nibbles of the colors here so that there is a
350    * visible variation between colors of sequential actor identifiers;
351    * otherwise pick buffers dumped to an image will pretty much just look
352    * black.
353    */
354   if (G_UNLIKELY (clutter_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS))
355     {
356       col->red = (col->red << 4) | (col->red >> 4);
357       col->green = (col->green << 4) | (col->green >> 4);
358       col->blue = (col->blue << 4) | (col->blue >> 4);
359     }
360 }
361
362 guint
363 _clutter_pixel_to_id (guchar pixel[4])
364 {
365   ClutterMainContext *ctx;
366   gint  red, green, blue;
367   guint id;
368
369   ctx = _clutter_context_get_default ();
370
371   /* reduce the pixel components to the number of bits actually used of the
372    * 8bits.
373    */
374   if (G_UNLIKELY (clutter_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS))
375     {
376       guchar tmp;
377
378       /* XXX: In _clutter_id_to_color we rotated the nibbles of the colors so
379        * that there is a visible variation between colors of sequential actor
380        * identifiers (otherwise pick buffers dumped to an image will pretty
381        * much just look black.) Here we reverse that rotation.
382        */
383       tmp = ((pixel[0] << 4) | (pixel[0] >> 4));
384       red = tmp >> (8 - ctx->fb_r_mask);
385       tmp = ((pixel[1] << 4) | (pixel[1] >> 4));
386       green = tmp >> (8 - ctx->fb_g_mask);
387       tmp = ((pixel[2] << 4) | (pixel[2] >> 4));
388       blue = tmp >> (8 - ctx->fb_b_mask);
389     }
390   else
391     {
392       red   = pixel[0] >> (8 - ctx->fb_r_mask);
393       green = pixel[1] >> (8 - ctx->fb_g_mask);
394       blue  = pixel[2] >> (8 - ctx->fb_b_mask);
395     }
396
397   /* divide potentially by two if 'fuzzy' */
398   red   = red   >> (ctx->fb_r_mask - ctx->fb_r_mask_used);
399   green = green >> (ctx->fb_g_mask - ctx->fb_g_mask_used);
400   blue  = blue  >> (ctx->fb_b_mask - ctx->fb_b_mask_used);
401
402   /* combine the correct per component values into the final id */
403   id =  blue + (green <<  ctx->fb_b_mask_used)
404           + (red << (ctx->fb_b_mask_used + ctx->fb_g_mask_used));
405
406   return id;
407 }
408
409 #ifdef USE_GDKPIXBUF
410 static void
411 pixbuf_free (guchar *pixels, gpointer data)
412 {
413   g_free (pixels);
414 }
415 #endif
416
417 static void
418 read_pixels_to_file (char *filename_stem,
419                      int x,
420                      int y,
421                      int width,
422                      int height)
423 {
424 #ifdef USE_GDKPIXBUF
425   GLubyte *data;
426   GdkPixbuf *pixbuf;
427   static int read_count = 0;
428
429   data = g_malloc (4 * width * height);
430   cogl_read_pixels (x, y, width, height,
431                     COGL_READ_PIXELS_COLOR_BUFFER,
432                     COGL_PIXEL_FORMAT_RGBA_8888,
433                     data);
434   pixbuf = gdk_pixbuf_new_from_data (data,
435                                      GDK_COLORSPACE_RGB,
436                                      TRUE, /* has alpha */
437                                      8, /* bits per sample */
438                                      width, /* width */
439                                      height, /* height */
440                                      width * 4, /* rowstride */
441                                      pixbuf_free, /* callback to free data */
442                                      NULL); /* callback data */
443   if (pixbuf)
444     {
445       char *filename =
446         g_strdup_printf ("%s-%05d.png", filename_stem, read_count);
447       GError *error = NULL;
448       if (!gdk_pixbuf_save (pixbuf, filename, "png", &error, NULL))
449         {
450           g_warning ("Failed to save pick buffer to file %s: %s",
451                      filename, error->message);
452           g_error_free (error);
453         }
454       g_free (filename);
455       g_object_unref (pixbuf);
456       read_count++;
457     }
458 #else
459   static gboolean seen = FALSE;
460   if (!seen)
461     {
462       g_warning ("dumping buffers to an image isn't supported on platforms "
463                  "without gdk pixbuf support\n");
464       seen = TRUE;
465     }
466 #endif
467 }
468
469 ClutterActor *
470 _clutter_do_pick (ClutterStage   *stage,
471                   gint            x,
472                   gint            y,
473                   ClutterPickMode mode)
474 {
475   ClutterMainContext *context;
476   guchar              pixel[4] = { 0xff, 0xff, 0xff, 0xff };
477   CoglColor           white;
478   guint32             id;
479   GLboolean           dither_was_on;
480
481   if (clutter_debug_flags & CLUTTER_DEBUG_NOP_PICKING)
482     return CLUTTER_ACTOR (stage);
483
484   context = _clutter_context_get_default ();
485
486   _clutter_backend_ensure_context (context->backend, stage);
487
488   /* needed for when a context switch happens */
489   _clutter_stage_maybe_setup_viewport (stage);
490
491   if (G_LIKELY (!(clutter_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS)))
492     cogl_clip_push_window_rectangle (x, y, 1, 1);
493
494   cogl_color_set_from_4ub (&white, 255, 255, 255, 255);
495   cogl_disable_fog ();
496   cogl_clear (&white,
497               COGL_BUFFER_BIT_COLOR |
498               COGL_BUFFER_BIT_DEPTH);
499
500   /* Disable dithering (if any) when doing the painting in pick mode */
501   dither_was_on = glIsEnabled (GL_DITHER);
502   if (dither_was_on)
503     glDisable (GL_DITHER);
504
505   /* Render the entire scence in pick mode - just single colored silhouette's
506    * are drawn offscreen (as we never swap buffers)
507   */
508   context->pick_mode = mode;
509   clutter_actor_paint (CLUTTER_ACTOR (stage));
510   context->pick_mode = CLUTTER_PICK_NONE;
511
512   if (G_LIKELY (!(clutter_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS)))
513     cogl_clip_pop ();
514
515   /* Make sure Cogl flushes any batched geometry to the GPU driver */
516   cogl_flush ();
517
518   /* Read the color of the screen co-ords pixel */
519   cogl_read_pixels (x, y, 1, 1,
520                     COGL_READ_PIXELS_COLOR_BUFFER,
521                     COGL_PIXEL_FORMAT_RGBA_8888,
522                     pixel);
523
524   if (G_UNLIKELY (clutter_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS))
525     {
526       read_pixels_to_file ("pick-buffer", 0, 0,
527                            clutter_actor_get_width (CLUTTER_ACTOR (stage)),
528                            clutter_actor_get_height (CLUTTER_ACTOR (stage)));
529     }
530
531   /* Restore whether GL_DITHER was enabled */
532   if (dither_was_on)
533     glEnable (GL_DITHER);
534
535   if (pixel[0] == 0xff && pixel[1] == 0xff && pixel[2] == 0xff)
536     return CLUTTER_ACTOR (stage);
537
538   id = _clutter_pixel_to_id (pixel);
539
540   return clutter_get_actor_by_gid (id);
541 }
542
543 static PangoDirection
544 clutter_get_text_direction (void)
545 {
546   PangoDirection dir = PANGO_DIRECTION_LTR;
547   const gchar *direction;
548
549   direction = g_getenv ("CLUTTER_TEXT_DIRECTION");
550   if (direction && *direction != '\0')
551     {
552       if (strcmp (direction, "rtl") == 0)
553         dir = PANGO_DIRECTION_RTL;
554       else if (strcmp (direction, "ltr") == 0)
555         dir = PANGO_DIRECTION_LTR;
556     }
557   else
558     {
559       /* Translate to default:RTL if you want your widgets
560        * to be RTL, otherwise translate to default:LTR.
561        *
562        * Do *not* translate it to "predefinito:LTR": if it
563        * it isn't default:LTR or default:RTL it will not work
564        */
565       char *e = _("default:LTR");
566
567       if (strcmp (e, "default:RTL") == 0)
568         dir = PANGO_DIRECTION_RTL;
569       else if (strcmp (e, "default:LTR") == 0)
570         dir = PANGO_DIRECTION_LTR;
571       else
572         g_warning ("Whoever translated default:LTR did so wrongly.");
573     }
574
575   return dir;
576 }
577
578 static void
579 update_pango_context (ClutterBackend *backend,
580                       PangoContext   *context)
581 {
582   PangoFontDescription *font_desc;
583   const cairo_font_options_t *font_options;
584   const gchar *font_name;
585   gdouble resolution;
586
587   /* update the text direction */
588   pango_context_set_base_dir (context, clutter_text_direction);
589
590   /* get the configuration for the PangoContext from the backend */
591   font_name = clutter_backend_get_font_name (backend);
592   font_options = clutter_backend_get_font_options (backend);
593   resolution = clutter_backend_get_resolution (backend);
594
595   font_desc = pango_font_description_from_string (font_name);
596
597   if (resolution < 0)
598     resolution = 96.0; /* fall back */
599
600   pango_context_set_font_description (context, font_desc);
601   pango_cairo_context_set_font_options (context, font_options);
602   pango_cairo_context_set_resolution (context, resolution);
603
604   pango_font_description_free (font_desc);
605 }
606
607 PangoContext *
608 _clutter_context_get_pango_context (ClutterMainContext *self)
609 {
610   if (G_UNLIKELY (self->pango_context == NULL))
611     {
612       PangoContext *context;
613
614       context = cogl_pango_font_map_create_context (self->font_map);
615       self->pango_context = context;
616
617       g_signal_connect (self->backend, "resolution-changed",
618                         G_CALLBACK (update_pango_context),
619                         self->pango_context);
620       g_signal_connect (self->backend, "font-changed",
621                         G_CALLBACK (update_pango_context),
622                         self->pango_context);
623     }
624
625   update_pango_context (self->backend, self->pango_context);
626
627   return self->pango_context;
628 }
629
630 PangoContext *
631 _clutter_context_create_pango_context (ClutterMainContext *self)
632 {
633   PangoContext *context;
634
635   context = cogl_pango_font_map_create_context (self->font_map);
636   update_pango_context (self->backend, context);
637
638   return context;
639 }
640
641 /**
642  * clutter_main_quit:
643  *
644  * Terminates the Clutter mainloop.
645  */
646 void
647 clutter_main_quit (void)
648 {
649   g_return_if_fail (main_loops != NULL);
650
651   g_main_loop_quit (main_loops->data);
652 }
653
654 /**
655  * clutter_main_level:
656  *
657  * Retrieves the depth of the Clutter mainloop.
658  *
659  * Return value: The level of the mainloop.
660  */
661 gint
662 clutter_main_level (void)
663 {
664   return clutter_main_loop_level;
665 }
666
667 /**
668  * clutter_main:
669  *
670  * Starts the Clutter mainloop.
671  */
672 void
673 clutter_main (void)
674 {
675   GMainLoop *loop;
676
677   /* Make sure there is a context */
678   CLUTTER_CONTEXT ();
679
680   if (!clutter_is_initialized)
681     {
682       g_warning ("Called clutter_main() but Clutter wasn't initialised.  "
683                  "You must call clutter_init() first.");
684       return;
685     }
686
687   CLUTTER_MARK ();
688
689   clutter_main_loop_level++;
690
691   loop = g_main_loop_new (NULL, TRUE);
692   main_loops = g_slist_prepend (main_loops, loop);
693
694 #ifdef HAVE_CLUTTER_FRUITY
695   /* clutter fruity creates an application that forwards events and manually
696    * spins the mainloop
697    */
698   clutter_fruity_main ();
699 #else
700   if (g_main_loop_is_running (main_loops->data))
701     {
702       clutter_threads_leave ();
703       g_main_loop_run (loop);
704       clutter_threads_enter ();
705     }
706 #endif
707
708   main_loops = g_slist_remove (main_loops, loop);
709
710   g_main_loop_unref (loop);
711
712   clutter_main_loop_level--;
713
714   CLUTTER_MARK ();
715 }
716
717 static void
718 clutter_threads_impl_lock (void)
719 {
720   if (clutter_threads_mutex)
721     g_mutex_lock (clutter_threads_mutex);
722 }
723
724 static void
725 clutter_threads_impl_unlock (void)
726 {
727   if (clutter_threads_mutex)
728     g_mutex_unlock (clutter_threads_mutex);
729 }
730
731 /**
732  * clutter_threads_init:
733  *
734  * Initialises the Clutter threading mechanism, so that Clutter API can be
735  * called by multiple threads, using clutter_threads_enter() and
736  * clutter_threads_leave() to mark the critical sections.
737  *
738  * You must call g_thread_init() before this function.
739  *
740  * This function must be called before clutter_init().
741  *
742  * Since: 0.4
743  */
744 void
745 clutter_threads_init (void)
746 {
747   if (!g_thread_supported ())
748     g_error ("g_thread_init() must be called before clutter_threads_init()");
749
750   clutter_threads_mutex = g_mutex_new ();
751
752   if (!clutter_threads_lock)
753     clutter_threads_lock = clutter_threads_impl_lock;
754
755   if (!clutter_threads_unlock)
756     clutter_threads_unlock = clutter_threads_impl_unlock;
757 }
758
759 /**
760  * clutter_threads_set_lock_functions:
761  * @enter_fn: function called when aquiring the Clutter main lock
762  * @leave_fn: function called when releasing the Clutter main lock
763  *
764  * Allows the application to replace the standard method that
765  * Clutter uses to protect its data structures. Normally, Clutter
766  * creates a single #GMutex that is locked by clutter_threads_enter(),
767  * and released by clutter_threads_leave(); using this function an
768  * application provides, instead, a function @enter_fn that is
769  * called by clutter_threads_enter() and a function @leave_fn that is
770  * called by clutter_threads_leave().
771  *
772  * The functions must provide at least same locking functionality
773  * as the default implementation, but can also do extra application
774  * specific processing.
775  *
776  * As an example, consider an application that has its own recursive
777  * lock that when held, holds the Clutter lock as well. When Clutter
778  * unlocks the Clutter lock when entering a recursive main loop, the
779  * application must temporarily release its lock as well.
780  *
781  * Most threaded Clutter apps won't need to use this method.
782  *
783  * This method must be called before clutter_threads_init(), and cannot
784  * be called multiple times.
785  *
786  * Since: 0.4
787  */
788 void
789 clutter_threads_set_lock_functions (GCallback enter_fn,
790                                     GCallback leave_fn)
791 {
792   g_return_if_fail (clutter_threads_lock == NULL &&
793                     clutter_threads_unlock == NULL);
794
795   clutter_threads_lock = enter_fn;
796   clutter_threads_unlock = leave_fn;
797 }
798
799 typedef struct
800 {
801   GSourceFunc func;
802   gpointer data;
803   GDestroyNotify notify;
804 } ClutterThreadsDispatch;
805
806 static gboolean
807 clutter_threads_dispatch (gpointer data)
808 {
809   ClutterThreadsDispatch *dispatch = data;
810   gboolean ret = FALSE;
811
812   clutter_threads_enter ();
813
814   if (!g_source_is_destroyed (g_main_current_source ()))
815     ret = dispatch->func (dispatch->data);
816
817   clutter_threads_leave ();
818
819   return ret;
820 }
821
822 static void
823 clutter_threads_dispatch_free (gpointer data)
824 {
825   ClutterThreadsDispatch *dispatch = data;
826
827   /* XXX - we cannot hold the thread lock here because the main loop
828    * might destroy a source while still in the dispatcher function; so
829    * knowing whether the lock is being held or not is not known a priori.
830    *
831    * see bug: http://bugzilla.gnome.org/show_bug.cgi?id=459555
832    */
833   if (dispatch->notify)
834     dispatch->notify (dispatch->data);
835
836   g_slice_free (ClutterThreadsDispatch, dispatch);
837 }
838
839 /**
840  * clutter_threads_add_idle_full:
841  * @priority: the priority of the timeout source. Typically this will be in the
842  *    range between #G_PRIORITY_DEFAULT_IDLE and #G_PRIORITY_HIGH_IDLE
843  * @func: function to call
844  * @data: data to pass to the function
845  * @notify: functio to call when the idle source is removed
846  *
847  * Adds a function to be called whenever there are no higher priority
848  * events pending. If the function returns %FALSE it is automatically
849  * removed from the list of event sources and will not be called again.
850  *
851  * This function can be considered a thread-safe variant of g_idle_add_full():
852  * it will call @function while holding the Clutter lock. It is logically
853  * equivalent to the following implementation:
854  *
855  * |[
856  * static gboolean
857  * idle_safe_callback (gpointer data)
858  * {
859  *    SafeClosure *closure = data;
860  *    gboolean res = FALSE;
861  *
862  *    /&ast; mark the critical section &ast;/
863  *
864  *    clutter_threads_enter();
865  *
866  *    /&ast; the callback does not need to acquire the Clutter
867  *     &ast; lock itself, as it is held by the this proxy handler
868  *     &ast;/
869  *    res = closure->callback (closure->data);
870  *
871  *    clutter_threads_leave();
872  *
873  *    return res;
874  * }
875  * static gulong
876  * add_safe_idle (GSourceFunc callback,
877  *                gpointer    data)
878  * {
879  *   SafeClosure *closure = g_new0 (SafeClosure, 1);
880  *
881  *   closure-&gt;callback = callback;
882  *   closure-&gt;data = data;
883  *
884  *   return g_add_idle_full (G_PRIORITY_DEFAULT_IDLE,
885  *                           idle_safe_callback,
886  *                           closure,
887  *                           g_free)
888  * }
889  *]|
890  *
891  * This function should be used by threaded applications to make sure
892  * that @func is emitted under the Clutter threads lock and invoked
893  * from the same thread that started the Clutter main loop. For instance,
894  * it can be used to update the UI using the results from a worker
895  * thread:
896  *
897  * |[
898  * static gboolean
899  * update_ui (gpointer data)
900  * {
901  *   SomeClosure *closure = data;
902  *
903  *   /&ast; it is safe to call Clutter API from this function because
904  *    &ast; it is invoked from the same thread that started the main
905  *    &ast; loop and under the Clutter thread lock
906  *    &ast;/
907  *   clutter_label_set_text (CLUTTER_LABEL (closure-&gt;label),
908  *                           closure-&gt;text);
909  *
910  *   g_object_unref (closure-&gt;label);
911  *   g_free (closure);
912  *
913  *   return FALSE;
914  * }
915  *
916  *   /&ast; within another thread &ast;/
917  *   closure = g_new0 (SomeClosure, 1);
918  *   /&ast; always take a reference on GObject instances &ast;/
919  *   closure-&gt;label = g_object_ref (my_application-&gt;label);
920  *   closure-&gt;text = g_strdup (processed_text_to_update_the_label);
921  *
922  *   clutter_threads_add_idle_full (G_PRIORITY_HIGH_IDLE,
923  *                                  update_ui,
924  *                                  closure,
925  *                                  NULL);
926  * ]|
927  *
928  * Return value: the ID (greater than 0) of the event source.
929  *
930  * Since: 0.4
931  */
932 guint
933 clutter_threads_add_idle_full (gint           priority,
934                                GSourceFunc    func,
935                                gpointer       data,
936                                GDestroyNotify notify)
937 {
938   ClutterThreadsDispatch *dispatch;
939
940   g_return_val_if_fail (func != NULL, 0);
941
942   dispatch = g_slice_new (ClutterThreadsDispatch);
943   dispatch->func = func;
944   dispatch->data = data;
945   dispatch->notify = notify;
946
947   return g_idle_add_full (priority,
948                           clutter_threads_dispatch, dispatch,
949                           clutter_threads_dispatch_free);
950 }
951
952 /**
953  * clutter_threads_add_idle:
954  * @func: function to call
955  * @data: data to pass to the function
956  *
957  * Simple wrapper around clutter_threads_add_idle_full() using the
958  * default priority.
959  *
960  * Return value: the ID (greater than 0) of the event source.
961  *
962  * Since: 0.4
963  */
964 guint
965 clutter_threads_add_idle (GSourceFunc func,
966                           gpointer    data)
967 {
968   g_return_val_if_fail (func != NULL, 0);
969
970   return clutter_threads_add_idle_full (G_PRIORITY_DEFAULT_IDLE,
971                                         func, data,
972                                         NULL);
973 }
974
975 /**
976  * clutter_threads_add_timeout_full:
977  * @priority: the priority of the timeout source. Typically this will be in the
978  *            range between #G_PRIORITY_DEFAULT and #G_PRIORITY_HIGH.
979  * @interval: the time between calls to the function, in milliseconds
980  * @func: function to call
981  * @data: data to pass to the function
982  * @notify: function to call when the timeout source is removed
983  *
984  * Sets a function to be called at regular intervals holding the Clutter
985  * threads lock, with the given priority. The function is called repeatedly
986  * until it returns %FALSE, at which point the timeout is automatically
987  * removed and the function will not be called again. The @notify function
988  * is called when the timeout is removed.
989  *
990  * The first call to the function will be at the end of the first @interval.
991  *
992  * It is important to note that, due to how the Clutter main loop is
993  * implemented, the timing will not be accurate and it will not try to
994  * "keep up" with the interval. A more reliable source is available
995  * using clutter_threads_add_frame_source_full(), which is also internally
996  * used by #ClutterTimeline.
997  *
998  * See also clutter_threads_add_idle_full().
999  *
1000  * Return value: the ID (greater than 0) of the event source.
1001  *
1002  * Since: 0.4
1003  */
1004 guint
1005 clutter_threads_add_timeout_full (gint           priority,
1006                                   guint          interval,
1007                                   GSourceFunc    func,
1008                                   gpointer       data,
1009                                   GDestroyNotify notify)
1010 {
1011   ClutterThreadsDispatch *dispatch;
1012
1013   g_return_val_if_fail (func != NULL, 0);
1014
1015   dispatch = g_slice_new (ClutterThreadsDispatch);
1016   dispatch->func = func;
1017   dispatch->data = data;
1018   dispatch->notify = notify;
1019
1020   return g_timeout_add_full (priority,
1021                              interval,
1022                              clutter_threads_dispatch, dispatch,
1023                              clutter_threads_dispatch_free);
1024 }
1025
1026 /**
1027  * clutter_threads_add_timeout:
1028  * @interval: the time between calls to the function, in milliseconds
1029  * @func: function to call
1030  * @data: data to pass to the function
1031  *
1032  * Simple wrapper around clutter_threads_add_timeout_full().
1033  *
1034  * Return value: the ID (greater than 0) of the event source.
1035  *
1036  * Since: 0.4
1037  */
1038 guint
1039 clutter_threads_add_timeout (guint       interval,
1040                              GSourceFunc func,
1041                              gpointer    data)
1042 {
1043   g_return_val_if_fail (func != NULL, 0);
1044
1045   return clutter_threads_add_timeout_full (G_PRIORITY_DEFAULT,
1046                                            interval,
1047                                            func, data,
1048                                            NULL);
1049 }
1050
1051 /**
1052  * clutter_threads_add_frame_source_full:
1053  * @priority: the priority of the frame source. Typically this will be in the
1054  *            range between #G_PRIORITY_DEFAULT and #G_PRIORITY_HIGH.
1055  * @fps: the number of times per second to call the function
1056  * @func: function to call
1057  * @data: data to pass to the function
1058  * @notify: function to call when the timeout source is removed
1059  *
1060  * Sets a function to be called at regular intervals holding the Clutter
1061  * threads lock, with the given priority. The function is called repeatedly
1062  * until it returns %FALSE, at which point the timeout is automatically
1063  * removed and the function will not be called again. The @notify function
1064  * is called when the timeout is removed.
1065  *
1066  * This function is similar to clutter_threads_add_timeout_full()
1067  * except that it will try to compensate for delays. For example, if
1068  * @func takes half the interval time to execute then the function
1069  * will be called again half the interval time after it finished. In
1070  * contrast clutter_threads_add_timeout_full() would not fire until a
1071  * full interval after the function completes so the delay between
1072  * calls would be @interval * 1.5. This function does not however try
1073  * to invoke the function multiple times to catch up missing frames if
1074  * @func takes more than @interval ms to execute.
1075  *
1076  * See also clutter_threads_add_idle_full().
1077  *
1078  * Return value: the ID (greater than 0) of the event source.
1079  *
1080  * Since: 0.8
1081  */
1082 guint
1083 clutter_threads_add_frame_source_full (gint           priority,
1084                                        guint          fps,
1085                                        GSourceFunc    func,
1086                                        gpointer       data,
1087                                        GDestroyNotify notify)
1088 {
1089   ClutterThreadsDispatch *dispatch;
1090
1091   g_return_val_if_fail (func != NULL, 0);
1092
1093   dispatch = g_slice_new (ClutterThreadsDispatch);
1094   dispatch->func = func;
1095   dispatch->data = data;
1096   dispatch->notify = notify;
1097
1098   return clutter_frame_source_add_full (priority,
1099                                         fps,
1100                                         clutter_threads_dispatch, dispatch,
1101                                         clutter_threads_dispatch_free);
1102 }
1103
1104 /**
1105  * clutter_threads_add_frame_source:
1106  * @fps: the number of times per second to call the function
1107  * @func: function to call
1108  * @data: data to pass to the function
1109  *
1110  * Simple wrapper around clutter_threads_add_frame_source_full().
1111  *
1112  * Return value: the ID (greater than 0) of the event source.
1113  *
1114  * Since: 0.8
1115  */
1116 guint
1117 clutter_threads_add_frame_source (guint       fps,
1118                                   GSourceFunc func,
1119                                   gpointer    data)
1120 {
1121   g_return_val_if_fail (func != NULL, 0);
1122
1123   return clutter_threads_add_frame_source_full (G_PRIORITY_DEFAULT,
1124                                                 fps,
1125                                                 func, data,
1126                                                 NULL);
1127 }
1128
1129 /**
1130  * clutter_threads_enter:
1131  *
1132  * Locks the Clutter thread lock.
1133  *
1134  * Since: 0.4
1135  */
1136 void
1137 clutter_threads_enter (void)
1138 {
1139   if (clutter_threads_lock)
1140     (* clutter_threads_lock) ();
1141 }
1142
1143 /**
1144  * clutter_threads_leave:
1145  *
1146  * Unlocks the Clutter thread lock.
1147  *
1148  * Since: 0.4
1149  */
1150 void
1151 clutter_threads_leave (void)
1152 {
1153   if (clutter_threads_unlock)
1154     (* clutter_threads_unlock) ();
1155 }
1156
1157
1158 /**
1159  * clutter_get_debug_enabled:
1160  *
1161  * Check if clutter has debugging turned on.
1162  *
1163  * Return value: TRUE if debugging is turned on, FALSE otherwise.
1164  */
1165 gboolean
1166 clutter_get_debug_enabled (void)
1167 {
1168 #ifdef CLUTTER_ENABLE_DEBUG
1169   return clutter_debug_flags != 0;
1170 #else
1171   return FALSE;
1172 #endif
1173 }
1174
1175 gboolean
1176 _clutter_context_is_initialized (void)
1177 {
1178   if (ClutterCntx == NULL)
1179     return FALSE;
1180
1181   return ClutterCntx->is_initialized;
1182 }
1183
1184 ClutterMainContext *
1185 _clutter_context_get_default (void)
1186 {
1187   if (G_UNLIKELY (ClutterCntx == NULL))
1188     {
1189       ClutterMainContext *ctx;
1190
1191       ClutterCntx = ctx = g_new0 (ClutterMainContext, 1);
1192
1193       ctx->backend = g_object_new (_clutter_backend_impl_get_type (), NULL);
1194
1195       ctx->is_initialized = FALSE;
1196       ctx->motion_events_per_actor = TRUE;
1197
1198 #ifdef CLUTTER_ENABLE_DEBUG
1199       ctx->timer = g_timer_new ();
1200       g_timer_start (ctx->timer);
1201 #endif
1202     }
1203
1204   return ClutterCntx;
1205 }
1206
1207 /**
1208  * clutter_get_timestamp:
1209  *
1210  * Returns the approximate number of microseconds passed since clutter was
1211  * intialised.
1212  *
1213  * Return value: Number of microseconds since clutter_init() was called.
1214  */
1215 gulong
1216 clutter_get_timestamp (void)
1217 {
1218 #ifdef CLUTTER_ENABLE_DEBUG
1219   ClutterMainContext *ctx;
1220   gdouble seconds;
1221
1222   ctx = _clutter_context_get_default ();
1223
1224   /* FIXME: may need a custom timer for embedded setups */
1225   seconds = g_timer_elapsed (ctx->timer, NULL);
1226
1227   return (gulong)(seconds / 1.0e-6);
1228 #else
1229   return 0;
1230 #endif
1231 }
1232
1233 static gboolean
1234 clutter_arg_direction_cb (const char *key,
1235                           const char *value,
1236                           gpointer    user_data)
1237 {
1238   clutter_text_direction =
1239     (strcmp (value, "rtl") == 0) ? PANGO_DIRECTION_RTL
1240                                  : PANGO_DIRECTION_LTR;
1241
1242   return TRUE;
1243 }
1244
1245 #ifdef CLUTTER_ENABLE_DEBUG
1246 static gboolean
1247 clutter_arg_debug_cb (const char *key,
1248                       const char *value,
1249                       gpointer    user_data)
1250 {
1251   clutter_debug_flags |=
1252     g_parse_debug_string (value,
1253                           clutter_debug_keys,
1254                           G_N_ELEMENTS (clutter_debug_keys));
1255   return TRUE;
1256 }
1257
1258 static gboolean
1259 clutter_arg_no_debug_cb (const char *key,
1260                          const char *value,
1261                          gpointer    user_data)
1262 {
1263   clutter_debug_flags &=
1264     ~g_parse_debug_string (value,
1265                            clutter_debug_keys,
1266                            G_N_ELEMENTS (clutter_debug_keys));
1267   return TRUE;
1268 }
1269 #endif /* CLUTTER_ENABLE_DEBUG */
1270
1271 GQuark
1272 clutter_init_error_quark (void)
1273 {
1274   return g_quark_from_static_string ("clutter-init-error-quark");
1275 }
1276
1277 static ClutterInitError
1278 clutter_init_real (GError **error)
1279 {
1280   ClutterMainContext *ctx;
1281   ClutterActor *stage;
1282   gdouble resolution;
1283   ClutterBackend *backend;
1284
1285   /* Note, creates backend if not already existing, though parse args will
1286    * have likely created it
1287    */
1288   ctx = _clutter_context_get_default ();
1289   backend = ctx->backend;
1290
1291   if (!ctx->options_parsed)
1292     {
1293       g_set_error (error, CLUTTER_INIT_ERROR,
1294                    CLUTTER_INIT_ERROR_INTERNAL,
1295                    "When using clutter_get_option_group_without_init() "
1296                    "you must parse options before calling clutter_init()");
1297
1298       return CLUTTER_INIT_ERROR_INTERNAL;
1299     }
1300
1301   /*
1302    * Call backend post parse hooks.
1303    */
1304   if (!_clutter_backend_post_parse (backend, error))
1305     return CLUTTER_INIT_ERROR_BACKEND;
1306
1307   /* Stage will give us a GL Context etc */
1308   stage = clutter_stage_get_default ();
1309   if (!stage)
1310     {
1311       if (error)
1312         g_set_error (error, CLUTTER_INIT_ERROR,
1313                      CLUTTER_INIT_ERROR_INTERNAL,
1314                      "Unable to create the default stage");
1315       else
1316         g_critical ("Unable to create the default stage");
1317
1318       return CLUTTER_INIT_ERROR_INTERNAL;
1319     }
1320
1321   clutter_stage_set_title (CLUTTER_STAGE (stage), g_get_prgname ());
1322
1323   clutter_actor_realize (stage);
1324
1325   if (!CLUTTER_ACTOR_IS_REALIZED (stage))
1326     {
1327       if (error)
1328         g_set_error (error, CLUTTER_INIT_ERROR,
1329                      CLUTTER_INIT_ERROR_INTERNAL,
1330                      "Unable to realize the default stage");
1331       else
1332         g_critical ("Unable to realize the default stage");
1333
1334       return CLUTTER_INIT_ERROR_INTERNAL;
1335     }
1336
1337   /* Now we can safely assume we have a valid GL context and can
1338    * start issueing cogl commands
1339   */
1340   /* - will call to backend and cogl */
1341   _clutter_feature_init ();
1342
1343   /*
1344    * Resolution requires display to be open, so can only be queried after
1345    * the post_parse hooks run.
1346    *
1347    * NB: cogl_pango requires a Cogl context.
1348    */
1349   ctx->font_map = COGL_PANGO_FONT_MAP (cogl_pango_font_map_new ());
1350
1351   resolution = clutter_backend_get_resolution (ctx->backend);
1352   cogl_pango_font_map_set_resolution (ctx->font_map, resolution);
1353
1354   if (G_LIKELY (!clutter_disable_mipmap_text))
1355     cogl_pango_font_map_set_use_mipmapping (ctx->font_map, TRUE);
1356
1357   clutter_text_direction = clutter_get_text_direction ();
1358
1359
1360   /* Figure out framebuffer masks used for pick */
1361   cogl_get_bitmasks (&ctx->fb_r_mask, &ctx->fb_g_mask, &ctx->fb_b_mask, NULL);
1362
1363   ctx->fb_r_mask_used = ctx->fb_r_mask;
1364   ctx->fb_g_mask_used = ctx->fb_g_mask;
1365   ctx->fb_b_mask_used = ctx->fb_b_mask;
1366
1367   /* XXX - describe what "fuzzy picking" is */
1368   if (clutter_use_fuzzy_picking)
1369     {
1370       ctx->fb_r_mask_used--;
1371       ctx->fb_g_mask_used--;
1372       ctx->fb_b_mask_used--;
1373     }
1374
1375   /* Initiate event collection */
1376   _clutter_backend_init_events (ctx->backend);
1377
1378   clutter_is_initialized = TRUE;
1379   ctx->is_initialized = TRUE;
1380
1381   return CLUTTER_INIT_SUCCESS;
1382 }
1383
1384 static GOptionEntry clutter_args[] = {
1385   { "clutter-show-fps", 0, 0, G_OPTION_ARG_NONE, &clutter_show_fps,
1386     N_("Show frames per second"), NULL },
1387   { "clutter-default-fps", 0, 0, G_OPTION_ARG_INT, &clutter_default_fps,
1388     N_("Default frame rate"), "FPS" },
1389   { "g-fatal-warnings", 0, 0, G_OPTION_ARG_NONE, &clutter_fatal_warnings,
1390     N_("Make all warnings fatal"), NULL },
1391   { "clutter-text-direction", 0, 0, G_OPTION_ARG_CALLBACK,
1392     clutter_arg_direction_cb,
1393     N_("Direction for the text"), "DIRECTION" },
1394   { "clutter-disable-mipmapped-text", 0, 0, G_OPTION_ARG_NONE,
1395     &clutter_disable_mipmap_text,
1396     N_("Disable mipmapping on text"), NULL },
1397   { "clutter-use-fuzzy-picking", 0, 0, G_OPTION_ARG_NONE,
1398     &clutter_use_fuzzy_picking,
1399     N_("Use 'fuzzy' picking"), NULL },
1400 #ifdef CLUTTER_ENABLE_DEBUG
1401   { "clutter-debug", 0, 0, G_OPTION_ARG_CALLBACK, clutter_arg_debug_cb,
1402     N_("Clutter debugging flags to set"), "FLAGS" },
1403   { "clutter-no-debug", 0, 0, G_OPTION_ARG_CALLBACK, clutter_arg_no_debug_cb,
1404     N_("Clutter debugging flags to unset"), "FLAGS" },
1405 #endif /* CLUTTER_ENABLE_DEBUG */
1406   { NULL, },
1407 };
1408
1409 /* pre_parse_hook: initialise variables depending on environment
1410  * variables; these variables might be overridden by the command
1411  * line arguments that are going to be parsed after.
1412  */
1413 static gboolean
1414 pre_parse_hook (GOptionContext  *context,
1415                 GOptionGroup    *group,
1416                 gpointer         data,
1417                 GError         **error)
1418 {
1419   ClutterMainContext *clutter_context;
1420   ClutterBackend *backend;
1421   const char *env_string;
1422
1423   if (clutter_is_initialized)
1424     return TRUE;
1425
1426   if (setlocale (LC_ALL, "") == NULL)
1427     g_warning ("Locale not supported by C library.\n"
1428                "Using the fallback 'C' locale.");
1429
1430   clutter_context = _clutter_context_get_default ();
1431
1432   clutter_context->id_pool = clutter_id_pool_new (256);
1433
1434   backend = clutter_context->backend;
1435   g_assert (CLUTTER_IS_BACKEND (backend));
1436
1437 #ifdef CLUTTER_ENABLE_DEBUG
1438   env_string = g_getenv ("CLUTTER_DEBUG");
1439   if (env_string != NULL)
1440     {
1441       clutter_debug_flags =
1442         g_parse_debug_string (env_string,
1443                               clutter_debug_keys,
1444                               G_N_ELEMENTS (clutter_debug_keys));
1445       env_string = NULL;
1446     }
1447 #endif /* CLUTTER_ENABLE_DEBUG */
1448
1449   env_string = g_getenv ("CLUTTER_SHOW_FPS");
1450   if (env_string)
1451     clutter_show_fps = TRUE;
1452
1453   env_string = g_getenv ("CLUTTER_DEFAULT_FPS");
1454   if (env_string)
1455     {
1456       gint default_fps = g_ascii_strtoll (env_string, NULL, 10);
1457
1458       clutter_default_fps = CLAMP (default_fps, 1, 1000);
1459     }
1460
1461   env_string = g_getenv ("CLUTTER_DISABLE_MIPMAPPED_TEXT");
1462   if (env_string)
1463     clutter_disable_mipmap_text = TRUE;
1464
1465 #ifdef HAVE_CLUTTER_FRUITY
1466   /* we always enable fuzzy picking in the "fruity" backend */
1467   clutter_use_fuzzy_picking = TRUE;
1468 #else
1469   env_string = g_getenv ("CLUTTER_FUZZY_PICK");
1470   if (env_string)
1471     clutter_use_fuzzy_picking = TRUE;
1472 #endif /* HAVE_CLUTTER_FRUITY */
1473
1474   return _clutter_backend_pre_parse (backend, error);
1475 }
1476
1477 /* post_parse_hook: initialise the context and data structures
1478  * and opens the X display
1479  */
1480 static gboolean
1481 post_parse_hook (GOptionContext  *context,
1482                  GOptionGroup    *group,
1483                  gpointer         data,
1484                  GError         **error)
1485 {
1486   ClutterMainContext *clutter_context;
1487   ClutterBackend *backend;
1488
1489   if (clutter_is_initialized)
1490     return TRUE;
1491
1492   clutter_context = _clutter_context_get_default ();
1493   backend = clutter_context->backend;
1494   g_assert (CLUTTER_IS_BACKEND (backend));
1495
1496   if (clutter_fatal_warnings)
1497     {
1498       GLogLevelFlags fatal_mask;
1499
1500       fatal_mask = g_log_set_always_fatal (G_LOG_FATAL_MASK);
1501       fatal_mask |= G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL;
1502       g_log_set_always_fatal (fatal_mask);
1503     }
1504
1505   clutter_context->frame_rate = clutter_default_fps;
1506   clutter_context->options_parsed = TRUE;
1507
1508   /*
1509    * If not asked to defer display setup, call clutter_init_real(),
1510    * which in turn calls the backend post parse hooks.
1511    */
1512   if (!clutter_context->defer_display_setup)
1513     return clutter_init_real (error);
1514
1515   return TRUE;
1516 }
1517
1518 /**
1519  * clutter_get_option_group:
1520  *
1521  * Returns a #GOptionGroup for the command line arguments recognized
1522  * by Clutter. You should add this group to your #GOptionContext with
1523  * g_option_context_add_group(), if you are using g_option_context_parse()
1524  * to parse your commandline arguments.
1525  *
1526  * Calling g_option_context_parse() with Clutter's #GOptionGroup will result
1527  * in Clutter's initialization. That is, the following code:
1528  *
1529  * |[
1530  *   g_option_context_set_main_group (context, clutter_get_option_group ());
1531  *   res = g_option_context_parse (context, &amp;argc, &amp;argc, NULL);
1532  * ]|
1533  *
1534  * is functionally equivalent to:
1535  *
1536  * |[
1537  *   clutter_init (&amp;argc, &amp;argv);
1538  * ]|
1539  *
1540  * After g_option_context_parse() on a #GOptionContext containing the
1541  * Clutter #GOptionGroup has returned %TRUE, Clutter is guaranteed to be
1542  * initialized.
1543  *
1544  * Return value: (transfer full): a #GOptionGroup for the commandline arguments
1545  *   recognized by Clutter
1546  *
1547  * Since: 0.2
1548  */
1549 GOptionGroup *
1550 clutter_get_option_group (void)
1551 {
1552   ClutterMainContext *context;
1553   GOptionGroup *group;
1554
1555   clutter_base_init ();
1556
1557   context = _clutter_context_get_default ();
1558
1559   group = g_option_group_new ("clutter",
1560                               _("Clutter Options"),
1561                               _("Show Clutter Options"),
1562                               NULL,
1563                               NULL);
1564
1565   g_option_group_set_parse_hooks (group, pre_parse_hook, post_parse_hook);
1566   g_option_group_add_entries (group, clutter_args);
1567   g_option_group_set_translation_domain (group, GETTEXT_PACKAGE);
1568
1569   /* add backend-specific options */
1570   _clutter_backend_add_options (context->backend, group);
1571
1572   return group;
1573 }
1574
1575 /**
1576  * clutter_get_option_group_without_init:
1577  *
1578  * Returns a #GOptionGroup for the command line arguments recognized
1579  * by Clutter. You should add this group to your #GOptionContext with
1580  * g_option_context_add_group(), if you are using g_option_context_parse()
1581  * to parse your commandline arguments. Unlike clutter_get_option_group(),
1582  * calling g_option_context_parse() with the #GOptionGroup returned by this
1583  * function requires a subsequent explicit call to clutter_init(); use this
1584  * function when needing to set foreign display connection with
1585  * clutter_x11_set_display(), or with gtk_clutter_init().
1586  *
1587  * Return value: (transfer full): a #GOptionGroup for the commandline arguments
1588  *   recognized by Clutter
1589  *
1590  * Since: 0.8.2
1591  */
1592 GOptionGroup *
1593 clutter_get_option_group_without_init (void)
1594 {
1595   ClutterMainContext *context;
1596   GOptionGroup *group;
1597
1598   clutter_base_init ();
1599
1600   context = _clutter_context_get_default ();
1601   context->defer_display_setup = TRUE;
1602
1603   group = clutter_get_option_group ();
1604
1605   return group;
1606 }
1607
1608 /* Note that the gobject-introspection annotations for the argc/argv
1609  * parameters do not produce the right result; however, they do
1610  * allow the common case of argc=NULL, argv=NULL to work.
1611  */
1612
1613 /**
1614  * clutter_init_with_args:
1615  * @argc: (inout): a pointer to the number of command line arguments
1616  * @argv: (array length=argc) (inout) (allow-none): a pointer to the array
1617  *   of command line arguments
1618  * @parameter_string: (allow-none): a string which is displayed in the
1619  *   first line of <option>--help</option> output, after
1620  *   <literal><replaceable>programname</replaceable> [OPTION...]</literal>
1621  * @entries: (allow-none): a %NULL terminated array of #GOptionEntry<!-- -->s
1622  *   describing the options of your program
1623  * @translation_domain: (allow-none): a translation domain to use for
1624  *   translating the <option>--help</option> output for the options in
1625  *   @entries with gettext(), or %NULL
1626  * @error: (allow-none): a return location for a #GError
1627  *
1628  * This function does the same work as clutter_init(). Additionally,
1629  * it allows you to add your own command line options, and it
1630  * automatically generates nicely formatted <option>--help</option>
1631  * output. Note that your program will be terminated after writing
1632  * out the help output. Also note that, in case of error, the
1633  * error message will be placed inside @error instead of being
1634  * printed on the display.
1635  *
1636  * Return value: %CLUTTER_INIT_SUCCESS if Clutter has been successfully
1637  *   initialised, or other values or #ClutterInitError in case of
1638  *   error.
1639  *
1640  * Since: 0.2
1641  */
1642 ClutterInitError
1643 clutter_init_with_args (int            *argc,
1644                         char         ***argv,
1645                         const char     *parameter_string,
1646                         GOptionEntry   *entries,
1647                         const char     *translation_domain,
1648                         GError        **error)
1649 {
1650   GOptionContext *context;
1651   GOptionGroup *group;
1652   gboolean res;
1653   ClutterMainContext *ctx;
1654
1655   if (clutter_is_initialized)
1656     return CLUTTER_INIT_SUCCESS;
1657
1658   clutter_base_init ();
1659
1660   ctx = _clutter_context_get_default ();
1661
1662   if (!ctx->defer_display_setup)
1663     {
1664       if (argc && *argc > 0 && *argv)
1665         g_set_prgname ((*argv)[0]);
1666
1667       context = g_option_context_new (parameter_string);
1668
1669       group = clutter_get_option_group ();
1670       g_option_context_add_group (context, group);
1671
1672       group = cogl_get_option_group ();
1673       g_option_context_add_group (context, group);
1674
1675       if (entries)
1676         g_option_context_add_main_entries (context, entries, translation_domain);
1677
1678       res = g_option_context_parse (context, argc, argv, error);
1679       g_option_context_free (context);
1680
1681       /* if res is FALSE, the error is filled for
1682        * us by g_option_context_parse()
1683        */
1684       if (!res)
1685         {
1686           /* if there has been an error in the initialization, the
1687            * error id will be preserved inside the GError code
1688            */
1689           if (error && *error)
1690             return (*error)->code;
1691           else
1692             return CLUTTER_INIT_ERROR_INTERNAL;
1693         }
1694
1695       return CLUTTER_INIT_SUCCESS;
1696     }
1697   else
1698     return clutter_init_real (error);
1699 }
1700
1701 static gboolean
1702 clutter_parse_args (int    *argc,
1703                     char ***argv)
1704 {
1705   GOptionContext *option_context;
1706   GOptionGroup   *clutter_group, *cogl_group;
1707   GError         *error = NULL;
1708   gboolean        ret = TRUE;
1709
1710   if (clutter_is_initialized)
1711     return TRUE;
1712
1713   option_context = g_option_context_new (NULL);
1714   g_option_context_set_ignore_unknown_options (option_context, TRUE);
1715   g_option_context_set_help_enabled (option_context, FALSE);
1716
1717   /* Initiate any command line options from the backend */
1718
1719   clutter_group = clutter_get_option_group ();
1720   g_option_context_set_main_group (option_context, clutter_group);
1721
1722   cogl_group = cogl_get_option_group ();
1723   g_option_context_add_group (option_context, cogl_group);
1724
1725   if (!g_option_context_parse (option_context, argc, argv, &error))
1726     {
1727       if (error)
1728         {
1729           g_warning ("%s", error->message);
1730           g_error_free (error);
1731         }
1732
1733       ret = FALSE;
1734     }
1735
1736   g_option_context_free (option_context);
1737
1738   return ret;
1739 }
1740
1741 /**
1742  * clutter_init:
1743  * @argc: (inout): The number of arguments in @argv
1744  * @argv: (array length=argc) (inout) (allow-none): A pointer to an array
1745  *   of arguments.
1746  *
1747  * It will initialise everything needed to operate with Clutter and
1748  * parses some standard command line options. @argc and @argv are
1749  * adjusted accordingly so your own code will never see those standard
1750  * arguments.
1751  *
1752  * Return value: 1 on success, < 0 on failure.
1753  */
1754 ClutterInitError
1755 clutter_init (int    *argc,
1756               char ***argv)
1757 {
1758   ClutterMainContext *ctx;
1759   GError *error = NULL;
1760
1761   if (clutter_is_initialized)
1762     return CLUTTER_INIT_SUCCESS;
1763
1764   clutter_base_init ();
1765
1766   ctx = _clutter_context_get_default ();
1767
1768   if (!ctx->defer_display_setup)
1769     {
1770       if (argc && *argc > 0 && *argv)
1771         g_set_prgname ((*argv)[0]);
1772
1773       /* parse_args will trigger backend creation and things like
1774        * DISPLAY connection etc.
1775        */
1776       if (clutter_parse_args (argc, argv) == FALSE)
1777         {
1778           CLUTTER_NOTE (MISC, "failed to parse arguments.");
1779           return CLUTTER_INIT_ERROR_INTERNAL;
1780         }
1781
1782       return CLUTTER_INIT_SUCCESS;
1783     }
1784   else
1785     return clutter_init_real (&error);
1786 }
1787
1788 gboolean
1789 _clutter_boolean_handled_accumulator (GSignalInvocationHint *ihint,
1790                                       GValue                *return_accu,
1791                                       const GValue          *handler_return,
1792                                       gpointer               dummy)
1793 {
1794   gboolean continue_emission;
1795   gboolean signal_handled;
1796
1797   signal_handled = g_value_get_boolean (handler_return);
1798   g_value_set_boolean (return_accu, signal_handled);
1799   continue_emission = !signal_handled;
1800
1801   return continue_emission;
1802 }
1803
1804 static void
1805 event_click_count_generate (ClutterEvent *event)
1806 {
1807   /* multiple button click detection */
1808   static gint    click_count            = 0;
1809   static gint    previous_x             = -1;
1810   static gint    previous_y             = -1;
1811   static guint32 previous_time          = 0;
1812   static gint    previous_button_number = -1;
1813
1814   ClutterBackend *backend;
1815   guint           double_click_time;
1816   guint           double_click_distance;
1817
1818   backend = _clutter_context_get_default ()->backend;
1819   double_click_distance = clutter_backend_get_double_click_distance (backend);
1820   double_click_time = clutter_backend_get_double_click_time (backend);
1821
1822   if (event->button.device != NULL)
1823     {
1824       click_count = event->button.device->click_count;
1825       previous_x = event->button.device->previous_x;
1826       previous_y = event->button.device->previous_y;
1827       previous_time = event->button.device->previous_time;
1828       previous_button_number = event->button.device->previous_button_number;
1829     }
1830
1831   switch (event->type)
1832     {
1833       case CLUTTER_BUTTON_PRESS:
1834       case CLUTTER_SCROLL:
1835         /* check if we are in time and within distance to increment an
1836          * existing click count
1837          */
1838         if (event->button.time < previous_time + double_click_time &&
1839             (ABS (event->button.x - previous_x) <= double_click_distance) &&
1840             (ABS (event->button.y - previous_y) <= double_click_distance)
1841             && event->button.button == previous_button_number)
1842           {
1843             click_count ++;
1844           }
1845         else /* start a new click count*/
1846           {
1847             click_count=1;
1848             previous_button_number = event->button.button;
1849           }
1850
1851         /* store time and position for this click for comparison with
1852          * next event
1853          */
1854         previous_time = event->button.time;
1855         previous_x    = event->button.x;
1856         previous_y    = event->button.y;
1857
1858         /* fallthrough */
1859       case CLUTTER_BUTTON_RELEASE:
1860         event->button.click_count=click_count;
1861         break;
1862       default:
1863         g_assert (NULL);
1864     }
1865
1866   if (event->button.device != NULL)
1867     {
1868       event->button.device->click_count = click_count;
1869       event->button.device->previous_x = previous_x;
1870       event->button.device->previous_y = previous_y;
1871       event->button.device->previous_time = previous_time;
1872       event->button.device->previous_button_number = previous_button_number;
1873     }
1874 }
1875
1876
1877 static inline void
1878 emit_event (ClutterEvent *event,
1879             gboolean      is_key_event)
1880 {
1881   static gboolean      lock = FALSE;
1882
1883   GPtrArray *event_tree = NULL;
1884   ClutterActor *actor;
1885   gint i = 0;
1886
1887   if (!event->any.source)
1888     {
1889       CLUTTER_NOTE (EVENT, "No source set, discarding event");
1890       return;
1891     }
1892
1893   /* reentrancy check */
1894   if (lock != FALSE)
1895     {
1896       g_warning ("Tried emitting event during event delivery, bailing out.n");
1897       return;
1898     }
1899
1900   lock = TRUE;
1901
1902   event_tree = g_ptr_array_sized_new (64);
1903
1904   actor = event->any.source;
1905
1906   /* Build 'tree' of emitters for the event */
1907   while (actor)
1908     {
1909       ClutterActor *parent;
1910
1911       parent = clutter_actor_get_parent (actor);
1912
1913       if (clutter_actor_get_reactive (actor) ||
1914           parent == NULL ||         /* stage gets all events */
1915           is_key_event)             /* keyboard events are always emitted */
1916         {
1917           g_ptr_array_add (event_tree, g_object_ref (actor));
1918         }
1919
1920       actor = parent;
1921     }
1922
1923   /* Capture */
1924   for (i = event_tree->len - 1; i >= 0; i--)
1925     if (clutter_actor_event (g_ptr_array_index (event_tree, i), event, TRUE))
1926       goto done;
1927
1928   /* Bubble */
1929   for (i = 0; i < event_tree->len; i++)
1930     if (clutter_actor_event (g_ptr_array_index (event_tree, i), event, FALSE))
1931       goto done;
1932
1933 done:
1934   for (i = 0; i < event_tree->len; i++)
1935     g_object_unref (g_ptr_array_index (event_tree, i));
1936
1937   g_ptr_array_free (event_tree, TRUE);
1938
1939   lock = FALSE;
1940 }
1941
1942 /*
1943  * Emits a pointer event after having prepared the event for delivery (setting
1944  * source, computing click_count, generating enter/leave etc.).
1945  */
1946
1947 static inline void
1948 emit_pointer_event (ClutterEvent       *event,
1949                     ClutterInputDevice *device)
1950 {
1951   /* Using the global variable directly, since it has to be initialized
1952    * at this point
1953    */
1954   ClutterMainContext *context = ClutterCntx;
1955
1956   if (G_UNLIKELY (context->pointer_grab_actor != NULL &&
1957                   device == NULL))
1958     {
1959       /* global grab */
1960       clutter_actor_event (context->pointer_grab_actor, event, FALSE);
1961     }
1962   else if (G_UNLIKELY (device != NULL &&
1963                        device->pointer_grab_actor != NULL))
1964     {
1965       /* per device grab */
1966       clutter_actor_event (device->pointer_grab_actor, event, FALSE);
1967     }
1968   else
1969     {
1970       /* no grab, time to capture and bubble */
1971       emit_event (event, FALSE);
1972     }
1973 }
1974
1975 static inline void
1976 emit_keyboard_event (ClutterEvent *event)
1977 {
1978   ClutterMainContext *context = ClutterCntx;
1979
1980   if (G_UNLIKELY (context->keyboard_grab_actor != NULL))
1981     clutter_actor_event (context->keyboard_grab_actor, event, FALSE);
1982   else
1983     emit_event (event, TRUE);
1984 }
1985
1986 static void
1987 unset_motion_last_actor (ClutterActor *actor, ClutterInputDevice *dev)
1988 {
1989   ClutterMainContext *context = ClutterCntx;
1990
1991   if (dev == NULL)
1992     context->motion_last_actor = NULL;
1993   else
1994     dev->motion_last_actor = NULL;
1995 }
1996
1997 static void
1998 set_motion_last_actor (ClutterActor       *motion_current_actor,
1999                        ClutterInputDevice *device)
2000 {
2001   ClutterMainContext *context              = ClutterCntx;
2002   ClutterActor       *last_actor           = context->motion_last_actor;
2003
2004   if (device != NULL)
2005     last_actor = device->motion_last_actor;
2006
2007   if (last_actor && last_actor != motion_current_actor)
2008     {
2009       g_signal_handlers_disconnect_by_func
2010                        (last_actor,
2011                         G_CALLBACK (unset_motion_last_actor),
2012                         device);
2013     }
2014
2015   if (motion_current_actor && last_actor != motion_current_actor)
2016     {
2017       g_signal_connect (motion_current_actor, "destroy",
2018                         G_CALLBACK (unset_motion_last_actor),
2019                         device);
2020     }
2021
2022   if (device != NULL)
2023     device->motion_last_actor = motion_current_actor;
2024   else
2025     context->motion_last_actor = motion_current_actor;
2026 }
2027
2028 static inline void
2029 generate_enter_leave_events (ClutterEvent *event)
2030 {
2031   ClutterMainContext *context              = ClutterCntx;
2032   ClutterActor       *motion_current_actor = event->motion.source;
2033   ClutterActor       *last_actor           = context->motion_last_actor;
2034   ClutterInputDevice *device               = clutter_event_get_device (event);
2035
2036   if (device != NULL)
2037     last_actor = device->motion_last_actor;
2038
2039   if (last_actor != motion_current_actor)
2040     {
2041       if (motion_current_actor)
2042         {
2043           ClutterEvent cev;
2044           gfloat x, y;
2045
2046           cev.crossing.device  = device;
2047           clutter_event_get_coords (event, &x, &y);
2048
2049           if (context->motion_last_actor)
2050             {
2051               cev.crossing.type    = CLUTTER_LEAVE;
2052               cev.crossing.time    = event->any.time;
2053               cev.crossing.flags   = 0;
2054               cev.crossing.x       = x;
2055               cev.crossing.y       = y;
2056               cev.crossing.source  = last_actor;
2057               cev.crossing.stage   = event->any.stage;
2058               cev.crossing.related = motion_current_actor;
2059
2060               emit_pointer_event (&cev, device);
2061             }
2062
2063           cev.crossing.type    = CLUTTER_ENTER;
2064           cev.crossing.time    = event->any.time;
2065           cev.crossing.flags   = 0;
2066           cev.crossing.x       = x;
2067           cev.crossing.y       = y;
2068           cev.crossing.source  = motion_current_actor;
2069           cev.crossing.stage   = event->any.stage;
2070
2071           if (context->motion_last_actor)
2072             cev.crossing.related = last_actor;
2073           else
2074             cev.crossing.related = NULL;
2075
2076           emit_pointer_event (&cev, device);
2077         }
2078     }
2079
2080   set_motion_last_actor (motion_current_actor, device);
2081 }
2082
2083 /**
2084  * clutter_do_event
2085  * @event: a #ClutterEvent.
2086  *
2087  * Processes an event. This function should never be called by applications.
2088  *
2089  * Since: 0.4
2090  */
2091 void
2092 clutter_do_event (ClutterEvent *event)
2093 {
2094   if (!event->any.stage)
2095     return;
2096
2097   /* Instead of processing events when received, we queue them up to
2098    * handle per-frame before animations, layout, and drawing.
2099    *
2100    * This gives us the chance to reliably compress motion events
2101    * because we've "looked ahead" and know all motion events that
2102    * will occur before drawing the frame.
2103    */
2104   _clutter_stage_queue_event (event->any.stage, event);
2105 }
2106
2107 /**
2108  * _clutter_process_event
2109  * @event: a #ClutterEvent.
2110  *
2111  * Does the actual work of processing an event that was queued earlier
2112  * out of clutter_do_event().
2113  */
2114 void
2115 _clutter_process_event (ClutterEvent *event)
2116 {
2117   /* FIXME: This should probably be clutter_cook_event() - it would
2118    * take a raw event from the backend and 'cook' it so its more tasty.
2119    *
2120   */
2121   ClutterMainContext  *context;
2122   ClutterBackend      *backend;
2123   ClutterActor        *stage;
2124   ClutterInputDevice  *device = NULL;
2125
2126   context = _clutter_context_get_default ();
2127   backend = context->backend;
2128   stage   = CLUTTER_ACTOR(event->any.stage);
2129
2130   if (!stage)
2131     return;
2132
2133   CLUTTER_TIMESTAMP (EVENT, "Event received");
2134
2135   context->last_event_time = clutter_event_get_time (event);
2136
2137   switch (event->type)
2138     {
2139       case CLUTTER_NOTHING:
2140         event->any.source = stage;
2141         break;
2142
2143       case CLUTTER_LEAVE:
2144         /* The source is set for generated events, not for events
2145          * resulting from the cursor leaving the stage
2146          */
2147         if (event->any.source == NULL)
2148           {
2149             ClutterActor *last_actor = context->motion_last_actor;
2150
2151             if (event->crossing.device != NULL)
2152               last_actor = event->crossing.device->motion_last_actor;
2153
2154             event->any.source = last_actor;
2155
2156             set_motion_last_actor (NULL, event->crossing.device);
2157           }
2158         /* flow through */
2159       case CLUTTER_ENTER:
2160         emit_pointer_event (event, event->crossing.device);
2161         break;
2162
2163       case CLUTTER_DESTROY_NOTIFY:
2164       case CLUTTER_DELETE:
2165         event->any.source = stage;
2166         /* the stage did not handle the event, so we just quit */
2167         if (!clutter_stage_event (CLUTTER_STAGE (stage), event))
2168           {
2169             if (stage == clutter_stage_get_default())
2170               clutter_main_quit ();
2171             else
2172               clutter_actor_destroy (stage);
2173           }
2174
2175         break;
2176
2177       case CLUTTER_KEY_PRESS:
2178       case CLUTTER_KEY_RELEASE:
2179         {
2180           ClutterActor *actor = NULL;
2181
2182           /* check that we're not a synthetic event with source set */
2183           if (event->any.source == NULL)
2184             {
2185               actor = clutter_stage_get_key_focus (CLUTTER_STAGE (stage));
2186               event->any.source = actor;
2187               if (G_UNLIKELY (actor == NULL))
2188                 {
2189                   g_warning ("No key focus set, discarding");
2190                   return;
2191                 }
2192             }
2193
2194           emit_keyboard_event (event);
2195         }
2196         break;
2197
2198       case CLUTTER_MOTION:
2199         device = event->motion.device;
2200
2201         /* Only stage gets motion events if clutter_set_motion_events is TRUE,
2202          * and the event is not a synthetic event with source set.
2203          */
2204         if (!context->motion_events_per_actor &&
2205             event->any.source == NULL)
2206           {
2207             /* Only stage gets motion events */
2208             event->any.source = stage;
2209
2210             /* global grabs */
2211             if (context->pointer_grab_actor != NULL)
2212               {
2213                 clutter_actor_event (context->pointer_grab_actor,
2214                                      event, FALSE);
2215                 break;
2216               }
2217             else if (device != NULL && device->pointer_grab_actor != NULL)
2218               {
2219                 clutter_actor_event (device->pointer_grab_actor,
2220                                      event, FALSE);
2221                 break;
2222               }
2223
2224             /* Trigger handlers on stage in both capture .. */
2225             if (!clutter_actor_event (stage, event, TRUE))
2226               {
2227                 /* and bubbling phase */
2228                 clutter_actor_event (stage, event, FALSE);
2229               }
2230             break;
2231           }
2232
2233         /* fallthrough */
2234
2235       case CLUTTER_BUTTON_PRESS:
2236       case CLUTTER_BUTTON_RELEASE:
2237       case CLUTTER_SCROLL:
2238         {
2239           ClutterActor *actor;
2240           gfloat x, y;
2241
2242           clutter_event_get_coords (event, &x, &y);
2243
2244           /* Only do a pick to find the source if source is not already set
2245            * (as it could be in a synthetic event)
2246            */
2247           if (event->any.source == NULL)
2248             {
2249               /* Handle release off stage */
2250               if ((x >= clutter_actor_get_width (stage) ||
2251                    y >= clutter_actor_get_height (stage) ||
2252                    x < 0 || y < 0))
2253                 {
2254                   if (event->type == CLUTTER_BUTTON_RELEASE)
2255                     {
2256                       CLUTTER_NOTE (EVENT,
2257                                     "Release off stage received at %.2f, %.2f",
2258                                     x, y);
2259
2260                       event->button.source = stage;
2261                       emit_pointer_event (event, event->button.device);
2262                     }
2263                   break;
2264                 }
2265
2266               /* Map the event to a reactive actor */
2267               actor = _clutter_do_pick (CLUTTER_STAGE (stage),
2268                                         x, y,
2269                                         CLUTTER_PICK_REACTIVE);
2270
2271               event->any.source = actor;
2272               if (!actor)
2273                 break;
2274             }
2275           else
2276             {
2277               /* use the source already set in the synthetic event */
2278               actor = event->any.source;
2279             }
2280
2281
2282           /* FIXME: for an optimisation should check if there are
2283            * actually any reactive actors and avoid the pick all together
2284            * (signalling just the stage). Should be big help for gles.
2285            */
2286
2287           CLUTTER_NOTE (EVENT,
2288                         "Reactive event received at %.2f, %.2f - actor: %p",
2289                         x, y,
2290                         actor);
2291
2292           /* Create, enter/leave events if needed */
2293           generate_enter_leave_events (event);
2294
2295           if (event->type != CLUTTER_MOTION)
2296             {
2297               /* Generate click count */
2298               event_click_count_generate (event);
2299             }
2300
2301           if (device == NULL)
2302             {
2303               switch (event->type)
2304                 {
2305                   case CLUTTER_BUTTON_PRESS:
2306                   case CLUTTER_BUTTON_RELEASE:
2307                     device = event->button.device;
2308                     break;
2309                   case CLUTTER_SCROLL:
2310                     device = event->scroll.device;
2311                     break;
2312                   case CLUTTER_MOTION:
2313                     /* already handled in the MOTION case of the switch */
2314                   default:
2315                     break;
2316                 }
2317             }
2318
2319           emit_pointer_event (event, device);
2320           break;
2321         }
2322
2323       case CLUTTER_STAGE_STATE:
2324         /* fullscreen / focus - forward to stage */
2325         event->any.source = stage;
2326         clutter_stage_event (CLUTTER_STAGE (stage), event);
2327         break;
2328
2329       case CLUTTER_CLIENT_MESSAGE:
2330         break;
2331     }
2332 }
2333
2334 /**
2335  * clutter_get_actor_by_gid
2336  * @id: a #ClutterActor ID.
2337  *
2338  * Retrieves the #ClutterActor with @id.
2339  *
2340  * Return value: (transfer none): the actor with the passed id or %NULL.
2341  *   The returned actor does not have its reference count increased.
2342  *
2343  * Since: 0.6
2344  */
2345 ClutterActor*
2346 clutter_get_actor_by_gid (guint32 id)
2347 {
2348   ClutterMainContext *context;
2349
2350   context = _clutter_context_get_default ();
2351
2352   g_return_val_if_fail (context != NULL, NULL);
2353
2354   return CLUTTER_ACTOR (clutter_id_pool_lookup (context->id_pool, id));
2355 }
2356
2357 void
2358 clutter_base_init (void)
2359 {
2360   static gboolean initialised = FALSE;
2361
2362   if (!initialised)
2363     {
2364       GType foo; /* Quiet gcc */
2365
2366       initialised = TRUE;
2367
2368       bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
2369       bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
2370
2371       /* initialise GLib type system */
2372       g_type_init ();
2373
2374       /* CLUTTER_TYPE_ACTOR */
2375       foo = clutter_actor_get_type ();
2376     }
2377 }
2378
2379 /**
2380  * clutter_get_default_frame_rate:
2381  *
2382  * Retrieves the default frame rate. See clutter_set_default_frame_rate().
2383  *
2384  * Return value: the default frame rate
2385  *
2386  * Since: 0.6
2387  */
2388 guint
2389 clutter_get_default_frame_rate (void)
2390 {
2391   ClutterMainContext *context;
2392
2393   context = _clutter_context_get_default ();
2394
2395   return context->frame_rate;
2396 }
2397
2398 /**
2399  * clutter_set_default_frame_rate:
2400  * @frames_per_sec: the new default frame rate
2401  *
2402  * Sets the default frame rate. This frame rate will be used to limit
2403  * the number of frames drawn if Clutter is not able to synchronize
2404  * with the vertical refresh rate of the display. When synchronization
2405  * is possible, this value is ignored.
2406  *
2407  * Since: 0.6
2408  */
2409 void
2410 clutter_set_default_frame_rate (guint frames_per_sec)
2411 {
2412   ClutterMainContext *context;
2413
2414   context = _clutter_context_get_default ();
2415
2416   if (context->frame_rate != frames_per_sec)
2417     context->frame_rate = frames_per_sec;
2418 }
2419
2420
2421 static void
2422 on_pointer_grab_weak_notify (gpointer data,
2423                              GObject *where_the_object_was)
2424 {
2425   ClutterInputDevice *dev = (ClutterInputDevice *)data;
2426   ClutterMainContext *context;
2427
2428   context = _clutter_context_get_default ();
2429
2430   if (dev)
2431     {
2432       dev->pointer_grab_actor = NULL;
2433       clutter_ungrab_pointer_for_device (dev->id);
2434     }
2435   else
2436     {
2437       context->pointer_grab_actor = NULL;
2438       clutter_ungrab_pointer ();
2439     }
2440 }
2441
2442 /**
2443  * clutter_grab_pointer:
2444  * @actor: a #ClutterActor
2445  *
2446  * Grabs pointer events, after the grab is done all pointer related events
2447  * (press, motion, release, enter, leave and scroll) are delivered to this
2448  * actor directly without passing through both capture and bubble phases of
2449  * the event delivery chain. The source set in the event will be the actor
2450  * that would have received the event if the pointer grab was not in effect.
2451  *
2452  * <note><para>Grabs completely override the entire event delivery chain
2453  * done by Clutter. Pointer grabs should only be used as a last resource;
2454  * using the #ClutterActor::captured-event signal should always be the
2455  * preferred way to intercept event delivery to reactive actors.</para></note>
2456  *
2457  * If you wish to grab all the pointer events for a specific input device,
2458  * you should use clutter_grab_pointer_for_device().
2459  *
2460  * Since: 0.6
2461  */
2462 void
2463 clutter_grab_pointer (ClutterActor *actor)
2464 {
2465   ClutterMainContext *context;
2466
2467   g_return_if_fail (actor == NULL || CLUTTER_IS_ACTOR (actor));
2468
2469   context = _clutter_context_get_default ();
2470
2471   if (context->pointer_grab_actor == actor)
2472     return;
2473
2474   if (context->pointer_grab_actor)
2475     {
2476       g_object_weak_unref (G_OBJECT (context->pointer_grab_actor),
2477                            on_pointer_grab_weak_notify,
2478                            NULL);
2479       context->pointer_grab_actor = NULL;
2480     }
2481
2482   if (actor)
2483     {
2484       context->pointer_grab_actor = actor;
2485
2486       g_object_weak_ref (G_OBJECT (actor),
2487                          on_pointer_grab_weak_notify,
2488                          NULL);
2489     }
2490 }
2491
2492 /**
2493  * clutter_grab_pointer_for_device:
2494  * @actor: a #ClutterActor
2495  * @id: a device id, or -1
2496  *
2497  * Grabs all the pointer events coming from the device @id for @actor.
2498  *
2499  * If @id is -1 then this function is equivalent to clutter_grab_pointer().
2500  *
2501  * Since: 0.8
2502  */
2503 void
2504 clutter_grab_pointer_for_device (ClutterActor *actor,
2505                                  gint          id)
2506 {
2507   ClutterInputDevice *dev;
2508
2509   g_return_if_fail (actor == NULL || CLUTTER_IS_ACTOR (actor));
2510
2511   /* essentially a global grab */
2512   if (id == -1)
2513     {
2514       clutter_grab_pointer (actor);
2515       return;
2516     }
2517
2518   dev = clutter_get_input_device_for_id (id);
2519
2520   if (!dev)
2521     return;
2522
2523   if (dev->pointer_grab_actor == actor)
2524     return;
2525
2526   if (dev->pointer_grab_actor)
2527     {
2528       g_object_weak_unref (G_OBJECT (dev->pointer_grab_actor),
2529                           on_pointer_grab_weak_notify,
2530                           dev);
2531       dev->pointer_grab_actor = NULL;
2532     }
2533
2534   if (actor)
2535     {
2536       dev->pointer_grab_actor = actor;
2537
2538       g_object_weak_ref (G_OBJECT (actor),
2539                         on_pointer_grab_weak_notify,
2540                         dev);
2541     }
2542 }
2543
2544
2545 /**
2546  * clutter_ungrab_pointer:
2547  *
2548  * Removes an existing grab of the pointer.
2549  *
2550  * Since: 0.6
2551  */
2552 void
2553 clutter_ungrab_pointer (void)
2554 {
2555   clutter_grab_pointer (NULL);
2556 }
2557
2558 /**
2559  * clutter_ungrab_pointer_for_device:
2560  * @id: a device id
2561  *
2562  * Removes an existing grab of the pointer events for device @id.
2563  *
2564  * Since: 0.8
2565  */
2566 void
2567 clutter_ungrab_pointer_for_device (gint id)
2568 {
2569   clutter_grab_pointer_for_device (NULL, id);
2570 }
2571
2572
2573 /**
2574  * clutter_get_pointer_grab:
2575  *
2576  * Queries the current pointer grab of clutter.
2577  *
2578  * Return value: (transfer none): the actor currently holding the pointer grab, or NULL if there is no grab.
2579  *
2580  * Since: 0.6
2581  */
2582 ClutterActor *
2583 clutter_get_pointer_grab (void)
2584 {
2585   ClutterMainContext *context;
2586   context = _clutter_context_get_default ();
2587
2588   return context->pointer_grab_actor;
2589 }
2590
2591
2592 static void
2593 on_keyboard_grab_weak_notify (gpointer data,
2594                               GObject *where_the_object_was)
2595 {
2596   ClutterMainContext *context;
2597
2598   context = _clutter_context_get_default ();
2599   context->keyboard_grab_actor = NULL;
2600
2601   clutter_ungrab_keyboard ();
2602 }
2603
2604 /**
2605  * clutter_grab_keyboard:
2606  * @actor: a #ClutterActor
2607  *
2608  * Grabs keyboard events, after the grab is done keyboard
2609  * events (#ClutterActor::key-press-event and #ClutterActor::key-release-event)
2610  * are delivered to this actor directly. The source set in the event will be
2611  * the actor that would have received the event if the keyboard grab was not
2612  * in effect.
2613  *
2614  * Like pointer grabs, keyboard grabs should only be used as a last
2615  * resource.
2616  *
2617  * See also clutter_stage_set_key_focus() and clutter_actor_grab_key_focus()
2618  * to perform a "soft" key grab and assign key focus to a specific actor.
2619  *
2620  * Since: 0.6
2621  */
2622 void
2623 clutter_grab_keyboard (ClutterActor *actor)
2624 {
2625   ClutterMainContext *context;
2626
2627   g_return_if_fail (actor == NULL || CLUTTER_IS_ACTOR (actor));
2628
2629   context = _clutter_context_get_default ();
2630
2631   if (context->keyboard_grab_actor == actor)
2632     return;
2633
2634   if (context->keyboard_grab_actor)
2635     {
2636       g_object_weak_unref (G_OBJECT (context->keyboard_grab_actor),
2637                            on_keyboard_grab_weak_notify,
2638                            NULL);
2639       context->keyboard_grab_actor = NULL;
2640     }
2641
2642   if (actor)
2643     {
2644       context->keyboard_grab_actor = actor;
2645
2646       g_object_weak_ref (G_OBJECT (actor),
2647                          on_keyboard_grab_weak_notify,
2648                          NULL);
2649     }
2650 }
2651
2652 /**
2653  * clutter_ungrab_keyboard:
2654  *
2655  * Removes an existing grab of the keyboard.
2656  *
2657  * Since: 0.6
2658  */
2659 void
2660 clutter_ungrab_keyboard (void)
2661 {
2662   clutter_grab_keyboard (NULL);
2663 }
2664
2665 /**
2666  * clutter_get_keyboard_grab:
2667  *
2668  * Queries the current keyboard grab of clutter.
2669  *
2670  * Return value: (transfer none): the actor currently holding the keyboard grab, or NULL if there is no grab.
2671  *
2672  * Since: 0.6
2673  */
2674 ClutterActor *
2675 clutter_get_keyboard_grab (void)
2676 {
2677   ClutterMainContext *context;
2678
2679   context = _clutter_context_get_default ();
2680
2681   return context->keyboard_grab_actor;
2682 }
2683
2684 /**
2685  * clutter_clear_glyph_cache:
2686  *
2687  * Clears the internal cache of glyphs used by the Pango
2688  * renderer. This will free up some memory and GL texture
2689  * resources. The cache will be automatically refilled as more text is
2690  * drawn.
2691  *
2692  * Since: 0.8
2693  */
2694 void
2695 clutter_clear_glyph_cache (void)
2696 {
2697   if (CLUTTER_CONTEXT ()->font_map)
2698     cogl_pango_font_map_clear_glyph_cache (CLUTTER_CONTEXT ()->font_map);
2699 }
2700
2701 /**
2702  * clutter_set_font_flags:
2703  * @flags: The new flags
2704  *
2705  * Sets the font quality options for subsequent text rendering
2706  * operations.
2707  *
2708  * Using mipmapped textures will improve the quality for scaled down
2709  * text but will use more texture memory.
2710  *
2711  * Enabling hinting improves text quality for static text but may
2712  * introduce some artifacts if the text is animated.
2713  *
2714  * Since: 1.0
2715  */
2716 void
2717 clutter_set_font_flags (ClutterFontFlags flags)
2718 {
2719   ClutterFontFlags old_flags, changed_flags;
2720   const cairo_font_options_t *font_options;
2721   cairo_font_options_t *new_font_options;
2722   ClutterBackend *backend;
2723
2724   backend = clutter_get_default_backend ();
2725
2726   if (CLUTTER_CONTEXT ()->font_map)
2727     cogl_pango_font_map_set_use_mipmapping (CLUTTER_CONTEXT ()->font_map,
2728                                             (flags
2729                                              & CLUTTER_FONT_MIPMAPPING) != 0);
2730
2731   old_flags = clutter_get_font_flags ();
2732
2733   font_options = clutter_backend_get_font_options (backend);
2734   new_font_options = cairo_font_options_copy (font_options);
2735
2736   /* Only set the font options that have actually changed so we don't
2737      override a detailed setting from the backend */
2738   changed_flags = old_flags ^ flags;
2739
2740   if ((changed_flags & CLUTTER_FONT_HINTING))
2741     cairo_font_options_set_hint_style (new_font_options,
2742                                        (flags & CLUTTER_FONT_HINTING)
2743                                        ? CAIRO_HINT_STYLE_FULL
2744                                        : CAIRO_HINT_STYLE_NONE);
2745
2746   clutter_backend_set_font_options (backend, new_font_options);
2747
2748   cairo_font_options_destroy (new_font_options);
2749
2750   if (CLUTTER_CONTEXT ()->pango_context)
2751     update_pango_context (backend, CLUTTER_CONTEXT ()->pango_context);
2752 }
2753
2754 /**
2755  * clutter_get_font_flags:
2756  *
2757  * Gets the current font flags for rendering text. See
2758  * clutter_set_font_flags().
2759  *
2760  * Return value: The font flags
2761  *
2762  * Since: 1.0
2763  */
2764 ClutterFontFlags
2765 clutter_get_font_flags (void)
2766 {
2767   ClutterMainContext *ctxt = CLUTTER_CONTEXT ();
2768   CoglPangoFontMap *font_map = NULL;
2769   const cairo_font_options_t *font_options;
2770   ClutterFontFlags flags = 0;
2771
2772   font_map = CLUTTER_CONTEXT ()->font_map;
2773
2774   if (G_LIKELY (font_map)
2775       && cogl_pango_font_map_get_use_mipmapping (font_map))
2776     flags |= CLUTTER_FONT_MIPMAPPING;
2777
2778   font_options = clutter_backend_get_font_options (ctxt->backend);
2779
2780   if ((cairo_font_options_get_hint_style (font_options)
2781        != CAIRO_HINT_STYLE_DEFAULT)
2782       && (cairo_font_options_get_hint_style (font_options)
2783           != CAIRO_HINT_STYLE_NONE))
2784     flags |= CLUTTER_FONT_HINTING;
2785
2786   return flags;
2787 }
2788
2789 /**
2790  * clutter_get_input_device_for_id:
2791  * @id: a device id
2792  *
2793  * Retrieves the #ClutterInputDevice from its id.
2794  *
2795  * Return value: (transfer none): a #ClutterInputDevice, or %NULL
2796  *
2797  * Since: 0.8
2798  */
2799 ClutterInputDevice *
2800 clutter_get_input_device_for_id (gint id)
2801 {
2802   GSList *item;
2803   ClutterInputDevice *device = NULL;
2804   ClutterMainContext  *context;
2805
2806   context = _clutter_context_get_default ();
2807
2808   for (item = context->input_devices;
2809        item != NULL;
2810        item = item->next)
2811   {
2812     device = item->data;
2813
2814     if (device->id == id)
2815       return device;
2816   }
2817
2818   return NULL;
2819 }
2820
2821 /**
2822  * clutter_get_font_map:
2823  *
2824  * Retrieves the #PangoFontMap instance used by Clutter.
2825  * You can use the global font map object with the COGL
2826  * Pango API.
2827  *
2828  * Return value: (transfer none): the #PangoFontMap instance. The returned
2829  *   value is owned by Clutter and it should never be unreferenced.
2830  *
2831  * Since: 1.0
2832  */
2833 PangoFontMap *
2834 clutter_get_font_map (void)
2835 {
2836   if (CLUTTER_CONTEXT ()->font_map)
2837     return PANGO_FONT_MAP (CLUTTER_CONTEXT ()->font_map);
2838
2839   return NULL;
2840 }
2841
2842 typedef struct _ClutterRepaintFunction
2843 {
2844   guint id;
2845   GSourceFunc func;
2846   gpointer data;
2847   GDestroyNotify notify;
2848 } ClutterRepaintFunction;
2849
2850 /**
2851  * clutter_threads_remove_repaint_func:
2852  * @handle_id: an unsigned integer greater than zero
2853  *
2854  * Removes the repaint function with @handle_id as its id
2855  *
2856  * Since: 1.0
2857  */
2858 void
2859 clutter_threads_remove_repaint_func (guint handle_id)
2860 {
2861   ClutterRepaintFunction *repaint_func;
2862   ClutterMainContext *context;
2863   GList *l;
2864
2865   g_return_if_fail (handle_id > 0);
2866
2867   context = CLUTTER_CONTEXT ();
2868   l = context->repaint_funcs;
2869   while (l != NULL)
2870     {
2871       repaint_func = l->data;
2872
2873       if (repaint_func->id == handle_id)
2874         {
2875           context->repaint_funcs =
2876             g_list_remove_link (context->repaint_funcs, l);
2877
2878           g_list_free (l);
2879
2880           if (repaint_func->notify)
2881             repaint_func->notify (repaint_func->data);
2882
2883           g_slice_free (ClutterRepaintFunction, repaint_func);
2884
2885           return;
2886         }
2887
2888       l = l->next;
2889     }
2890 }
2891
2892 /**
2893  * clutter_threads_add_repaint_func:
2894  * @func: the function to be called within the paint cycle
2895  * @data: data to be passed to the function, or %NULL
2896  * @notify: function to be called when removing the repaint
2897  *    function, or %NULL
2898  *
2899  * Adds a function to be called whenever Clutter is repainting a Stage.
2900  * If the function returns %FALSE it is automatically removed from the
2901  * list of repaint functions and will not be called again.
2902  *
2903  * This function is guaranteed to be called from within the same thread
2904  * that called clutter_main(), and while the Clutter lock is being held.
2905  *
2906  * A repaint function is useful to ensure that an update of the scenegraph
2907  * is performed before the scenegraph is repainted; for instance, uploading
2908  * a frame from a video into a #ClutterTexture.
2909  *
2910  * When the repaint function is removed (either because it returned %FALSE
2911  * or because clutter_threads_remove_repaint_func() has been called) the
2912  * @notify function will be called, if any is set.
2913  *
2914  * Return value: the ID (greater than 0) of the repaint function. You
2915  *   can use the returned integer to remove the repaint function by
2916  *   calling clutter_threads_remove_repaint_func().
2917  *
2918  * Since: 1.0
2919  */
2920 guint
2921 clutter_threads_add_repaint_func (GSourceFunc    func,
2922                                   gpointer       data,
2923                                   GDestroyNotify notify)
2924 {
2925   static guint repaint_id = 1;
2926   ClutterMainContext *context;
2927   ClutterRepaintFunction *repaint_func;
2928
2929   g_return_val_if_fail (func != NULL, 0);
2930
2931   context = CLUTTER_CONTEXT ();
2932
2933   /* XXX lock the context */
2934
2935   repaint_func = g_slice_new (ClutterRepaintFunction);
2936
2937   repaint_func->id = repaint_id++;
2938   repaint_func->func = func;
2939   repaint_func->data = data;
2940   repaint_func->notify = notify;
2941
2942   context->repaint_funcs = g_list_prepend (context->repaint_funcs,
2943                                            repaint_func);
2944
2945   /* XXX unlock the context */
2946
2947   return repaint_func->id;
2948 }
2949
2950 /*
2951  * _clutter_run_repaint_functions:
2952  *
2953  * Executes the repaint functions added using the
2954  * clutter_threads_add_repaint_func() function.
2955  *
2956  * Must be called before calling clutter_redraw() and
2957  * with the Clutter thread lock held.
2958  */
2959 void
2960 _clutter_run_repaint_functions (void)
2961 {
2962   ClutterMainContext *context = CLUTTER_CONTEXT ();
2963   ClutterRepaintFunction *repaint_func;
2964   GList *reinvoke_list, *l;
2965
2966   if (context->repaint_funcs == NULL)
2967     return;
2968
2969   reinvoke_list = NULL;
2970
2971   /* consume the whole list while we execute the functions */
2972   while (context->repaint_funcs)
2973     {
2974       gboolean res = FALSE;
2975
2976       repaint_func = context->repaint_funcs->data;
2977
2978       l = context->repaint_funcs;
2979       context->repaint_funcs =
2980         g_list_remove_link (context->repaint_funcs, context->repaint_funcs);
2981
2982       g_list_free (l);
2983
2984       res = repaint_func->func (repaint_func->data);
2985
2986       if (res)
2987         reinvoke_list = g_list_prepend (reinvoke_list, repaint_func);
2988       else
2989         {
2990           if (repaint_func->notify)
2991             repaint_func->notify (repaint_func->data);
2992
2993           g_slice_free (ClutterRepaintFunction, repaint_func);
2994         }
2995     }
2996
2997   if (reinvoke_list)
2998     context->repaint_funcs = reinvoke_list;
2999 }
3000
3001 /**
3002  * clutter_check_version:
3003  * @major: major version, like 1 in 1.2.3
3004  * @minor: minor version, like 2 in 1.2.3
3005  * @micro: micro version, like 3 in 1.2.3
3006  *
3007  * Run-time version check, to check the version the Clutter library
3008  * that an application is currently linked against
3009  *
3010  * This is the run-time equivalent of the compile-time %CLUTTER_CHECK_VERSION
3011  * pre-processor macro
3012  *
3013  * Return value: %TRUE if the version of the Clutter library is
3014  *   greater than (@major, @minor, @micro), and %FALSE otherwise
3015  *
3016  * Since: 1.2
3017  */
3018 gboolean
3019 clutter_check_version (guint major,
3020                        guint minor,
3021                        guint micro)
3022 {
3023   return (clutter_major_version > major ||
3024           (clutter_major_version == major &&
3025            clutter_minor_version > minor) ||
3026           (clutter_major_version == major &&
3027            clutter_minor_version == minor &&
3028            clutter_micro_version >= micro));
3029 }