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, see <http://www.gnu.org/licenses/>.
22  */
23 
24 /**
25  * SECTION:clutter-main
26  * @short_description: Various 'global' Clutter functions.
27  *
28  * Functions to retrieve various global Clutter resources and other utility
29  * functions for mainloops, events and threads
30  *
31  * ## The Clutter Threading Model
32  *
33  * Clutter is *thread-aware*: all operations performed by Clutter are assumed
34  * to be under the Big Clutter Lock, which is created when the threading is
35  * initialized through clutter_init(), and entered when calling user-related
36  * code during event handling and actor drawing.
37  *
38  * The only safe and portable way to use the Clutter API in a multi-threaded
39  * environment is to only access the Clutter API from a thread that did called
40  * clutter_init() and clutter_main().
41  *
42  * The common pattern for using threads with Clutter is to use worker threads
43  * to perform blocking operations and then install idle or timeout sources with
44  * the result when the thread finishes, and update the UI from those callbacks.
45  *
46  * For a working example of how to use a worker thread to update the UI, see
47  * [threads.c](https://git.gnome.org/browse/clutter/tree/examples/threads.c?h=clutter-1.18)
48  */
49 
50 #include "clutter-build-config.h"
51 
52 #include <stdlib.h>
53 
54 #include "clutter-actor-private.h"
55 #include "clutter-backend-private.h"
56 #include "clutter-debug.h"
57 #include "clutter-event-private.h"
58 #include "clutter-feature.h"
59 #include "clutter-input-device-private.h"
60 #include "clutter-input-pointer-a11y-private.h"
61 #include "clutter-graphene.h"
62 #include "clutter-main.h"
63 #include "clutter-mutter.h"
64 #include "clutter-paint-node-private.h"
65 #include "clutter-private.h"
66 #include "clutter-settings-private.h"
67 #include "clutter-stage.h"
68 #include "clutter-stage-manager.h"
69 #include "clutter-stage-private.h"
70 #include "clutter-backend-private.h"
71 
72 #include <cogl/cogl.h>
73 #include <cogl-pango/cogl-pango.h>
74 
75 #include "cally/cally.h" /* For accessibility support */
76 
77 /* main context */
78 static ClutterMainContext *ClutterCntx       = NULL;
79 
80 /* command line options */
81 static gboolean clutter_is_initialized       = FALSE;
82 static gboolean clutter_show_fps             = FALSE;
83 static gboolean clutter_fatal_warnings       = FALSE;
84 static gboolean clutter_disable_mipmap_text  = FALSE;
85 static gboolean clutter_enable_accessibility = TRUE;
86 static gboolean clutter_sync_to_vblank       = TRUE;
87 
88 static guint clutter_default_fps             = 60;
89 
90 static ClutterTextDirection clutter_text_direction = CLUTTER_TEXT_DIRECTION_LTR;
91 
92 /* debug flags */
93 guint clutter_debug_flags       = 0;
94 guint clutter_paint_debug_flags = 0;
95 guint clutter_pick_debug_flags  = 0;
96 
97 /* A constant added to heuristic max render time to account for variations
98  * in the estimates.
99  */
100 int clutter_max_render_time_constant_us = 2000;
101 
102 #ifdef CLUTTER_ENABLE_DEBUG
103 static const GDebugKey clutter_debug_keys[] = {
104   { "misc", CLUTTER_DEBUG_MISC },
105   { "actor", CLUTTER_DEBUG_ACTOR },
106   { "texture", CLUTTER_DEBUG_TEXTURE },
107   { "event", CLUTTER_DEBUG_EVENT },
108   { "paint", CLUTTER_DEBUG_PAINT },
109   { "pick", CLUTTER_DEBUG_PICK },
110   { "pango", CLUTTER_DEBUG_PANGO },
111   { "backend", CLUTTER_DEBUG_BACKEND },
112   { "scheduler", CLUTTER_DEBUG_SCHEDULER },
113   { "script", CLUTTER_DEBUG_SCRIPT },
114   { "shader", CLUTTER_DEBUG_SHADER },
115   { "animation", CLUTTER_DEBUG_ANIMATION },
116   { "layout", CLUTTER_DEBUG_LAYOUT },
117   { "clipping", CLUTTER_DEBUG_CLIPPING },
118   { "oob-transforms", CLUTTER_DEBUG_OOB_TRANSFORMS },
119   { "frame-timings", CLUTTER_DEBUG_FRAME_TIMINGS },
120   { "detailed-trace", CLUTTER_DEBUG_DETAILED_TRACE },
121 };
122 #endif /* CLUTTER_ENABLE_DEBUG */
123 
124 static const GDebugKey clutter_pick_debug_keys[] = {
125   { "nop-picking", CLUTTER_DEBUG_NOP_PICKING },
126 };
127 
128 static const GDebugKey clutter_paint_debug_keys[] = {
129   { "disable-swap-events", CLUTTER_DEBUG_DISABLE_SWAP_EVENTS },
130   { "disable-clipped-redraws", CLUTTER_DEBUG_DISABLE_CLIPPED_REDRAWS },
131   { "redraws", CLUTTER_DEBUG_REDRAWS },
132   { "paint-volumes", CLUTTER_DEBUG_PAINT_VOLUMES },
133   { "disable-culling", CLUTTER_DEBUG_DISABLE_CULLING },
134   { "disable-offscreen-redirect", CLUTTER_DEBUG_DISABLE_OFFSCREEN_REDIRECT },
135   { "continuous-redraw", CLUTTER_DEBUG_CONTINUOUS_REDRAW },
136   { "paint-deform-tiles", CLUTTER_DEBUG_PAINT_DEFORM_TILES },
137   { "damage-region", CLUTTER_DEBUG_PAINT_DAMAGE_REGION },
138   { "disable-dynamic-max-render-time", CLUTTER_DEBUG_DISABLE_DYNAMIC_MAX_RENDER_TIME },
139   { "max-render-time", CLUTTER_DEBUG_PAINT_MAX_RENDER_TIME },
140 };
141 
142 gboolean
_clutter_context_get_show_fps(void)143 _clutter_context_get_show_fps (void)
144 {
145   ClutterMainContext *context = _clutter_context_get_default ();
146 
147   return context->show_fps;
148 }
149 
150 /**
151  * clutter_get_accessibility_enabled:
152  *
153  * Returns whether Clutter has accessibility support enabled.  As
154  * least, a value of TRUE means that there are a proper AtkUtil
155  * implementation available
156  *
157  * Return value: %TRUE if Clutter has accessibility support enabled
158  *
159  * Since: 1.4
160  */
161 gboolean
clutter_get_accessibility_enabled(void)162 clutter_get_accessibility_enabled (void)
163 {
164   return cally_get_cally_initialized ();
165 }
166 
167 /**
168  * clutter_disable_accessibility:
169  *
170  * Disable loading the accessibility support. It has the same effect
171  * as setting the environment variable
172  * CLUTTER_DISABLE_ACCESSIBILITY. For the same reason, this method
173  * should be called before clutter_init().
174  *
175  * Since: 1.14
176  */
177 void
clutter_disable_accessibility(void)178 clutter_disable_accessibility (void)
179 {
180   if (clutter_is_initialized)
181     {
182       g_warning ("clutter_disable_accessibility() can only be called before "
183                  "initializing Clutter.");
184       return;
185     }
186 
187   clutter_enable_accessibility = FALSE;
188 }
189 
190 static CoglPangoFontMap *
clutter_context_get_pango_fontmap(void)191 clutter_context_get_pango_fontmap (void)
192 {
193   ClutterMainContext *self;
194   CoglPangoFontMap *font_map;
195   gdouble resolution;
196   gboolean use_mipmapping;
197 
198   self = _clutter_context_get_default ();
199   if (G_LIKELY (self->font_map != NULL))
200     return self->font_map;
201 
202   font_map = COGL_PANGO_FONT_MAP (cogl_pango_font_map_new ());
203 
204   resolution = clutter_backend_get_resolution (self->backend);
205   cogl_pango_font_map_set_resolution (font_map, resolution);
206 
207   use_mipmapping = !clutter_disable_mipmap_text;
208   cogl_pango_font_map_set_use_mipmapping (font_map, use_mipmapping);
209 
210   self->font_map = font_map;
211 
212   return self->font_map;
213 }
214 
215 static ClutterTextDirection
clutter_get_text_direction(void)216 clutter_get_text_direction (void)
217 {
218   ClutterTextDirection dir = CLUTTER_TEXT_DIRECTION_LTR;
219   const gchar *direction;
220 
221   direction = g_getenv ("CLUTTER_TEXT_DIRECTION");
222   if (direction && *direction != '\0')
223     {
224       if (strcmp (direction, "rtl") == 0)
225         dir = CLUTTER_TEXT_DIRECTION_RTL;
226       else if (strcmp (direction, "ltr") == 0)
227         dir = CLUTTER_TEXT_DIRECTION_LTR;
228     }
229   else
230     {
231       /* Re-use GTK+'s LTR/RTL handling */
232       const char *e = g_dgettext ("gtk30", "default:LTR");
233 
234       if (strcmp (e, "default:RTL") == 0)
235         dir = CLUTTER_TEXT_DIRECTION_RTL;
236       else if (strcmp (e, "default:LTR") == 0)
237         dir = CLUTTER_TEXT_DIRECTION_LTR;
238       else
239         g_warning ("Whoever translated default:LTR did so wrongly.");
240     }
241 
242   CLUTTER_NOTE (MISC, "Text direction: %s",
243                 dir == CLUTTER_TEXT_DIRECTION_RTL ? "rtl" : "ltr");
244 
245   return dir;
246 }
247 
248 gboolean
_clutter_threads_dispatch(gpointer data)249 _clutter_threads_dispatch (gpointer data)
250 {
251   ClutterThreadsDispatch *dispatch = data;
252   gboolean ret = FALSE;
253 
254   if (!g_source_is_destroyed (g_main_current_source ()))
255     ret = dispatch->func (dispatch->data);
256 
257   return ret;
258 }
259 
260 void
_clutter_threads_dispatch_free(gpointer data)261 _clutter_threads_dispatch_free (gpointer data)
262 {
263   ClutterThreadsDispatch *dispatch = data;
264 
265   /* XXX - we cannot hold the thread lock here because the main loop
266    * might destroy a source while still in the dispatcher function; so
267    * knowing whether the lock is being held or not is not known a priori.
268    *
269    * see bug: http://bugzilla.gnome.org/show_bug.cgi?id=459555
270    */
271   if (dispatch->notify)
272     dispatch->notify (dispatch->data);
273 
274   g_free (dispatch);
275 }
276 
277 /**
278  * clutter_threads_add_idle_full: (rename-to clutter_threads_add_idle)
279  * @priority: the priority of the timeout source. Typically this will be in the
280  *    range between #G_PRIORITY_DEFAULT_IDLE and #G_PRIORITY_HIGH_IDLE
281  * @func: function to call
282  * @data: data to pass to the function
283  * @notify: functio to call when the idle source is removed
284  *
285  * Adds a function to be called whenever there are no higher priority
286  * events pending. If the function returns %FALSE it is automatically
287  * removed from the list of event sources and will not be called again.
288  *
289  * This function can be considered a thread-safe variant of g_idle_add_full():
290  * it will call @function while holding the Clutter lock. It is logically
291  * equivalent to the following implementation:
292  *
293  * |[
294  * static gboolean
295  * idle_safe_callback (gpointer data)
296  * {
297  *    SafeClosure *closure = data;
298  *    gboolean res = FALSE;
299  *
300  *    // the callback does not need to acquire the Clutter
301  *     / lock itself, as it is held by the this proxy handler
302  *     //
303  *    res = closure->callback (closure->data);
304  *
305  *    return res;
306  * }
307  * static gulong
308  * add_safe_idle (GSourceFunc callback,
309  *                gpointer    data)
310  * {
311  *   SafeClosure *closure = g_new0 (SafeClosure, 1);
312  *
313  *   closure->callback = callback;
314  *   closure->data = data;
315  *
316  *   return g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
317  *                           idle_safe_callback,
318  *                           closure,
319  *                           g_free)
320  * }
321  *]|
322  *
323  * This function should be used by threaded applications to make sure
324  * that @func is emitted under the Clutter threads lock and invoked
325  * from the same thread that started the Clutter main loop. For instance,
326  * it can be used to update the UI using the results from a worker
327  * thread:
328  *
329  * |[
330  * static gboolean
331  * update_ui (gpointer data)
332  * {
333  *   SomeClosure *closure = data;
334  *
335  *   // it is safe to call Clutter API from this function because
336  *    / it is invoked from the same thread that started the main
337  *    / loop and under the Clutter thread lock
338  *    //
339  *   clutter_label_set_text (CLUTTER_LABEL (closure->label),
340  *                           closure->text);
341  *
342  *   g_object_unref (closure->label);
343  *   g_free (closure);
344  *
345  *   return FALSE;
346  * }
347  *
348  *   // within another thread //
349  *   closure = g_new0 (SomeClosure, 1);
350  *   // always take a reference on GObject instances //
351  *   closure->label = g_object_ref (my_application->label);
352  *   closure->text = g_strdup (processed_text_to_update_the_label);
353  *
354  *   clutter_threads_add_idle_full (G_PRIORITY_HIGH_IDLE,
355  *                                  update_ui,
356  *                                  closure,
357  *                                  NULL);
358  * ]|
359  *
360  * Return value: the ID (greater than 0) of the event source.
361  *
362  * Since: 0.4
363  */
364 guint
clutter_threads_add_idle_full(gint priority,GSourceFunc func,gpointer data,GDestroyNotify notify)365 clutter_threads_add_idle_full (gint           priority,
366                                GSourceFunc    func,
367                                gpointer       data,
368                                GDestroyNotify notify)
369 {
370   ClutterThreadsDispatch *dispatch;
371 
372   g_return_val_if_fail (func != NULL, 0);
373 
374   dispatch = g_new0 (ClutterThreadsDispatch, 1);
375   dispatch->func = func;
376   dispatch->data = data;
377   dispatch->notify = notify;
378 
379   return g_idle_add_full (priority,
380                           _clutter_threads_dispatch, dispatch,
381                           _clutter_threads_dispatch_free);
382 }
383 
384 /**
385  * clutter_threads_add_idle: (skip)
386  * @func: function to call
387  * @data: data to pass to the function
388  *
389  * Simple wrapper around clutter_threads_add_idle_full() using the
390  * default priority.
391  *
392  * Return value: the ID (greater than 0) of the event source.
393  *
394  * Since: 0.4
395  */
396 guint
clutter_threads_add_idle(GSourceFunc func,gpointer data)397 clutter_threads_add_idle (GSourceFunc func,
398                           gpointer    data)
399 {
400   g_return_val_if_fail (func != NULL, 0);
401 
402   return clutter_threads_add_idle_full (G_PRIORITY_DEFAULT_IDLE,
403                                         func, data,
404                                         NULL);
405 }
406 
407 /**
408  * clutter_threads_add_timeout_full: (rename-to clutter_threads_add_timeout)
409  * @priority: the priority of the timeout source. Typically this will be in the
410  *            range between #G_PRIORITY_DEFAULT and #G_PRIORITY_HIGH.
411  * @interval: the time between calls to the function, in milliseconds
412  * @func: function to call
413  * @data: data to pass to the function
414  * @notify: function to call when the timeout source is removed
415  *
416  * Sets a function to be called at regular intervals holding the Clutter
417  * threads lock, with the given priority. The function is called repeatedly
418  * until it returns %FALSE, at which point the timeout is automatically
419  * removed and the function will not be called again. The @notify function
420  * is called when the timeout is removed.
421  *
422  * The first call to the function will be at the end of the first @interval.
423  *
424  * It is important to note that, due to how the Clutter main loop is
425  * implemented, the timing will not be accurate and it will not try to
426  * "keep up" with the interval.
427  *
428  * See also clutter_threads_add_idle_full().
429  *
430  * Return value: the ID (greater than 0) of the event source.
431  *
432  * Since: 0.4
433  */
434 guint
clutter_threads_add_timeout_full(gint priority,guint interval,GSourceFunc func,gpointer data,GDestroyNotify notify)435 clutter_threads_add_timeout_full (gint           priority,
436                                   guint          interval,
437                                   GSourceFunc    func,
438                                   gpointer       data,
439                                   GDestroyNotify notify)
440 {
441   ClutterThreadsDispatch *dispatch;
442 
443   g_return_val_if_fail (func != NULL, 0);
444 
445   dispatch = g_new0 (ClutterThreadsDispatch, 1);
446   dispatch->func = func;
447   dispatch->data = data;
448   dispatch->notify = notify;
449 
450   return g_timeout_add_full (priority,
451                              interval,
452                              _clutter_threads_dispatch, dispatch,
453                              _clutter_threads_dispatch_free);
454 }
455 
456 /**
457  * clutter_threads_add_timeout: (skip)
458  * @interval: the time between calls to the function, in milliseconds
459  * @func: function to call
460  * @data: data to pass to the function
461  *
462  * Simple wrapper around clutter_threads_add_timeout_full().
463  *
464  * Return value: the ID (greater than 0) of the event source.
465  *
466  * Since: 0.4
467  */
468 guint
clutter_threads_add_timeout(guint interval,GSourceFunc func,gpointer data)469 clutter_threads_add_timeout (guint       interval,
470                              GSourceFunc func,
471                              gpointer    data)
472 {
473   g_return_val_if_fail (func != NULL, 0);
474 
475   return clutter_threads_add_timeout_full (G_PRIORITY_DEFAULT,
476                                            interval,
477                                            func, data,
478                                            NULL);
479 }
480 
481 gboolean
_clutter_context_is_initialized(void)482 _clutter_context_is_initialized (void)
483 {
484   if (ClutterCntx == NULL)
485     return FALSE;
486 
487   return ClutterCntx->is_initialized;
488 }
489 
490 ClutterMainContext *
_clutter_context_get_default(void)491 _clutter_context_get_default (void)
492 {
493   if (G_UNLIKELY (ClutterCntx == NULL))
494     {
495       ClutterMainContext *ctx;
496 
497       ClutterCntx = ctx = g_new0 (ClutterMainContext, 1);
498 
499       ctx->is_initialized = FALSE;
500 
501       /* create the windowing system backend */
502       ctx->backend = _clutter_create_backend ();
503 
504       /* create the default settings object, and store a back pointer to
505        * the backend singleton
506        */
507       ctx->settings = clutter_settings_get_default ();
508       _clutter_settings_set_backend (ctx->settings, ctx->backend);
509 
510       ctx->events_queue = g_async_queue_new ();
511 
512       ctx->last_repaint_id = 1;
513     }
514 
515   return ClutterCntx;
516 }
517 
518 static gboolean
clutter_arg_direction_cb(const char * key,const char * value,gpointer user_data)519 clutter_arg_direction_cb (const char *key,
520                           const char *value,
521                           gpointer    user_data)
522 {
523   clutter_text_direction =
524     (strcmp (value, "rtl") == 0) ? CLUTTER_TEXT_DIRECTION_RTL
525                                  : CLUTTER_TEXT_DIRECTION_LTR;
526 
527   return TRUE;
528 }
529 
530 #ifdef CLUTTER_ENABLE_DEBUG
531 static gboolean
clutter_arg_debug_cb(const char * key,const char * value,gpointer user_data)532 clutter_arg_debug_cb (const char *key,
533                       const char *value,
534                       gpointer    user_data)
535 {
536   clutter_debug_flags |=
537     g_parse_debug_string (value,
538                           clutter_debug_keys,
539                           G_N_ELEMENTS (clutter_debug_keys));
540   return TRUE;
541 }
542 
543 static gboolean
clutter_arg_no_debug_cb(const char * key,const char * value,gpointer user_data)544 clutter_arg_no_debug_cb (const char *key,
545                          const char *value,
546                          gpointer    user_data)
547 {
548   clutter_debug_flags &=
549     ~g_parse_debug_string (value,
550                            clutter_debug_keys,
551                            G_N_ELEMENTS (clutter_debug_keys));
552   return TRUE;
553 }
554 #endif /* CLUTTER_ENABLE_DEBUG */
555 
556 GQuark
clutter_init_error_quark(void)557 clutter_init_error_quark (void)
558 {
559   return g_quark_from_static_string ("clutter-init-error-quark");
560 }
561 
562 static ClutterInitError
clutter_init_real(GError ** error)563 clutter_init_real (GError **error)
564 {
565   ClutterMainContext *ctx;
566   ClutterBackend *backend;
567 
568   /* Note, creates backend if not already existing, though parse args will
569    * have likely created it
570    */
571   ctx = _clutter_context_get_default ();
572   backend = ctx->backend;
573 
574   if (!ctx->options_parsed)
575     {
576       if (error)
577         g_set_error (error, CLUTTER_INIT_ERROR,
578                      CLUTTER_INIT_ERROR_INTERNAL,
579                      "When using clutter_get_option_group_without_init() "
580 		     "you must parse options before calling clutter_init()");
581       else
582         g_critical ("When using clutter_get_option_group_without_init() "
583 		    "you must parse options before calling clutter_init()");
584 
585       return CLUTTER_INIT_ERROR_INTERNAL;
586     }
587 
588   /*
589    * Call backend post parse hooks.
590    */
591   if (!_clutter_backend_post_parse (backend, error))
592     return CLUTTER_INIT_ERROR_BACKEND;
593 
594   /* If we are displaying the regions that would get redrawn with clipped
595    * redraws enabled we actually have to disable the clipped redrawing
596    * because otherwise we end up with nasty trails of rectangles everywhere.
597    */
598   if (clutter_paint_debug_flags & CLUTTER_DEBUG_REDRAWS)
599     clutter_paint_debug_flags |= CLUTTER_DEBUG_DISABLE_CLIPPED_REDRAWS;
600 
601   /* The same is true when drawing the outlines of paint volumes... */
602   if (clutter_paint_debug_flags & CLUTTER_DEBUG_PAINT_VOLUMES)
603     {
604       clutter_paint_debug_flags |=
605         CLUTTER_DEBUG_DISABLE_CLIPPED_REDRAWS | CLUTTER_DEBUG_DISABLE_CULLING;
606     }
607 
608   if (clutter_paint_debug_flags & CLUTTER_DEBUG_PAINT_DAMAGE_REGION)
609     g_message ("Enabling damaged region");
610 
611   /* this will take care of initializing Cogl's state and
612    * query the GL machinery for features
613    */
614   if (!_clutter_feature_init (error))
615     return CLUTTER_INIT_ERROR_BACKEND;
616 
617   clutter_text_direction = clutter_get_text_direction ();
618 
619   clutter_is_initialized = TRUE;
620   ctx->is_initialized = TRUE;
621 
622   /* Initialize a11y */
623   if (clutter_enable_accessibility)
624     cally_accessibility_init ();
625 
626   /* Initialize types required for paint nodes */
627   _clutter_paint_node_init_types ();
628 
629   return CLUTTER_INIT_SUCCESS;
630 }
631 
632 static GOptionEntry clutter_args[] = {
633   { "clutter-show-fps", 0, 0, G_OPTION_ARG_NONE, &clutter_show_fps,
634     N_("Show frames per second"), NULL },
635   { "clutter-default-fps", 0, 0, G_OPTION_ARG_INT, &clutter_default_fps,
636     N_("Default frame rate"), "FPS" },
637   { "g-fatal-warnings", 0, 0, G_OPTION_ARG_NONE, &clutter_fatal_warnings,
638     N_("Make all warnings fatal"), NULL },
639   { "clutter-text-direction", 0, 0, G_OPTION_ARG_CALLBACK,
640     clutter_arg_direction_cb,
641     N_("Direction for the text"), "DIRECTION" },
642   { "clutter-disable-mipmapped-text", 0, 0, G_OPTION_ARG_NONE,
643     &clutter_disable_mipmap_text,
644     N_("Disable mipmapping on text"), NULL },
645 #ifdef CLUTTER_ENABLE_DEBUG
646   { "clutter-debug", 0, 0, G_OPTION_ARG_CALLBACK, clutter_arg_debug_cb,
647     N_("Clutter debugging flags to set"), "FLAGS" },
648   { "clutter-no-debug", 0, 0, G_OPTION_ARG_CALLBACK, clutter_arg_no_debug_cb,
649     N_("Clutter debugging flags to unset"), "FLAGS" },
650 #endif /* CLUTTER_ENABLE_DEBUG */
651   { "clutter-enable-accessibility", 0, 0, G_OPTION_ARG_NONE, &clutter_enable_accessibility,
652     N_("Enable accessibility"), NULL },
653   { NULL, },
654 };
655 
656 /* pre_parse_hook: initialise variables depending on environment
657  * variables; these variables might be overridden by the command
658  * line arguments that are going to be parsed after.
659  */
660 static gboolean
pre_parse_hook(GOptionContext * context,GOptionGroup * group,gpointer data,GError ** error)661 pre_parse_hook (GOptionContext  *context,
662                 GOptionGroup    *group,
663                 gpointer         data,
664                 GError         **error)
665 {
666   ClutterMainContext *clutter_context;
667   ClutterBackend *backend;
668   const char *env_string;
669 
670   if (clutter_is_initialized)
671     return TRUE;
672 
673   clutter_context = _clutter_context_get_default ();
674 
675   backend = clutter_context->backend;
676   g_assert (CLUTTER_IS_BACKEND (backend));
677 
678 #ifdef CLUTTER_ENABLE_DEBUG
679   env_string = g_getenv ("CLUTTER_DEBUG");
680   if (env_string != NULL)
681     {
682       clutter_debug_flags =
683         g_parse_debug_string (env_string,
684                               clutter_debug_keys,
685                               G_N_ELEMENTS (clutter_debug_keys));
686       env_string = NULL;
687     }
688 #endif /* CLUTTER_ENABLE_DEBUG */
689 
690   env_string = g_getenv ("CLUTTER_PICK");
691   if (env_string != NULL)
692     {
693       clutter_pick_debug_flags =
694         g_parse_debug_string (env_string,
695                               clutter_pick_debug_keys,
696                               G_N_ELEMENTS (clutter_pick_debug_keys));
697       env_string = NULL;
698     }
699 
700   env_string = g_getenv ("CLUTTER_PAINT");
701   if (env_string != NULL)
702     {
703       clutter_paint_debug_flags =
704         g_parse_debug_string (env_string,
705                               clutter_paint_debug_keys,
706                               G_N_ELEMENTS (clutter_paint_debug_keys));
707       env_string = NULL;
708     }
709 
710   env_string = g_getenv ("CLUTTER_SHOW_FPS");
711   if (env_string)
712     clutter_show_fps = TRUE;
713 
714   env_string = g_getenv ("CLUTTER_DEFAULT_FPS");
715   if (env_string)
716     {
717       gint default_fps = g_ascii_strtoll (env_string, NULL, 10);
718 
719       clutter_default_fps = CLAMP (default_fps, 1, 1000);
720     }
721 
722   env_string = g_getenv ("CLUTTER_DISABLE_MIPMAPPED_TEXT");
723   if (env_string)
724     clutter_disable_mipmap_text = TRUE;
725 
726   return _clutter_backend_pre_parse (backend, error);
727 }
728 
729 /* post_parse_hook: initialise the context and data structures
730  * and opens the X display
731  */
732 static gboolean
post_parse_hook(GOptionContext * context,GOptionGroup * group,gpointer data,GError ** error)733 post_parse_hook (GOptionContext  *context,
734                  GOptionGroup    *group,
735                  gpointer         data,
736                  GError         **error)
737 {
738   ClutterMainContext *clutter_context;
739   ClutterBackend *backend;
740 
741   if (clutter_is_initialized)
742     return TRUE;
743 
744   clutter_context = _clutter_context_get_default ();
745   backend = clutter_context->backend;
746   g_assert (CLUTTER_IS_BACKEND (backend));
747 
748   if (clutter_fatal_warnings)
749     {
750       GLogLevelFlags fatal_mask;
751 
752       fatal_mask = g_log_set_always_fatal (G_LOG_FATAL_MASK);
753       fatal_mask |= G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL;
754       g_log_set_always_fatal (fatal_mask);
755     }
756 
757   clutter_context->frame_rate = clutter_default_fps;
758   clutter_context->show_fps = clutter_show_fps;
759   clutter_context->options_parsed = TRUE;
760 
761   /* If not asked to defer display setup, call clutter_init_real(),
762    * which in turn calls the backend post parse hooks.
763    */
764   if (!clutter_context->defer_display_setup)
765     return clutter_init_real (error) == CLUTTER_INIT_SUCCESS;
766 
767   return TRUE;
768 }
769 
770 /**
771  * clutter_get_option_group: (skip)
772  *
773  * Returns a #GOptionGroup for the command line arguments recognized
774  * by Clutter. You should add this group to your #GOptionContext with
775  * g_option_context_add_group(), if you are using g_option_context_parse()
776  * to parse your commandline arguments.
777  *
778  * Calling g_option_context_parse() with Clutter's #GOptionGroup will result
779  * in Clutter's initialization. That is, the following code:
780  *
781  * |[
782  *   g_option_context_set_main_group (context, clutter_get_option_group ());
783  *   res = g_option_context_parse (context, &argc, &argc, NULL);
784  * ]|
785  *
786  * is functionally equivalent to:
787  *
788  * |[
789  *   clutter_init (&argc, &argv);
790  * ]|
791  *
792  * After g_option_context_parse() on a #GOptionContext containing the
793  * Clutter #GOptionGroup has returned %TRUE, Clutter is guaranteed to be
794  * initialized.
795  *
796  * Return value: (transfer full): a #GOptionGroup for the commandline arguments
797  *   recognized by Clutter
798  *
799  * Since: 0.2
800  */
801 GOptionGroup *
clutter_get_option_group(void)802 clutter_get_option_group (void)
803 {
804   ClutterMainContext *context;
805   GOptionGroup *group;
806 
807   clutter_base_init ();
808 
809   context = _clutter_context_get_default ();
810 
811   group = g_option_group_new ("clutter",
812                               "Clutter Options",
813                               "Show Clutter Options",
814                               NULL,
815                               NULL);
816 
817   g_option_group_set_parse_hooks (group, pre_parse_hook, post_parse_hook);
818   g_option_group_add_entries (group, clutter_args);
819 
820   /* add backend-specific options */
821   _clutter_backend_add_options (context->backend, group);
822 
823   return group;
824 }
825 
826 /**
827  * clutter_get_option_group_without_init: (skip)
828  *
829  * Returns a #GOptionGroup for the command line arguments recognized
830  * by Clutter. You should add this group to your #GOptionContext with
831  * g_option_context_add_group(), if you are using g_option_context_parse()
832  * to parse your commandline arguments.
833  *
834  * Unlike clutter_get_option_group(), calling g_option_context_parse() with
835  * the #GOptionGroup returned by this function requires a subsequent explicit
836  * call to clutter_init(); use this function when needing to set foreign
837  * display connection with clutter_x11_set_display(), or with
838  * `gtk_clutter_init()`.
839  *
840  * Return value: (transfer full): a #GOptionGroup for the commandline arguments
841  *   recognized by Clutter
842  *
843  * Since: 0.8
844  */
845 GOptionGroup *
clutter_get_option_group_without_init(void)846 clutter_get_option_group_without_init (void)
847 {
848   ClutterMainContext *context;
849   GOptionGroup *group;
850 
851   clutter_base_init ();
852 
853   context = _clutter_context_get_default ();
854   context->defer_display_setup = TRUE;
855 
856   group = clutter_get_option_group ();
857 
858   return group;
859 }
860 
861 /* Note that the gobject-introspection annotations for the argc/argv
862  * parameters do not produce the right result; however, they do
863  * allow the common case of argc=NULL, argv=NULL to work.
864  */
865 
866 
867 static gboolean
clutter_parse_args(int * argc,char *** argv,GError ** error)868 clutter_parse_args (int      *argc,
869                     char   ***argv,
870                     GError  **error)
871 {
872   GOptionContext *option_context;
873   GOptionGroup *clutter_group, *cogl_group;
874   GError *internal_error = NULL;
875   gboolean ret = TRUE;
876 
877   if (clutter_is_initialized)
878     return TRUE;
879 
880   option_context = g_option_context_new (NULL);
881   g_option_context_set_ignore_unknown_options (option_context, TRUE);
882   g_option_context_set_help_enabled (option_context, FALSE);
883 
884   /* Initiate any command line options from the backend */
885   clutter_group = clutter_get_option_group ();
886   g_option_context_set_main_group (option_context, clutter_group);
887 
888   cogl_group = cogl_get_option_group ();
889   g_option_context_add_group (option_context, cogl_group);
890 
891   if (!g_option_context_parse (option_context, argc, argv, &internal_error))
892     {
893       g_propagate_error (error, internal_error);
894       ret = FALSE;
895     }
896 
897   g_option_context_free (option_context);
898 
899   return ret;
900 }
901 
902 /**
903  * clutter_init:
904  * @argc: (inout): The number of arguments in @argv
905  * @argv: (array length=argc) (inout) (allow-none): A pointer to an array
906  *   of arguments.
907  *
908  * Initialises everything needed to operate with Clutter and parses some
909  * standard command line options; @argc and @argv are adjusted accordingly
910  * so your own code will never see those standard arguments.
911  *
912  * It is safe to call this function multiple times.
913  *
914  * This function will not abort in case of errors during
915  * initialization; clutter_init() will print out the error message on
916  * stderr, and will return an error code. It is up to the application
917  * code to handle this case.
918  *
919  * If this function fails, and returns an error code, any subsequent
920  * Clutter API will have undefined behaviour - including segmentation
921  * faults and assertion failures. Make sure to handle the returned
922  * #ClutterInitError enumeration value.
923  *
924  * Return value: a #ClutterInitError value
925  */
926 ClutterInitError
clutter_init(int * argc,char *** argv)927 clutter_init (int    *argc,
928               char ***argv)
929 {
930   ClutterMainContext *ctx;
931   GError *error = NULL;
932   ClutterInitError res;
933 
934   if (clutter_is_initialized)
935     return CLUTTER_INIT_SUCCESS;
936 
937   clutter_base_init ();
938 
939   ctx = _clutter_context_get_default ();
940 
941   if (!ctx->defer_display_setup)
942     {
943 #if 0
944       if (argc && *argc > 0 && *argv)
945 	g_set_prgname ((*argv)[0]);
946 #endif
947 
948       /* parse_args will trigger backend creation and things like
949        * DISPLAY connection etc.
950        */
951       if (!clutter_parse_args (argc, argv, &error))
952 	{
953           g_critical ("Unable to initialize Clutter: %s", error->message);
954           g_error_free (error);
955 
956           res = CLUTTER_INIT_ERROR_INTERNAL;
957 	}
958       else
959         res = CLUTTER_INIT_SUCCESS;
960     }
961   else
962     {
963       res = clutter_init_real (&error);
964       if (error != NULL)
965         {
966           g_critical ("Unable to initialize Clutter: %s", error->message);
967           g_error_free (error);
968         }
969     }
970 
971   return res;
972 }
973 
974 gboolean
_clutter_boolean_handled_accumulator(GSignalInvocationHint * ihint,GValue * return_accu,const GValue * handler_return,gpointer dummy)975 _clutter_boolean_handled_accumulator (GSignalInvocationHint *ihint,
976                                       GValue                *return_accu,
977                                       const GValue          *handler_return,
978                                       gpointer               dummy)
979 {
980   gboolean continue_emission;
981   gboolean signal_handled;
982 
983   signal_handled = g_value_get_boolean (handler_return);
984   g_value_set_boolean (return_accu, signal_handled);
985   continue_emission = !signal_handled;
986 
987   return continue_emission;
988 }
989 
990 gboolean
_clutter_boolean_continue_accumulator(GSignalInvocationHint * ihint,GValue * return_accu,const GValue * handler_return,gpointer dummy)991 _clutter_boolean_continue_accumulator (GSignalInvocationHint *ihint,
992                                        GValue                *return_accu,
993                                        const GValue          *handler_return,
994                                        gpointer               dummy)
995 {
996   gboolean continue_emission;
997 
998   continue_emission = g_value_get_boolean (handler_return);
999   g_value_set_boolean (return_accu, continue_emission);
1000 
1001   return continue_emission;
1002 }
1003 
1004 static void
event_click_count_generate(ClutterEvent * event)1005 event_click_count_generate (ClutterEvent *event)
1006 {
1007   /* multiple button click detection */
1008   static gint    click_count            = 0;
1009   static gint    previous_x             = -1;
1010   static gint    previous_y             = -1;
1011   static guint32 previous_time          = 0;
1012   static gint    previous_button_number = -1;
1013 
1014   ClutterInputDevice *device = NULL;
1015   ClutterSettings *settings;
1016   guint double_click_time;
1017   guint double_click_distance;
1018 
1019   settings = clutter_settings_get_default ();
1020 
1021   g_object_get (settings,
1022                 "double-click-distance", &double_click_distance,
1023                 "double-click-time", &double_click_time,
1024                 NULL);
1025 
1026   device = clutter_event_get_device (event);
1027   if (device != NULL)
1028     {
1029       click_count = device->click_count;
1030       previous_x = device->previous_x;
1031       previous_y = device->previous_y;
1032       previous_time = device->previous_time;
1033       previous_button_number = device->previous_button_number;
1034 
1035       CLUTTER_NOTE (EVENT,
1036                     "Restoring previous click count:%d (device:%s, time:%u)",
1037                     click_count,
1038                     clutter_input_device_get_device_name (device),
1039                     previous_time);
1040     }
1041   else
1042     {
1043       CLUTTER_NOTE (EVENT,
1044                     "Restoring previous click count:%d (time:%u)",
1045                     click_count,
1046                     previous_time);
1047     }
1048 
1049   switch (clutter_event_type (event))
1050     {
1051       case CLUTTER_BUTTON_PRESS:
1052         /* check if we are in time and within distance to increment an
1053          * existing click count
1054          */
1055         if (event->button.button == previous_button_number &&
1056             event->button.time < (previous_time + double_click_time) &&
1057             (ABS (event->button.x - previous_x) <= double_click_distance) &&
1058             (ABS (event->button.y - previous_y) <= double_click_distance))
1059           {
1060             CLUTTER_NOTE (EVENT, "Increase click count (button: %d, time: %u)",
1061                           event->button.button,
1062                           event->button.time);
1063 
1064             click_count += 1;
1065           }
1066         else /* start a new click count*/
1067           {
1068             CLUTTER_NOTE (EVENT, "Reset click count (button: %d, time: %u)",
1069                           event->button.button,
1070                           event->button.time);
1071 
1072             click_count = 1;
1073             previous_button_number = event->button.button;
1074           }
1075 
1076         previous_x = event->button.x;
1077         previous_y = event->button.y;
1078         previous_time = event->button.time;
1079 
1080         G_GNUC_FALLTHROUGH;
1081       case CLUTTER_BUTTON_RELEASE:
1082         event->button.click_count = click_count;
1083         break;
1084 
1085       default:
1086         g_assert_not_reached ();
1087         break;
1088     }
1089 
1090   if (event->type == CLUTTER_BUTTON_PRESS && device != NULL)
1091     {
1092       CLUTTER_NOTE (EVENT, "Storing click count: %d (device:%s, time:%u)",
1093                     click_count,
1094                     clutter_input_device_get_device_name (device),
1095                     previous_time);
1096 
1097       device->click_count = click_count;
1098       device->previous_x = previous_x;
1099       device->previous_y = previous_y;
1100       device->previous_time = previous_time;
1101       device->previous_button_number = previous_button_number;
1102     }
1103 }
1104 
1105 static inline void
emit_event_chain(ClutterEvent * event)1106 emit_event_chain (ClutterEvent *event)
1107 {
1108   if (event->any.source == NULL)
1109     {
1110       CLUTTER_NOTE (EVENT, "No source set, discarding event");
1111       return;
1112     }
1113 
1114   _clutter_actor_handle_event (event->any.source, event);
1115 }
1116 
1117 /*
1118  * Emits a pointer event after having prepared the event for delivery (setting
1119  * source, computing click_count, generating enter/leave etc.).
1120  */
1121 
1122 static inline void
emit_pointer_event(ClutterEvent * event,ClutterInputDevice * device)1123 emit_pointer_event (ClutterEvent       *event,
1124                     ClutterInputDevice *device)
1125 {
1126   if (_clutter_event_process_filters (event))
1127     return;
1128 
1129   if (device != NULL && device->pointer_grab_actor != NULL)
1130     clutter_actor_event (device->pointer_grab_actor, event, FALSE);
1131   else
1132     emit_event_chain (event);
1133 }
1134 
1135 static inline void
emit_crossing_event(ClutterEvent * event,ClutterInputDevice * device)1136 emit_crossing_event (ClutterEvent       *event,
1137                      ClutterInputDevice *device)
1138 {
1139   ClutterEventSequence *sequence = clutter_event_get_event_sequence (event);
1140   ClutterActor *grab_actor = NULL;
1141 
1142   if (_clutter_event_process_filters (event))
1143     return;
1144 
1145   if (sequence)
1146     {
1147       if (device->sequence_grab_actors != NULL)
1148         grab_actor = g_hash_table_lookup (device->sequence_grab_actors, sequence);
1149     }
1150   else
1151     {
1152       if (device != NULL && device->pointer_grab_actor != NULL)
1153         grab_actor = device->pointer_grab_actor;
1154     }
1155 
1156   if (grab_actor != NULL)
1157     clutter_actor_event (grab_actor, event, FALSE);
1158   else
1159     emit_event_chain (event);
1160 }
1161 
1162 static inline void
emit_touch_event(ClutterEvent * event,ClutterInputDevice * device)1163 emit_touch_event (ClutterEvent       *event,
1164                   ClutterInputDevice *device)
1165 {
1166   ClutterActor *grab_actor = NULL;
1167 
1168   if (_clutter_event_process_filters (event))
1169     return;
1170 
1171   if (device->sequence_grab_actors != NULL)
1172     {
1173       grab_actor = g_hash_table_lookup (device->sequence_grab_actors,
1174                                         event->touch.sequence);
1175     }
1176 
1177   if (grab_actor != NULL)
1178     {
1179       /* per-device sequence grab */
1180       clutter_actor_event (grab_actor, event, FALSE);
1181     }
1182   else
1183     {
1184       /* no grab, time to capture and bubble */
1185       emit_event_chain (event);
1186     }
1187 }
1188 
1189 static inline void
process_key_event(ClutterEvent * event,ClutterInputDevice * device)1190 process_key_event (ClutterEvent       *event,
1191                    ClutterInputDevice *device)
1192 {
1193   if (_clutter_event_process_filters (event))
1194     return;
1195 
1196   if (device != NULL && device->keyboard_grab_actor != NULL)
1197     clutter_actor_event (device->keyboard_grab_actor, event, FALSE);
1198   else
1199     emit_event_chain (event);
1200 }
1201 
1202 static gboolean
is_off_stage(ClutterActor * stage,gfloat x,gfloat y)1203 is_off_stage (ClutterActor *stage,
1204               gfloat        x,
1205               gfloat        y)
1206 {
1207   gfloat width, height;
1208 
1209   clutter_actor_get_size (stage, &width, &height);
1210 
1211   return (x < 0 ||
1212           y < 0 ||
1213           x >= width ||
1214           y >= height);
1215 }
1216 
1217 /**
1218  * clutter_do_event:
1219  * @event: a #ClutterEvent.
1220  *
1221  * Processes an event.
1222  *
1223  * The @event must be a valid #ClutterEvent and have a #ClutterStage
1224  * associated to it.
1225  *
1226  * This function is only useful when embedding Clutter inside another
1227  * toolkit, and it should never be called by applications.
1228  *
1229  * Since: 0.4
1230  */
1231 void
clutter_do_event(ClutterEvent * event)1232 clutter_do_event (ClutterEvent *event)
1233 {
1234   /* we need the stage for the event */
1235   if (event->any.stage == NULL)
1236     {
1237       g_warning ("%s: Event does not have a stage: discarding.", G_STRFUNC);
1238       return;
1239     }
1240 
1241   /* stages in destruction do not process events */
1242   if (CLUTTER_ACTOR_IN_DESTRUCTION (event->any.stage))
1243     return;
1244 
1245   /* Instead of processing events when received, we queue them up to
1246    * handle per-frame before animations, layout, and drawing.
1247    *
1248    * This gives us the chance to reliably compress motion events
1249    * because we've "looked ahead" and know all motion events that
1250    * will occur before drawing the frame.
1251    */
1252   _clutter_stage_queue_event (event->any.stage, event, TRUE);
1253 }
1254 
1255 static void
create_crossing_event(ClutterStage * stage,ClutterInputDevice * device,ClutterEventSequence * sequence,ClutterEventType event_type,ClutterActor * source,ClutterActor * related,graphene_point_t coords,uint32_t time)1256 create_crossing_event (ClutterStage         *stage,
1257                        ClutterInputDevice   *device,
1258                        ClutterEventSequence *sequence,
1259                        ClutterEventType      event_type,
1260                        ClutterActor         *source,
1261                        ClutterActor         *related,
1262                        graphene_point_t      coords,
1263                        uint32_t              time)
1264 {
1265   ClutterEvent *event;
1266 
1267   event = clutter_event_new (event_type);
1268   event->crossing.time = time;
1269   event->crossing.flags = 0;
1270   event->crossing.stage = stage;
1271   event->crossing.source = source;
1272   event->crossing.x = coords.x;
1273   event->crossing.y = coords.y;
1274   event->crossing.related = related;
1275   event->crossing.sequence = sequence;
1276   clutter_event_set_device (event, device);
1277 
1278   /* we need to make sure that this event is processed
1279    * before any other event we might have queued up until
1280    * now, so we go on, and synthesize the event emission
1281    * ourselves
1282    */
1283   _clutter_process_event (event);
1284 
1285   clutter_event_free (event);
1286 }
1287 
1288 void
clutter_stage_update_device(ClutterStage * stage,ClutterInputDevice * device,ClutterEventSequence * sequence,graphene_point_t point,uint32_t time,ClutterActor * new_actor,gboolean emit_crossing)1289 clutter_stage_update_device (ClutterStage         *stage,
1290                              ClutterInputDevice   *device,
1291                              ClutterEventSequence *sequence,
1292                              graphene_point_t      point,
1293                              uint32_t              time,
1294                              ClutterActor         *new_actor,
1295                              gboolean              emit_crossing)
1296 {
1297   ClutterInputDeviceType device_type;
1298   ClutterActor *old_actor;
1299   gboolean device_actor_changed;
1300 
1301   device_type = clutter_input_device_get_device_type (device);
1302 
1303   g_assert (device_type != CLUTTER_KEYBOARD_DEVICE &&
1304             device_type != CLUTTER_PAD_DEVICE);
1305 
1306   old_actor = clutter_stage_get_device_actor (stage, device, sequence);
1307   device_actor_changed = new_actor != old_actor;
1308 
1309   clutter_stage_update_device_entry (stage,
1310                                      device, sequence,
1311                                      point,
1312                                      new_actor);
1313 
1314   if (device_actor_changed)
1315     {
1316       CLUTTER_NOTE (EVENT,
1317                     "Updating actor under cursor (device %s, at %.2f, %.2f): %s",
1318                     clutter_input_device_get_device_name (device),
1319                     point.x,
1320                     point.y,
1321                     _clutter_actor_get_debug_name (new_actor));
1322 
1323       if (old_actor && emit_crossing)
1324         {
1325           create_crossing_event (stage,
1326                                  device, sequence,
1327                                  CLUTTER_LEAVE,
1328                                  old_actor, new_actor,
1329                                  point, time);
1330         }
1331 
1332       if (new_actor && emit_crossing)
1333         {
1334           create_crossing_event (stage,
1335                                  device, sequence,
1336                                  CLUTTER_ENTER,
1337                                  new_actor, old_actor,
1338                                  point, time);
1339         }
1340     }
1341 }
1342 
1343 void
clutter_stage_repick_device(ClutterStage * stage,ClutterInputDevice * device)1344 clutter_stage_repick_device (ClutterStage       *stage,
1345                              ClutterInputDevice *device)
1346 {
1347   graphene_point_t point;
1348   ClutterActor *new_actor;
1349 
1350   clutter_stage_get_device_coords (stage, device, NULL, &point);
1351   new_actor =
1352     clutter_stage_get_actor_at_pos (stage, CLUTTER_PICK_REACTIVE,
1353                                     point.x, point.y);
1354 
1355   clutter_stage_update_device (stage,
1356                                device, NULL,
1357                                point,
1358                                CLUTTER_CURRENT_TIME,
1359                                new_actor,
1360                                TRUE);
1361 }
1362 
1363 static ClutterActor *
update_device_for_event(ClutterStage * stage,ClutterEvent * event,gboolean emit_crossing)1364 update_device_for_event (ClutterStage *stage,
1365                          ClutterEvent *event,
1366                          gboolean      emit_crossing)
1367 {
1368   ClutterInputDevice *device = clutter_event_get_device (event);
1369   ClutterEventSequence *sequence = clutter_event_get_event_sequence (event);
1370   ClutterActor *new_actor;
1371   graphene_point_t point;
1372   uint32_t time;
1373 
1374   clutter_event_get_coords (event, &point.x, &point.y);
1375   time = clutter_event_get_time (event);
1376 
1377   new_actor =
1378     _clutter_stage_do_pick (stage, point.x, point.y, CLUTTER_PICK_REACTIVE);
1379 
1380   /* Picking should never fail, but if it does, we bail out here */
1381   g_return_val_if_fail (new_actor != NULL, NULL);
1382 
1383   clutter_stage_update_device (stage,
1384                                device, sequence,
1385                                point,
1386                                time,
1387                                new_actor,
1388                                emit_crossing);
1389 
1390   return new_actor;
1391 }
1392 
1393 static void
remove_device_for_event(ClutterStage * stage,ClutterEvent * event,gboolean emit_crossing)1394 remove_device_for_event (ClutterStage *stage,
1395                          ClutterEvent *event,
1396                          gboolean      emit_crossing)
1397 {
1398   ClutterInputDevice *device = clutter_event_get_device (event);
1399   ClutterEventSequence *sequence = clutter_event_get_event_sequence (event);
1400   graphene_point_t point;
1401   uint32_t time;
1402 
1403   clutter_event_get_coords (event, &point.x, &point.y);
1404   time = clutter_event_get_time (event);
1405 
1406   clutter_stage_update_device (stage,
1407                                device, sequence,
1408                                point,
1409                                time,
1410                                NULL,
1411                                TRUE);
1412 
1413   clutter_stage_remove_device_entry (stage, device, sequence);
1414 }
1415 
1416 
1417 static void
_clutter_process_event_details(ClutterActor * stage,ClutterMainContext * context,ClutterEvent * event)1418 _clutter_process_event_details (ClutterActor        *stage,
1419                                 ClutterMainContext  *context,
1420                                 ClutterEvent        *event)
1421 {
1422   ClutterInputDevice *device = clutter_event_get_device (event);
1423   ClutterMainContext *clutter_context;
1424   ClutterBackend *backend;
1425 
1426   clutter_context = _clutter_context_get_default ();
1427   backend = clutter_context->backend;
1428 
1429   switch (event->type)
1430     {
1431       case CLUTTER_NOTHING:
1432         event->any.source = stage;
1433         break;
1434 
1435       case CLUTTER_KEY_PRESS:
1436       case CLUTTER_KEY_RELEASE:
1437       case CLUTTER_PAD_BUTTON_PRESS:
1438       case CLUTTER_PAD_BUTTON_RELEASE:
1439       case CLUTTER_PAD_STRIP:
1440       case CLUTTER_PAD_RING:
1441       case CLUTTER_IM_COMMIT:
1442       case CLUTTER_IM_DELETE:
1443       case CLUTTER_IM_PREEDIT:
1444         {
1445           ClutterActor *actor = NULL;
1446 
1447           /* check that we're not a synthetic event with source set */
1448           if (event->any.source == NULL)
1449             {
1450               actor = clutter_stage_get_key_focus (CLUTTER_STAGE (stage));
1451               event->any.source = actor;
1452               if (G_UNLIKELY (actor == NULL))
1453                 {
1454                   g_warning ("No key focus set, discarding");
1455                   return;
1456                 }
1457             }
1458 
1459           process_key_event (event, device);
1460         }
1461         break;
1462 
1463       case CLUTTER_ENTER:
1464         /* if we're entering from outside the stage we need
1465          * to check whether the pointer is actually on another
1466          * actor, and emit an additional pointer event
1467          */
1468         if (event->any.source == stage &&
1469             event->crossing.related == NULL)
1470           {
1471             ClutterActor *actor = NULL;
1472 
1473             emit_crossing_event (event, device);
1474 
1475             actor = update_device_for_event (CLUTTER_STAGE (stage), event, FALSE);
1476             if (actor != stage)
1477               {
1478                 ClutterEvent *crossing;
1479 
1480                 /* we emit the exact same event on the actor */
1481                 crossing = clutter_event_copy (event);
1482                 crossing->crossing.related = stage;
1483                 crossing->crossing.source = actor;
1484 
1485                 emit_crossing_event (crossing, device);
1486                 clutter_event_free (crossing);
1487               }
1488           }
1489         else
1490           emit_crossing_event (event, device);
1491         break;
1492 
1493       case CLUTTER_LEAVE:
1494         /* same as CLUTTER_ENTER above: when leaving the stage
1495          * we need to also emit a CLUTTER_LEAVE event on the
1496          * actor currently underneath the device, unless it's the
1497          * stage
1498          */
1499         if (event->any.source == stage &&
1500             event->crossing.related == NULL &&
1501             clutter_stage_get_device_actor (CLUTTER_STAGE (stage), device, NULL) != stage)
1502           {
1503             ClutterEvent *crossing;
1504 
1505             crossing = clutter_event_copy (event);
1506             crossing->crossing.related = stage;
1507             crossing->crossing.source =
1508               clutter_stage_get_device_actor (CLUTTER_STAGE (stage), device, NULL);
1509 
1510             emit_crossing_event (crossing, device);
1511             clutter_event_free (crossing);
1512           }
1513         emit_crossing_event (event, device);
1514         break;
1515 
1516       case CLUTTER_MOTION:
1517         if (clutter_backend_is_display_server (backend) &&
1518             !(event->any.flags & CLUTTER_EVENT_FLAG_SYNTHETIC))
1519           {
1520             if (_clutter_is_input_pointer_a11y_enabled (device))
1521               {
1522                 gfloat x, y;
1523 
1524                 clutter_event_get_coords (event, &x, &y);
1525                 _clutter_input_pointer_a11y_on_motion_event (device, x, y);
1526               }
1527           }
1528         /* only the stage gets motion events if they are enabled */
1529         if (!clutter_stage_get_motion_events_enabled (CLUTTER_STAGE (stage)) &&
1530             event->any.source == NULL)
1531           {
1532             /* Only stage gets motion events */
1533             event->any.source = stage;
1534 
1535             if (_clutter_event_process_filters (event))
1536               break;
1537 
1538             if (device != NULL && device->pointer_grab_actor != NULL)
1539               {
1540                 clutter_actor_event (device->pointer_grab_actor,
1541                                      event,
1542                                      FALSE);
1543                 break;
1544               }
1545 
1546             /* Trigger handlers on stage in both capture .. */
1547             if (!clutter_actor_event (stage, event, TRUE))
1548               {
1549                 /* and bubbling phase */
1550                 clutter_actor_event (stage, event, FALSE);
1551               }
1552             break;
1553           }
1554 
1555         G_GNUC_FALLTHROUGH;
1556       case CLUTTER_BUTTON_PRESS:
1557       case CLUTTER_BUTTON_RELEASE:
1558         if (clutter_backend_is_display_server (backend))
1559           {
1560             if (_clutter_is_input_pointer_a11y_enabled (device) && (event->type != CLUTTER_MOTION))
1561               {
1562                 _clutter_input_pointer_a11y_on_button_event (device,
1563                                                              event->button.button,
1564                                                              event->type == CLUTTER_BUTTON_PRESS);
1565               }
1566           }
1567       case CLUTTER_SCROLL:
1568       case CLUTTER_TOUCHPAD_PINCH:
1569       case CLUTTER_TOUCHPAD_SWIPE:
1570         {
1571           gfloat x, y;
1572 
1573           clutter_event_get_coords (event, &x, &y);
1574 
1575           /* Only do a pick to find the source if source is not already set
1576            * (as it could be in a synthetic event)
1577            */
1578           if (event->any.source == NULL)
1579             {
1580               /* emulate X11 the implicit soft grab; the implicit soft grab
1581                * keeps relaying motion events when the stage is left with a
1582                * pointer button pressed. since this is what happens when we
1583                * disable per-actor motion events we need to maintain the same
1584                * behaviour when the per-actor motion events are enabled as
1585                * well
1586                */
1587               if (is_off_stage (stage, x, y))
1588                 {
1589                   if (event->type == CLUTTER_BUTTON_RELEASE)
1590                     {
1591                       CLUTTER_NOTE (EVENT,
1592                                     "Release off stage received at %.2f, %.2f",
1593                                     x, y);
1594 
1595                       event->button.source = stage;
1596                       event->button.click_count = 1;
1597 
1598                       emit_pointer_event (event, device);
1599                     }
1600                   else if (event->type == CLUTTER_MOTION)
1601                     {
1602                       CLUTTER_NOTE (EVENT,
1603                                     "Motion off stage received at %.2f, %2.f",
1604                                     x, y);
1605 
1606                       event->motion.source = stage;
1607 
1608                       emit_pointer_event (event, device);
1609                     }
1610 
1611                   break;
1612                 }
1613 
1614               /* We need to repick on both motion and button press events, the
1615                * latter is only needed for X11 (there the device actor might be
1616                * stale because we don't always receive motion events).
1617                */
1618               if (event->type == CLUTTER_BUTTON_PRESS ||
1619                   event->type == CLUTTER_MOTION)
1620                 {
1621                   event->any.source =
1622                     update_device_for_event (CLUTTER_STAGE (stage), event, TRUE);
1623                 }
1624               else
1625                 {
1626                   event->any.source =
1627                     clutter_stage_get_device_actor (CLUTTER_STAGE (stage),
1628                                                     device,
1629                                                     NULL);
1630                 }
1631 
1632               if (event->any.source == NULL)
1633                 break;
1634             }
1635 
1636           CLUTTER_NOTE (EVENT,
1637                         "Reactive event received at %.2f, %.2f - actor: %p",
1638                         x, y,
1639                         event->any.source);
1640 
1641           /* button presses and releases need a click count */
1642           if (event->type == CLUTTER_BUTTON_PRESS ||
1643               event->type == CLUTTER_BUTTON_RELEASE)
1644             {
1645               /* Generate click count */
1646               event_click_count_generate (event);
1647             }
1648 
1649           emit_pointer_event (event, device);
1650           break;
1651         }
1652 
1653       case CLUTTER_TOUCH_UPDATE:
1654         /* only the stage gets motion events if they are enabled */
1655         if (!clutter_stage_get_motion_events_enabled (CLUTTER_STAGE (stage)) &&
1656             event->any.source == NULL)
1657           {
1658             ClutterActor *grab_actor = NULL;
1659 
1660             /* Only stage gets motion events */
1661             event->any.source = stage;
1662 
1663             if (_clutter_event_process_filters (event))
1664               break;
1665 
1666             /* global grabs */
1667             if (device->sequence_grab_actors != NULL)
1668               {
1669                 grab_actor = g_hash_table_lookup (device->sequence_grab_actors,
1670                                                   event->touch.sequence);
1671               }
1672 
1673             if (grab_actor != NULL)
1674               {
1675                 clutter_actor_event (grab_actor, event, FALSE);
1676                 break;
1677               }
1678 
1679             /* Trigger handlers on stage in both capture .. */
1680             if (!clutter_actor_event (stage, event, TRUE))
1681               {
1682                 /* and bubbling phase */
1683                 clutter_actor_event (stage, event, FALSE);
1684               }
1685             break;
1686           }
1687 
1688         G_GNUC_FALLTHROUGH;
1689       case CLUTTER_TOUCH_BEGIN:
1690       case CLUTTER_TOUCH_CANCEL:
1691       case CLUTTER_TOUCH_END:
1692         {
1693           gfloat x, y;
1694 
1695           clutter_event_get_coords (event, &x, &y);
1696 
1697           /* Only do a pick to find the source if source is not already set
1698            * (as it could be in a synthetic event)
1699            */
1700           if (event->any.source == NULL)
1701             {
1702               /* same as the mouse events above, emulate the X11 implicit
1703                * soft grab */
1704               if (is_off_stage (stage, x, y))
1705                 {
1706                   CLUTTER_NOTE (EVENT,
1707                                 "Touch %s off stage received at %.2f, %.2f",
1708                                 event->type == CLUTTER_TOUCH_UPDATE ? "update" :
1709                                 event->type == CLUTTER_TOUCH_END ? "end" :
1710                                 event->type == CLUTTER_TOUCH_CANCEL ? "cancel" :
1711                                 "?", x, y);
1712 
1713                   event->touch.source = stage;
1714 
1715                   emit_touch_event (event, device);
1716 
1717                   if (event->type == CLUTTER_TOUCH_END ||
1718                       event->type == CLUTTER_TOUCH_CANCEL)
1719                     remove_device_for_event (CLUTTER_STAGE (stage), event, TRUE);
1720 
1721                   break;
1722                 }
1723 
1724               if (event->type == CLUTTER_TOUCH_BEGIN ||
1725                   event->type == CLUTTER_TOUCH_UPDATE)
1726                 {
1727                   event->any.source =
1728                     update_device_for_event (CLUTTER_STAGE (stage), event, TRUE);
1729                 }
1730               else
1731                 {
1732                   event->any.source =
1733                     clutter_stage_get_device_actor (CLUTTER_STAGE (stage),
1734                                                     device,
1735                                                     event->touch.sequence);
1736                 }
1737 
1738               if (event->any.source == NULL)
1739                 break;
1740             }
1741 
1742           CLUTTER_NOTE (EVENT,
1743                         "Reactive event received at %.2f, %.2f - actor: %p",
1744                         x, y,
1745                         event->any.source);
1746 
1747           emit_touch_event (event, device);
1748 
1749           if (event->type == CLUTTER_TOUCH_END ||
1750               event->type == CLUTTER_TOUCH_CANCEL)
1751             remove_device_for_event (CLUTTER_STAGE (stage), event, TRUE);
1752 
1753           break;
1754         }
1755 
1756       case CLUTTER_PROXIMITY_IN:
1757       case CLUTTER_PROXIMITY_OUT:
1758         if (_clutter_event_process_filters (event))
1759           break;
1760 
1761         if (!clutter_actor_event (stage, event, TRUE))
1762           {
1763             /* and bubbling phase */
1764             clutter_actor_event (stage, event, FALSE);
1765           }
1766 
1767         break;
1768 
1769       case CLUTTER_DEVICE_ADDED:
1770         _clutter_event_process_filters (event);
1771         break;
1772 
1773       case CLUTTER_DEVICE_REMOVED:
1774         {
1775           ClutterInputDeviceType device_type;
1776 
1777           _clutter_event_process_filters (event);
1778 
1779           device_type = clutter_input_device_get_device_type (device);
1780           if (device_type == CLUTTER_POINTER_DEVICE ||
1781               device_type == CLUTTER_TABLET_DEVICE ||
1782               device_type == CLUTTER_PEN_DEVICE ||
1783               device_type == CLUTTER_ERASER_DEVICE ||
1784               device_type == CLUTTER_CURSOR_DEVICE)
1785             remove_device_for_event (CLUTTER_STAGE (stage), event, TRUE);
1786 
1787           break;
1788         }
1789 
1790       case CLUTTER_EVENT_LAST:
1791         break;
1792     }
1793 }
1794 
1795 /*
1796  * _clutter_process_event
1797  * @event: a #ClutterEvent.
1798  *
1799  * Does the actual work of processing an event that was queued earlier
1800  * out of clutter_do_event().
1801  */
1802 void
_clutter_process_event(ClutterEvent * event)1803 _clutter_process_event (ClutterEvent *event)
1804 {
1805   ClutterMainContext *context;
1806   ClutterActor *stage;
1807   ClutterSeat *seat;
1808 
1809   context = _clutter_context_get_default ();
1810   seat = clutter_backend_get_default_seat (context->backend);
1811 
1812   stage = CLUTTER_ACTOR (event->any.stage);
1813   if (stage == NULL)
1814     {
1815       CLUTTER_NOTE (EVENT, "Discarding event without a stage set");
1816       return;
1817     }
1818 
1819   /* push events on a stack, so that we don't need to
1820    * add an event parameter to all signals that can be emitted within
1821    * an event chain
1822    */
1823   context->current_event = g_slist_prepend (context->current_event, event);
1824 
1825   clutter_seat_handle_event_post (seat, event);
1826   _clutter_process_event_details (stage, context, event);
1827 
1828   context->current_event = g_slist_delete_link (context->current_event, context->current_event);
1829 }
1830 
1831 void
clutter_base_init(void)1832 clutter_base_init (void)
1833 {
1834   static gboolean initialised = FALSE;
1835 
1836   if (!initialised)
1837     {
1838       initialised = TRUE;
1839 
1840 #if !GLIB_CHECK_VERSION (2, 35, 1)
1841       /* initialise GLib type system */
1842       g_type_init ();
1843 #endif
1844 
1845       clutter_graphene_init ();
1846     }
1847 }
1848 
1849 /**
1850  * clutter_get_default_frame_rate:
1851  *
1852  * Retrieves the default frame rate. See clutter_set_default_frame_rate().
1853  *
1854  * Return value: the default frame rate
1855  *
1856  * Since: 0.6
1857  */
1858 guint
clutter_get_default_frame_rate(void)1859 clutter_get_default_frame_rate (void)
1860 {
1861   ClutterMainContext *context;
1862 
1863   context = _clutter_context_get_default ();
1864 
1865   return context->frame_rate;
1866 }
1867 
1868 /**
1869  * clutter_get_font_map:
1870  *
1871  * Retrieves the #PangoFontMap instance used by Clutter.
1872  * You can use the global font map object with the COGL
1873  * Pango API.
1874  *
1875  * Return value: (transfer none): the #PangoFontMap instance. The returned
1876  *   value is owned by Clutter and it should never be unreferenced.
1877  *
1878  * Since: 1.0
1879  */
1880 PangoFontMap *
clutter_get_font_map(void)1881 clutter_get_font_map (void)
1882 {
1883   return PANGO_FONT_MAP (clutter_context_get_pango_fontmap ());
1884 }
1885 
1886 typedef struct _ClutterRepaintFunction
1887 {
1888   guint id;
1889   ClutterRepaintFlags flags;
1890   GSourceFunc func;
1891   gpointer data;
1892   GDestroyNotify notify;
1893 } ClutterRepaintFunction;
1894 
1895 /**
1896  * clutter_threads_remove_repaint_func:
1897  * @handle_id: an unsigned integer greater than zero
1898  *
1899  * Removes the repaint function with @handle_id as its id
1900  *
1901  * Since: 1.0
1902  */
1903 void
clutter_threads_remove_repaint_func(guint handle_id)1904 clutter_threads_remove_repaint_func (guint handle_id)
1905 {
1906   ClutterRepaintFunction *repaint_func;
1907   ClutterMainContext *context;
1908   GList *l;
1909 
1910   g_return_if_fail (handle_id > 0);
1911 
1912   context = _clutter_context_get_default ();
1913   l = context->repaint_funcs;
1914   while (l != NULL)
1915     {
1916       repaint_func = l->data;
1917 
1918       if (repaint_func->id == handle_id)
1919         {
1920           context->repaint_funcs =
1921             g_list_remove_link (context->repaint_funcs, l);
1922 
1923           g_list_free (l);
1924 
1925           if (repaint_func->notify)
1926             repaint_func->notify (repaint_func->data);
1927 
1928           g_free (repaint_func);
1929 
1930           break;
1931         }
1932 
1933       l = l->next;
1934     }
1935 }
1936 
1937 /**
1938  * clutter_threads_add_repaint_func:
1939  * @func: the function to be called within the paint cycle
1940  * @data: data to be passed to the function, or %NULL
1941  * @notify: function to be called when removing the repaint
1942  *    function, or %NULL
1943  *
1944  * Adds a function to be called whenever Clutter is processing a new
1945  * frame.
1946  *
1947  * If the function returns %FALSE it is automatically removed from the
1948  * list of repaint functions and will not be called again.
1949  *
1950  * This function is guaranteed to be called from within the same thread
1951  * that called clutter_main(), and while the Clutter lock is being held;
1952  * the function will be called within the main loop, so it is imperative
1953  * that it does not block, otherwise the frame time budget may be lost.
1954  *
1955  * A repaint function is useful to ensure that an update of the scenegraph
1956  * is performed before the scenegraph is repainted. By default, a repaint
1957  * function added using this function will be invoked prior to the frame
1958  * being processed.
1959  *
1960  * Adding a repaint function does not automatically ensure that a new
1961  * frame will be queued.
1962  *
1963  * When the repaint function is removed (either because it returned %FALSE
1964  * or because clutter_threads_remove_repaint_func() has been called) the
1965  * @notify function will be called, if any is set.
1966  *
1967  * See also: clutter_threads_add_repaint_func_full()
1968  *
1969  * Return value: the ID (greater than 0) of the repaint function. You
1970  *   can use the returned integer to remove the repaint function by
1971  *   calling clutter_threads_remove_repaint_func().
1972  *
1973  * Since: 1.0
1974  */
1975 guint
clutter_threads_add_repaint_func(GSourceFunc func,gpointer data,GDestroyNotify notify)1976 clutter_threads_add_repaint_func (GSourceFunc    func,
1977                                   gpointer       data,
1978                                   GDestroyNotify notify)
1979 {
1980   return clutter_threads_add_repaint_func_full (CLUTTER_REPAINT_FLAGS_PRE_PAINT,
1981                                                 func,
1982                                                 data, notify);
1983 }
1984 
1985 /**
1986  * clutter_threads_add_repaint_func_full:
1987  * @flags: flags for the repaint function
1988  * @func: the function to be called within the paint cycle
1989  * @data: data to be passed to the function, or %NULL
1990  * @notify: function to be called when removing the repaint
1991  *    function, or %NULL
1992  *
1993  * Adds a function to be called whenever Clutter is processing a new
1994  * frame.
1995  *
1996  * If the function returns %FALSE it is automatically removed from the
1997  * list of repaint functions and will not be called again.
1998  *
1999  * This function is guaranteed to be called from within the same thread
2000  * that called clutter_main(), and while the Clutter lock is being held;
2001  * the function will be called within the main loop, so it is imperative
2002  * that it does not block, otherwise the frame time budget may be lost.
2003  *
2004  * A repaint function is useful to ensure that an update of the scenegraph
2005  * is performed before the scenegraph is repainted. The @flags passed to this
2006  * function will determine the section of the frame processing that will
2007  * result in @func being called.
2008  *
2009  * Adding a repaint function does not automatically ensure that a new
2010  * frame will be queued.
2011  *
2012  * When the repaint function is removed (either because it returned %FALSE
2013  * or because clutter_threads_remove_repaint_func() has been called) the
2014  * @notify function will be called, if any is set.
2015  *
2016  * Return value: the ID (greater than 0) of the repaint function. You
2017  *   can use the returned integer to remove the repaint function by
2018  *   calling clutter_threads_remove_repaint_func().
2019  *
2020  * Since: 1.10
2021  */
2022 guint
clutter_threads_add_repaint_func_full(ClutterRepaintFlags flags,GSourceFunc func,gpointer data,GDestroyNotify notify)2023 clutter_threads_add_repaint_func_full (ClutterRepaintFlags flags,
2024                                        GSourceFunc         func,
2025                                        gpointer            data,
2026                                        GDestroyNotify      notify)
2027 {
2028   ClutterMainContext *context;
2029   ClutterRepaintFunction *repaint_func;
2030 
2031   g_return_val_if_fail (func != NULL, 0);
2032 
2033   context = _clutter_context_get_default ();
2034 
2035   repaint_func = g_new0 (ClutterRepaintFunction, 1);
2036 
2037   repaint_func->id = context->last_repaint_id++;
2038 
2039   repaint_func->flags = flags;
2040   repaint_func->func = func;
2041   repaint_func->data = data;
2042   repaint_func->notify = notify;
2043 
2044   context->repaint_funcs = g_list_prepend (context->repaint_funcs,
2045                                            repaint_func);
2046 
2047   return repaint_func->id;
2048 }
2049 
2050 /*
2051  * _clutter_run_repaint_functions:
2052  * @flags: only run the repaint functions matching the passed flags
2053  *
2054  * Executes the repaint functions added using the
2055  * clutter_threads_add_repaint_func() function.
2056  *
2057  * Must be called with the Clutter thread lock held.
2058  */
2059 void
_clutter_run_repaint_functions(ClutterRepaintFlags flags)2060 _clutter_run_repaint_functions (ClutterRepaintFlags flags)
2061 {
2062   ClutterMainContext *context = _clutter_context_get_default ();
2063   ClutterRepaintFunction *repaint_func;
2064   GList *invoke_list, *reinvoke_list, *l;
2065 
2066   if (context->repaint_funcs == NULL)
2067     return;
2068 
2069   /* steal the list */
2070   invoke_list = context->repaint_funcs;
2071   context->repaint_funcs = NULL;
2072 
2073   reinvoke_list = NULL;
2074 
2075   /* consume the whole list while we execute the functions */
2076   while (invoke_list != NULL)
2077     {
2078       gboolean res = FALSE;
2079 
2080       repaint_func = invoke_list->data;
2081 
2082       l = invoke_list;
2083       invoke_list = g_list_remove_link (invoke_list, invoke_list);
2084 
2085       g_list_free (l);
2086 
2087       if ((repaint_func->flags & flags) != 0)
2088         res = repaint_func->func (repaint_func->data);
2089       else
2090         res = TRUE;
2091 
2092       if (res)
2093         reinvoke_list = g_list_prepend (reinvoke_list, repaint_func);
2094       else
2095         {
2096           if (repaint_func->notify != NULL)
2097             repaint_func->notify (repaint_func->data);
2098 
2099           g_free (repaint_func);
2100         }
2101     }
2102 
2103   if (context->repaint_funcs != NULL)
2104     {
2105       context->repaint_funcs = g_list_concat (context->repaint_funcs,
2106                                               g_list_reverse (reinvoke_list));
2107     }
2108   else
2109     context->repaint_funcs = g_list_reverse (reinvoke_list);
2110 }
2111 
2112 /**
2113  * clutter_get_default_text_direction:
2114  *
2115  * Retrieves the default direction for the text. The text direction is
2116  * determined by the locale and/or by the `CLUTTER_TEXT_DIRECTION`
2117  * environment variable.
2118  *
2119  * The default text direction can be overridden on a per-actor basis by using
2120  * clutter_actor_set_text_direction().
2121  *
2122  * Return value: the default text direction
2123  *
2124  * Since: 1.2
2125  */
2126 ClutterTextDirection
clutter_get_default_text_direction(void)2127 clutter_get_default_text_direction (void)
2128 {
2129   return clutter_text_direction;
2130 }
2131 
2132 /*< private >
2133  * clutter_clear_events_queue:
2134  *
2135  * Clears the events queue stored in the main context.
2136  */
2137 void
_clutter_clear_events_queue(void)2138 _clutter_clear_events_queue (void)
2139 {
2140   ClutterMainContext *context = _clutter_context_get_default ();
2141   ClutterEvent *event;
2142   GAsyncQueue *events_queue;
2143 
2144   if (!context->events_queue)
2145     return;
2146 
2147   g_async_queue_lock (context->events_queue);
2148 
2149   while ((event = g_async_queue_try_pop_unlocked (context->events_queue)))
2150     clutter_event_free (event);
2151 
2152   events_queue = context->events_queue;
2153   context->events_queue = NULL;
2154 
2155   g_async_queue_unlock (events_queue);
2156   g_async_queue_unref (events_queue);
2157 }
2158 
2159 /**
2160  * clutter_add_debug_flags: (skip)
2161  *
2162  * Adds the debug flags passed to the list of debug flags.
2163  */
2164 void
clutter_add_debug_flags(ClutterDebugFlag debug_flags,ClutterDrawDebugFlag draw_flags,ClutterPickDebugFlag pick_flags)2165 clutter_add_debug_flags (ClutterDebugFlag     debug_flags,
2166                          ClutterDrawDebugFlag draw_flags,
2167                          ClutterPickDebugFlag pick_flags)
2168 {
2169   clutter_debug_flags |= debug_flags;
2170   clutter_paint_debug_flags |= draw_flags;
2171   clutter_pick_debug_flags |= pick_flags;
2172 }
2173 
2174 /**
2175  * clutter_remove_debug_flags: (skip)
2176  *
2177  * Removes the debug flags passed from the list of debug flags.
2178  */
2179 void
clutter_remove_debug_flags(ClutterDebugFlag debug_flags,ClutterDrawDebugFlag draw_flags,ClutterPickDebugFlag pick_flags)2180 clutter_remove_debug_flags (ClutterDebugFlag     debug_flags,
2181                             ClutterDrawDebugFlag draw_flags,
2182                             ClutterPickDebugFlag pick_flags)
2183 {
2184   clutter_debug_flags &= ~debug_flags;
2185   clutter_paint_debug_flags &= ~draw_flags;
2186   clutter_pick_debug_flags &= ~pick_flags;
2187 }
2188 
2189 void
clutter_debug_set_max_render_time_constant(int max_render_time_constant_us)2190 clutter_debug_set_max_render_time_constant (int max_render_time_constant_us)
2191 {
2192   clutter_max_render_time_constant_us = max_render_time_constant_us;
2193 }
2194 
2195 void
clutter_get_debug_flags(ClutterDebugFlag * debug_flags,ClutterDrawDebugFlag * draw_flags,ClutterPickDebugFlag * pick_flags)2196 clutter_get_debug_flags (ClutterDebugFlag     *debug_flags,
2197                          ClutterDrawDebugFlag *draw_flags,
2198                          ClutterPickDebugFlag *pick_flags)
2199 {
2200   if (debug_flags)
2201     *debug_flags = clutter_debug_flags;
2202   if (draw_flags)
2203     *draw_flags = clutter_paint_debug_flags;
2204   if (pick_flags)
2205     *pick_flags = clutter_pick_debug_flags;
2206 }
2207 
2208 void
_clutter_set_sync_to_vblank(gboolean sync_to_vblank)2209 _clutter_set_sync_to_vblank (gboolean sync_to_vblank)
2210 {
2211   clutter_sync_to_vblank = !!sync_to_vblank;
2212 }
2213 
2214 void
_clutter_debug_messagev(const char * format,va_list var_args)2215 _clutter_debug_messagev (const char *format,
2216                          va_list     var_args)
2217 {
2218   static gint64 last_debug_stamp;
2219   gchar *stamp, *fmt;
2220   gint64 cur_time, debug_stamp;
2221 
2222   cur_time = g_get_monotonic_time ();
2223 
2224   /* if the last debug message happened less than a second ago, just
2225    * show the increments instead of the full timestamp
2226    */
2227   if (last_debug_stamp == 0 ||
2228       cur_time - last_debug_stamp >= G_USEC_PER_SEC)
2229     {
2230       debug_stamp = cur_time;
2231       last_debug_stamp = debug_stamp;
2232 
2233       stamp = g_strdup_printf ("[%16" G_GINT64_FORMAT "]", debug_stamp);
2234     }
2235   else
2236     {
2237       debug_stamp = cur_time - last_debug_stamp;
2238 
2239       stamp = g_strdup_printf ("[%+16" G_GINT64_FORMAT "]", debug_stamp);
2240     }
2241 
2242   fmt = g_strconcat (stamp, ":", format, NULL);
2243   g_free (stamp);
2244 
2245   g_logv (G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, fmt, var_args);
2246 
2247   g_free (fmt);
2248 }
2249 
2250 void
_clutter_debug_message(const char * format,...)2251 _clutter_debug_message (const char *format, ...)
2252 {
2253   va_list args;
2254 
2255   va_start (args, format);
2256   _clutter_debug_messagev (format, args);
2257   va_end (args);
2258 }
2259 
2260 gboolean
_clutter_diagnostic_enabled(void)2261 _clutter_diagnostic_enabled (void)
2262 {
2263   static const char *clutter_enable_diagnostic = NULL;
2264 
2265   if (G_UNLIKELY (clutter_enable_diagnostic == NULL))
2266     {
2267       clutter_enable_diagnostic = g_getenv ("CLUTTER_ENABLE_DIAGNOSTIC");
2268 
2269       if (clutter_enable_diagnostic == NULL)
2270         clutter_enable_diagnostic = "0";
2271     }
2272 
2273   return *clutter_enable_diagnostic != '0';
2274 }
2275 
2276 void
_clutter_diagnostic_message(const char * format,...)2277 _clutter_diagnostic_message (const char *format, ...)
2278 {
2279   va_list args;
2280   char *fmt;
2281 
2282   fmt = g_strconcat ("[DIAGNOSTIC]: ", format, NULL);
2283 
2284   va_start (args, format);
2285   g_logv (G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, fmt, args);
2286   va_end (args);
2287 
2288   g_free (fmt);
2289 }
2290