From 4befcd0a649a0bfa66eca5c81b6989e98be95696 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 8 Aug 2007 10:20:14 +0000 Subject: [PATCH] Merge clutter.git/threading branch --- ChangeLog | 37 ++++ clutter/clutter-actor.c | 11 +- clutter/clutter-main.c | 381 +++++++++++++++++++++++++++++++--- clutter/clutter-main.h | 43 +++- clutter/clutter-private.h | 12 +- clutter/clutter-timeline.c | 6 +- clutter/clutter-timeout-pool.c | 12 +- clutter/eglnative/clutter-event-egl.c | 28 ++- clutter/eglx/clutter-event-egl.c | 12 ++ clutter/glx/clutter-event-glx.c | 12 ++ clutter/sdl/clutter-event-sdl.c | 24 ++- configure.ac | 2 +- doc/reference/ChangeLog | 4 + doc/reference/clutter-sections.txt | 15 +- tests/Makefile.am | 4 +- tests/test-actors.c | 2 +- 16 files changed, 543 insertions(+), 62 deletions(-) diff --git a/ChangeLog b/ChangeLog index b3ae5b7..c2e1027 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,40 @@ +2007-08-08 Emmanuele Bassi + + Merge the clutter.git/threading branch. + + * clutter/clutter-main.c: + * clutter/clutter-main.h: + * clutter/clutter-private.h: Add threading locking and unlocking + functions, to mark a critical section and access the Clutter API + from differen threads. Add an initialisation function and a function + to override the default lock aquisition and release functions, for + bindings and application-specific locking handling. Add MT-safe + versions of g_idle_add() and g_timeout_add() which will call the + functions under the main Clutter lock and without races. The + Clutter thread-safe implementation is basically the same used by + GDK, so the same caveats apply. + + * clutter/clutter-actor.c: + * clutter/clutter-timeline.c: + * clutter/clutter-timeout-pool.c: Use the new threading API when + invoking idle and timeouts. + + * clutter/eglnative/clutter-event-egl.c: + * clutter/eglx/clutter-event-egl.c: + * clutter/glx/clutter-event-glx.c: + * clutter/sdl/clutter-event-sdl.c: Acquire and release the main + Clutter lock when preparing, checking and dispatching the events + on the queue in every backend. + + * tests/Makefile.am: + * tests/test-threads.c: Add a test case, showing how to use the + threading API and write thread-safe Clutter applications. + +2007-08-08 Emmanuele Bassi + + * configure.ac: Bump up to 0.5.0 and start the new development + branch. + 2007-08-07 Emmanuele Bassi * configure.ac: Bump up to 0.4.0. diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c index b5c8d61..32b2890 100644 --- a/clutter/clutter-actor.c +++ b/clutter/clutter-actor.c @@ -1210,11 +1210,12 @@ clutter_actor_queue_redraw (ClutterActor *self) if (!ctx->update_idle) { - CLUTTER_TIMESTAMP (SCHEDULER, - "Adding ideler for actor: %p", self); - ctx->update_idle = g_idle_add_full (G_PRIORITY_DEFAULT + 10, - redraw_update_idle, - NULL, NULL); + CLUTTER_TIMESTAMP (SCHEDULER, "Adding idle source for actor: %p", self); + + ctx->update_idle = + clutter_threads_add_idle_full (G_PRIORITY_DEFAULT + 10, + redraw_update_idle, + NULL, NULL); } } diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index 64ac463..8894434 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -48,12 +48,21 @@ #include "cogl.h" +/* main context */ static ClutterMainContext *ClutterCntx = NULL; +/* main lock and locking/unlocking functions */ +static GMutex *clutter_threads_mutex = NULL; +static GCallback clutter_threads_lock = NULL; +static GCallback clutter_threads_unlock = NULL; + static gboolean clutter_is_initialized = FALSE; static gboolean clutter_show_fps = FALSE; static gboolean clutter_fatal_warnings = FALSE; +static guint clutter_main_loop_level = 0; +static GSList *main_loops = NULL; + guint clutter_debug_flags = 0; /* global clutter debug flag */ #ifdef CLUTTER_ENABLE_DEBUG @@ -75,9 +84,12 @@ static const GDebugKey clutter_debug_keys[] = { /** * clutter_get_show_fps: * - * FIXME + * Returns whether Clutter should print out the frames per second on the + * console. You can enable this setting either using the + * CLUTTER_SHOW_FPS environment variable or passing + * the --clutter-show-fps command line argument. * * - * Return value: FIXME + * Return value: %TRUE if Clutter should show the FPS. * * Since: 0.4 */ @@ -91,7 +103,8 @@ clutter_get_show_fps (void) /** * clutter_redraw: * - * FIXME + * Forces a redraw of the entire stage. Applications should never use this + * function, but queue a redraw using clutter_actor_queue_redraw(). */ void clutter_redraw (void) @@ -161,8 +174,11 @@ clutter_redraw (void) /** * clutter_do_event + * @event: a #ClutterEvent. + * + * Processes an event. This function should never be called by applications. * - * This function should never be called by applications. + * Since: 0.4 */ void clutter_do_event (ClutterEvent *event) @@ -215,12 +231,9 @@ clutter_do_event (ClutterEvent *event) void clutter_main_quit (void) { - ClutterMainContext *context = CLUTTER_CONTEXT (); + g_return_if_fail (main_loops != NULL); - g_return_if_fail (context->main_loops != NULL); - - if (g_main_loop_is_running (context->main_loops->data)) - g_main_loop_quit (context->main_loops->data); + g_main_loop_quit (main_loops->data); } /** @@ -233,9 +246,7 @@ clutter_main_quit (void) gint clutter_main_level (void) { - ClutterMainContext *context = CLUTTER_CONTEXT (); - - return context->main_loop_level; + return clutter_main_loop_level; } /** @@ -258,24 +269,25 @@ clutter_main (void) CLUTTER_MARK (); - context->main_loop_level++; + clutter_main_loop_level++; loop = g_main_loop_new (NULL, TRUE); - context->main_loops = g_slist_prepend (context->main_loops, loop); + main_loops = g_slist_prepend (main_loops, loop); - if (g_main_loop_is_running (context->main_loops->data)) + if (g_main_loop_is_running (main_loops->data)) { - /* FIXME - add thread locking around this call */ + clutter_threads_leave (); g_main_loop_run (loop); + clutter_threads_enter (); } - context->main_loops = g_slist_remove (context->main_loops, loop); + main_loops = g_slist_remove (main_loops, loop); g_main_loop_unref (loop); - context->main_loop_level--; + clutter_main_loop_level--; - if (context->main_loop_level == 0) + if (clutter_main_loop_level == 0) { /* this will take care of destroying the stage */ g_object_unref (context->backend); @@ -287,6 +299,326 @@ clutter_main (void) CLUTTER_MARK (); } +static void +clutter_threads_impl_lock (void) +{ + if (clutter_threads_mutex) + g_mutex_lock (clutter_threads_mutex); +} + +static void +clutter_threads_impl_unlock (void) +{ + if (clutter_threads_mutex) + g_mutex_unlock (clutter_threads_mutex); +} + +/** + * clutter_threads_init: + * + * Initialises the Clutter threading mechanism, so that Clutter API can be + * called by multiple threads, using clutter_threads_enter() and + * clutter_threads_leave() to mark the critical sections. + * + * You must call g_thread_init() before this function. + * + * This function must be called before clutter_init(). + * + * Since: 0.6 + */ +void +clutter_threads_init (void) +{ + if (!g_thread_supported ()) + g_error ("g_thread_init() must be called before clutter_threads_init()"); + + clutter_threads_mutex = g_mutex_new (); + + if (!clutter_threads_lock) + clutter_threads_lock = clutter_threads_impl_lock; + + if (!clutter_threads_unlock) + clutter_threads_unlock = clutter_threads_impl_unlock; +} + +/** + * clutter_threads_set_lock_functions: + * @enter_fn: function called when aquiring the Clutter main lock + * @leave_fn: function called when releasing the Clutter main lock + * + * Allows the application to replace the standard method that + * Clutter uses to protect its data structures. Normally, Clutter + * creates a single #GMutex that is locked by clutter_threads_enter(), + * and released by clutter_threads_leave(); using this function an + * application provides, instead, a function @enter_fn that is + * called by clutter_threads_enter() and a function @leave_fn that is + * called by clutter_threads_leave(). + * + * The functions must provide at least same locking functionality + * as the default implementation, but can also do extra application + * specific processing. + * + * As an example, consider an application that has its own recursive + * lock that when held, holds the Clutter lock as well. When Clutter + * unlocks the Clutter lock when entering a recursive main loop, the + * application must temporarily release its lock as well. + * + * Most threaded Clutter apps won't need to use this method. + * + * This method must be called before clutter_threads_init(), and cannot + * be called multiple times. + * + * Since: 0.6 + */ +void +clutter_threads_set_lock_functions (GCallback enter_fn, + GCallback leave_fn) +{ + g_return_if_fail (clutter_threads_lock == NULL && + clutter_threads_unlock == NULL); + + clutter_threads_lock = enter_fn; + clutter_threads_unlock = leave_fn; +} + +typedef struct +{ + GSourceFunc func; + gpointer data; + GDestroyNotify notify; +} ClutterThreadsDispatch; + +static gboolean +clutter_threads_dispatch (gpointer data) +{ + ClutterThreadsDispatch *dispatch = data; + gboolean ret = FALSE; + + clutter_threads_enter (); + + if (!g_source_is_destroyed (g_main_current_source ())) + ret = dispatch->func (dispatch->data); + + clutter_threads_leave (); + + return ret; +} + +static void +clutter_threads_dispatch_free (gpointer data) +{ + ClutterThreadsDispatch *dispatch = data; + + clutter_threads_enter (); + + if (dispatch->notify) + dispatch->notify (dispatch->data); + + clutter_threads_leave (); + + g_slice_free (ClutterThreadsDispatch, dispatch); +} + +/** + * clutter_threads_add_idle_full: + * @priority: the priority of the timeout source. Typically this will be in the + * range between #G_PRIORITY_DEFAULT_IDLE and #G_PRIORITY_HIGH_IDLE + * @func: function to call + * @data: data to pass to the function + * @notify: functio to call when the idle source is removed + * + * Adds a function to be called whenever there are no higher priority + * events pending. If the function returns %FALSE it is automatically + * removed from the list of event sources and will not be called again. + * + * This variant of g_idle_add_full() calls @function with the Clutter lock + * held. It can be thought of a MT-safe version for Clutter actors for the + * following use case, where you have to worry about idle_callback() + * running in thread A and accessing @self after it has been finalized + * in thread B: + * + * + * static gboolean + * idle_callback (gpointer data) + * { + * // clutter_threads_enter(); would be needed for g_idle_add() + * + * SomeActor *self = data; + * /* do stuff with self */ + * + * self->idle_id = 0; + * + * // clutter_threads_leave(); would be needed for g_idle_add() + * return FALSE; + * } + * static void + * some_actor_do_stuff_later (SomeActor *self) + * { + * self->idle_id = clutter_threads_add_idle (idle_callback, self) + * // using g_idle_add() here would require thread protection in the callback + * } + * + * static void + * some_actor_finalize (GObject *object) + * { + * SomeActor *self = SOME_ACTOR (object); + * if (self->idle_id) + * g_source_remove (self->idle_id); + * G_OBJECT_CLASS (parent_class)->finalize (object); + * } + * + * + * Return value: the ID (greater than 0) of the event source. + * + * Since: 0.6 + */ +guint +clutter_threads_add_idle_full (gint priority, + GSourceFunc func, + gpointer data, + GDestroyNotify notify) +{ + ClutterThreadsDispatch *dispatch; + + g_return_val_if_fail (func != NULL, 0); + + dispatch = g_slice_new (ClutterThreadsDispatch); + dispatch->func = func; + dispatch->data = data; + dispatch->notify = notify; + + return g_idle_add_full (priority, + clutter_threads_dispatch, dispatch, + clutter_threads_dispatch_free); +} + +/** + * clutter_threads_add_idle: + * @func: function to call + * @data: data to pass to the function + * + * Simple wrapper around clutter_threads_add_idle_full() + * + * Return value: the ID (greater than 0) of the event source. + * + * Since: 0.6 + */ +guint +clutter_threads_add_idle (GSourceFunc func, + gpointer data) +{ + g_return_val_if_fail (func != NULL, 0); + + return clutter_threads_add_idle_full (G_PRIORITY_DEFAULT_IDLE, + func, data, + NULL); +} + +/** + * clutter_threads_add_timeout_full: + * @priority: the priority of the timeout source. Typically this will be in the + * range between #G_PRIORITY_DEFAULT and #G_PRIORITY_HIGH. + * @interval: the time between calls to the function, in milliseconds + * @func: function to call + * @data: data to pass to the function + * @notify: function to call when the timeout source is removed + * + * Sets a function to be called at regular intervals holding the Clutter lock, + * with the given priority. The function is called repeatedly until it + * returns %FALSE, at which point the timeout is automatically destroyed + * and the function will not be called again. The @notify function is + * called when the timeout is destroyed. The first call to the + * function will be at the end of the first @interval. + * + * Note that timeout functions may be delayed, due to the processing of other + * event sources. Thus they should not be relied on for precise timing. + * After each call to the timeout function, the time of the next + * timeout is recalculated based on the current time and the given interval + * (it does not try to 'catch up' time lost in delays). + * + * This variant of g_timeout_add_full() can be thought of a MT-safe version + * for Clutter actors. See also clutter_threads_add_idle_full(). + * + * Return value: the ID (greater than 0) of the event source. + * + * Since: 0.6 + */ +guint +clutter_threads_add_timeout_full (gint priority, + guint interval, + GSourceFunc func, + gpointer data, + GDestroyNotify notify) +{ + ClutterThreadsDispatch *dispatch; + + g_return_val_if_fail (func != NULL, 0); + + dispatch = g_slice_new (ClutterThreadsDispatch); + dispatch->func = func; + dispatch->data = data; + dispatch->notify = notify; + + return g_timeout_add_full (priority, + interval, + clutter_threads_dispatch, dispatch, + clutter_threads_dispatch_free); +} + +/** + * clutter_threads_add_timeout: + * @interval: the time between calls to the function, in milliseconds + * @func: function to call + * @data: data to pass to the function + * + * Simple wrapper around clutter_threads_add_timeout_full(). + * + * Return value: the ID (greater than 0) of the event source. + * + * Since: 0.6 + */ +guint +clutter_threads_add_timeout (guint interval, + GSourceFunc func, + gpointer data) +{ + g_return_val_if_fail (func != NULL, 0); + + return clutter_threads_add_timeout_full (G_PRIORITY_DEFAULT, + interval, + func, data, + NULL); +} + +/** + * clutter_threads_enter: + * + * Locks the Clutter thread lock. + * + * Since: 0.6 + */ +void +clutter_threads_enter (void) +{ + if (clutter_threads_lock) + (* clutter_threads_lock) (); +} + +/** + * clutter_threads_leave: + * + * Unlocks the Clutter thread lock. + * + * Since: 0.6 + */ +void +clutter_threads_leave (void) +{ + if (clutter_threads_unlock) + (* clutter_threads_unlock) (); +} + + /** * clutter_get_debug_enabled: * @@ -410,8 +742,6 @@ pre_parse_hook (GOptionContext *context, return TRUE; clutter_context = clutter_context_get_default (); - clutter_context->main_loops = NULL; - clutter_context->main_loop_level = 0; clutter_context->font_map = PANGO_FT2_FONT_MAP (pango_ft2_font_map_new ()); pango_ft2_font_map_set_resolution (clutter_context->font_map, 96.0, 96.0); @@ -568,11 +898,11 @@ clutter_init_with_args (int *argc, if (clutter_is_initialized) return CLUTTER_INIT_SUCCESS; + clutter_base_init (); + if (argc && *argc > 0 && *argv) g_set_prgname ((*argv)[0]); - clutter_base_init (); - group = clutter_get_option_group (); context = g_option_context_new (parameter_string); @@ -664,10 +994,11 @@ clutter_init (int *argc, if (clutter_is_initialized) return CLUTTER_INIT_SUCCESS; + clutter_base_init (); + if (argc && *argc > 0 && *argv) g_set_prgname ((*argv)[0]); - clutter_base_init (); /* parse_args will trigger backend creation and things like * DISPLAY connection etc. @@ -734,6 +1065,8 @@ clutter_base_init (void) /* initialise GLib type system */ g_type_init (); + + /* CLUTTER_TYPE_ACTOR */ foo = clutter_actor_get_type (); } } diff --git a/clutter/clutter-main.h b/clutter/clutter-main.h index 77f9ef0..42b647d 100644 --- a/clutter/clutter-main.h +++ b/clutter/clutter-main.h @@ -55,6 +55,8 @@ typedef enum { GQuark clutter_init_error_quark (void); +/* Initialisation */ +void clutter_base_init (void); ClutterInitError clutter_init (int *argc, char ***argv); ClutterInitError clutter_init_with_args (int *argc, @@ -63,22 +65,41 @@ ClutterInitError clutter_init_with_args (int *argc, GOptionEntry *entries, char *translation_domain, GError **error); - GOptionGroup * clutter_get_option_group (void); -void clutter_main (void); -void clutter_main_quit (void); -gint clutter_main_level (void); +/* Mainloop */ +void clutter_main (void); +void clutter_main_quit (void); +gint clutter_main_level (void); -void clutter_redraw (void); +void clutter_redraw (void); -gboolean clutter_get_debug_enabled (void); -gboolean clutter_get_show_fps (void); - -void clutter_base_init (void); +/* Debug utility functions */ +gboolean clutter_get_debug_enabled (void); +gboolean clutter_get_show_fps (void); +gulong clutter_get_timestamp (void); -gulong clutter_get_timestamp (void); +/* Threading functions */ +void clutter_threads_init (void); +void clutter_threads_enter (void); +void clutter_threads_leave (void); +void clutter_threads_set_lock_functions (GCallback enter_fn, + GCallback leave_fn); +guint clutter_threads_add_idle (GSourceFunc func, + gpointer data); +guint clutter_threads_add_idle_full (gint priority, + GSourceFunc func, + gpointer data, + GDestroyNotify notify); +guint clutter_threads_add_timeout (guint interval, + GSourceFunc func, + gpointer data); +guint clutter_threads_add_timeout_full (gint priority, + guint interval, + GSourceFunc func, + gpointer data, + GDestroyNotify notify); G_END_DECLS -#endif +#endif /* _HAVE_CLUTTER_MAIN_H */ diff --git a/clutter/clutter-private.h b/clutter/clutter-private.h index 1591604..fd610f5 100644 --- a/clutter/clutter-private.h +++ b/clutter/clutter-private.h @@ -47,15 +47,19 @@ typedef struct _ClutterMainContext ClutterMainContext; struct _ClutterMainContext { - /* holds a pointer to the stage */ + /* holds a pointer to the backend, which controls the stage */ ClutterBackend *backend; + + /* the main event queue */ GQueue *events_queue; + PangoFT2FontMap *font_map; + guint update_idle; - guint main_loop_level; - GSList *main_loops; + guint is_initialized : 1; - guint pick_mode :1; /* Indicates pick render mode */ + guint pick_mode : 1; /* Indicates pick render mode */ + GTimer *timer; /* Used for debugging scheduler */ }; diff --git a/clutter/clutter-timeline.c b/clutter/clutter-timeline.c index 7fc55a9..893f2d6 100644 --- a/clutter/clutter-timeline.c +++ b/clutter/clutter-timeline.c @@ -133,9 +133,9 @@ timeout_add (guint interval, } else { - res = g_timeout_add_full (CLUTTER_TIMELINE_PRIORITY, - interval, - func, data, notify); + res = clutter_threads_add_timeout_full (CLUTTER_TIMELINE_PRIORITY, + interval, + func, data, notify); } return res; diff --git a/clutter/clutter-timeout-pool.c b/clutter/clutter-timeout-pool.c index c3b2dbe..ce457c6 100644 --- a/clutter/clutter-timeout-pool.c +++ b/clutter/clutter-timeout-pool.c @@ -183,12 +183,16 @@ static gboolean clutter_timeout_dispatch (GSource *source, ClutterTimeout *timeout) { + gboolean retval = FALSE; + if (G_UNLIKELY (!timeout->func)) { g_warning ("Timeout dispatched without a callback."); return FALSE; } + clutter_threads_enter (); + if (timeout->func (timeout->data)) { GTimeVal current_time; @@ -196,10 +200,12 @@ clutter_timeout_dispatch (GSource *source, g_source_get_current_time (source, ¤t_time); clutter_timeout_set_expiration (timeout, ¤t_time); - return TRUE; + retval = TRUE; } - else - return FALSE; + + clutter_threads_leave (); + + return retval; } static ClutterTimeout * diff --git a/clutter/eglnative/clutter-event-egl.c b/clutter/eglnative/clutter-event-egl.c index 5cdea35..b1d7d58 100644 --- a/clutter/eglnative/clutter-event-egl.c +++ b/clutter/eglnative/clutter-event-egl.c @@ -150,10 +150,16 @@ clutter_event_prepare (GSource *source, gint *timeout) { ClutterBackend *backend = ((ClutterEventSource *) source)->backend; + gboolean retval; + + clutter_threads_enter (); *timeout = -1; + retval = clutter_events_pending (); + + clutter_threads_leave (); - return clutter_events_pending (); + return retval; } static gboolean @@ -161,9 +167,16 @@ clutter_event_check (GSource *source) { ClutterEventSource *event_source = (ClutterEventSource *) source; ClutterBackend *backend = event_source->backend; + gboolean retval; + + clutter_threads_enter (); + + retval = ((event_source->event_poll_fd.revents & G_IO_IN) || + clutter_events_pending ()); - return ((event_source->event_poll_fd.revents & G_IO_IN) - || clutter_events_pending ()); + clutter_threads_leave (); + + return retval; } static gboolean @@ -178,6 +191,8 @@ clutter_event_dispatch (GSource *source, ClutterMainContext *clutter_context; static gint last_x, last_y; + clutter_threads_enter (); + clutter_context = clutter_context_get_default (); #ifdef HAVE_TSLIB /* FIXME while would be better here but need to deal with lockups */ @@ -189,8 +204,8 @@ clutter_event_dispatch (GSource *source, * event_button_generate gets confused generating lots of double * and triple clicks. */ - if (tsevent.pressure && last_x == tsevent.x && last_y == tsevent.y) - return; + if (tsevent.pressure && last_x == tsevent.x && last_y == tsevent.y) + goto out; event = clutter_event_new (CLUTTER_NOTHING); @@ -223,5 +238,8 @@ clutter_event_dispatch (GSource *source, clutter_event_free (event); } +out: + clutter_threads_leave (); + return TRUE; } diff --git a/clutter/eglx/clutter-event-egl.c b/clutter/eglx/clutter-event-egl.c index a1ae1f1..966771e 100644 --- a/clutter/eglx/clutter-event-egl.c +++ b/clutter/eglx/clutter-event-egl.c @@ -326,9 +326,13 @@ clutter_event_prepare (GSource *source, ClutterBackend *backend = ((ClutterEventSource *) source)->backend; gboolean retval; + clutter_threads_enter (); + *timeout = -1; retval = (clutter_events_pending () || clutter_check_xpending (backend)); + clutter_threads_leave (); + return retval; } @@ -339,10 +343,14 @@ clutter_event_check (GSource *source) ClutterBackend *backend = event_source->backend; gboolean retval; + clutter_threads_enter (); + if (event_source->event_poll_fd.revents & G_IO_IN) retval = (clutter_events_pending () || clutter_check_xpending (backend)); else retval = FALSE; + + clutter_threads_leave (); return retval; } @@ -355,6 +363,8 @@ clutter_event_dispatch (GSource *source, ClutterBackend *backend = ((ClutterEventSource *) source)->backend; ClutterEvent *event; + clutter_thread_enter (); + events_queue (backend); event = clutter_event_get (); @@ -365,5 +375,7 @@ clutter_event_dispatch (GSource *source, clutter_event_free (event); } + clutter_threads_leave (); + return TRUE; } diff --git a/clutter/glx/clutter-event-glx.c b/clutter/glx/clutter-event-glx.c index 0af0600..8636601 100644 --- a/clutter/glx/clutter-event-glx.c +++ b/clutter/glx/clutter-event-glx.c @@ -549,9 +549,13 @@ clutter_event_prepare (GSource *source, ClutterBackend *backend = ((ClutterEventSource *) source)->backend; gboolean retval; + clutter_threads_enter (); + *timeout = -1; retval = (clutter_events_pending () || check_xpending (backend)); + clutter_threads_leave (); + return retval; } @@ -562,11 +566,15 @@ clutter_event_check (GSource *source) ClutterBackend *backend = event_source->backend; gboolean retval; + clutter_threads_enter (); + if (event_source->event_poll_fd.revents & G_IO_IN) retval = (clutter_events_pending () || check_xpending (backend)); else retval = FALSE; + clutter_threads_leave (); + return retval; } @@ -578,6 +586,8 @@ clutter_event_dispatch (GSource *source, ClutterBackend *backend = ((ClutterEventSource *) source)->backend; ClutterEvent *event; + clutter_threads_enter (); + /* Grab the event(s), translate and figure out double click. * The push onto queue (stack) if valid. */ @@ -593,5 +603,7 @@ clutter_event_dispatch (GSource *source, clutter_event_free (event); } + clutter_threads_leave (); + return TRUE; } diff --git a/clutter/sdl/clutter-event-sdl.c b/clutter/sdl/clutter-event-sdl.c index 3ebcd69..f9c05e6 100644 --- a/clutter/sdl/clutter-event-sdl.c +++ b/clutter/sdl/clutter-event-sdl.c @@ -112,11 +112,16 @@ clutter_event_prepare (GSource *source, { SDL_Event events; int num_events; + gboolean retval; + + clutter_threads_enter (); num_events = SDL_PeepEvents(&events, 1, SDL_PEEKEVENT, SDL_ALLEVENTS); if (num_events == 1) { + clutter_threads_leave (); + *timeout = 0; return TRUE; } @@ -126,7 +131,11 @@ clutter_event_prepare (GSource *source, *timeout = 50; - return clutter_events_pending (); + retval = clutter_events_pending (); + + clutter_threads_leave (); + + return retval; } static gboolean @@ -134,6 +143,9 @@ clutter_event_check (GSource *source) { SDL_Event events; int num_events; + gboolean retval; + + clutter_threads_enter (); /* Pump SDL */ SDL_PumpEvents(); @@ -143,7 +155,11 @@ clutter_event_check (GSource *source) if (num_events == -1) g_warning("Error polling SDL: %s", SDL_GetError()); - return (num_events == 1 || clutter_events_pending ()); + retval = (num_events == 1 || clutter_events_pending ()); + + clutter_threads_leave (); + + return retval; } static void @@ -292,6 +308,8 @@ clutter_event_dispatch (GSource *source, ClutterBackend *backend = ((ClutterEventSource *) source)->backend; ClutterMainContext *clutter_context; + clutter_threads_enter (); + clutter_context = clutter_context_get_default (); while (SDL_PollEvent(&sdl_event)) @@ -328,5 +346,7 @@ clutter_event_dispatch (GSource *source, clutter_event_free (event); } + clutter_threads_leave (); + return TRUE; } diff --git a/configure.ac b/configure.ac index 18cc38f..aed891e 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ # An odd micro number indicates in-progress development, (eg. from CVS) # An even micro number indicates a released version. m4_define([clutter_major_version], [0]) -m4_define([clutter_minor_version], [4]) +m4_define([clutter_minor_version], [5]) m4_define([clutter_micro_version], [0]) m4_define([clutter_version], diff --git a/doc/reference/ChangeLog b/doc/reference/ChangeLog index 0fac3f0..0c9f103 100644 --- a/doc/reference/ChangeLog +++ b/doc/reference/ChangeLog @@ -1,3 +1,7 @@ +2007-08-08 Emmanuele Bassi + + * clutter-sections.txt: Add the new clutter_threads_* API. + 2007-08-07 Emmanuele Bassi * clutter-sections.txt: Shuffle around a bit the symbols. diff --git a/doc/reference/clutter-sections.txt b/doc/reference/clutter-sections.txt index 2d0c6ff..6f25aae 100644 --- a/doc/reference/clutter-sections.txt +++ b/doc/reference/clutter-sections.txt @@ -828,12 +828,23 @@ ClutterInitError clutter_init clutter_init_with_args clutter_get_option_group -clutter_get_debug_enabled -clutter_get_show_fps + clutter_main clutter_main_quit clutter_main_level + +clutter_get_debug_enabled +clutter_get_show_fps clutter_get_timestamp + +clutter_threads_set_lock_functions +clutter_threads_init +clutter_threads_enter +clutter_threads_leave +clutter_threads_add_idle +clutter_threads_add_idle_full +clutter_threads_add_timeout +clutter_threads_add_timeout_full CLUTTER_INIT_ERROR diff --git a/tests/Makefile.am b/tests/Makefile.am index 06d5e9a..3855f4d 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,6 +1,7 @@ noinst_PROGRAMS = test-textures test-events test-offscreen test-scale \ test-actors test-behave test-text test-entry test-project \ - test-boxes test-perspective test-rotate test-depth + test-boxes test-perspective test-rotate test-depth \ + test-threads INCLUDES = -I$(top_srcdir)/ LDADD = $(top_builddir)/clutter/libclutter-@CLUTTER_FLAVOUR@-@CLUTTER_MAJORMINOR@.la @@ -20,5 +21,6 @@ test_boxes_SOURCES = test-boxes.c test_perspective_SOURCES = test-perspective.c test_rotate_SOURCES = test-rotate.c test_depth_SOURCES = test-depth.c +test_threads_SOURCES = test-threads.c EXTRA_DIST = redhand.png diff --git a/tests/test-actors.c b/tests/test-actors.c index 0db771d..2037090 100644 --- a/tests/test-actors.c +++ b/tests/test-actors.c @@ -234,7 +234,7 @@ main (int argc, char *argv[]) /* and start it */ clutter_timeline_start (timeline); - clutter_main(); + clutter_main (); g_free (oh->hand); g_free (oh); -- 2.7.4