4 * An OpenGL based 'interactive canvas' library.
6 * Copyright (C) 2008 Intel Corporation.
8 * Authored By: Emmanuele Bassi <ebassi@linux.intel.com>
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.
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.
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/>.
25 * SECTION:clutter-binding-pool
26 * @short_description: Pool for key bindings
28 * #ClutterBindingPool is a data structure holding a set of key bindings.
29 * Each key binding associates a key symbol (eventually with modifiers)
30 * to an action. A callback function is associated to each action.
32 * For a given key symbol and modifier mask combination there can be only one
33 * action; for each action there can be only one callback. There can be
34 * multiple actions with the same name, and the same callback can be used
35 * to handle multiple key bindings.
37 * Actors requiring key bindings should create a new #ClutterBindingPool
38 * inside their class initialization function and then install actions
43 * foo_class_init (FooClass *klass)
45 * ClutterBindingPool *binding_pool;
47 * binding_pool = clutter_binding_pool_get_for_class (klass);
49 * clutter_binding_pool_install_action (binding_pool, "move-up",
51 * G_CALLBACK (foo_action_move_up),
53 * clutter_binding_pool_install_action (binding_pool, "move-up",
55 * G_CALLBACK (foo_action_move_up),
60 * The callback has a signature of:
63 * gboolean (* callback) (GObject *instance,
64 * const gchar *action_name,
66 * ClutterModifierType modifiers,
67 * gpointer user_data);
70 * The actor should then override the #ClutterActor::key-press-event and
71 * use clutter_binding_pool_activate() to match a #ClutterKeyEvent structure
72 * to one of the actions:
75 * ClutterBindingPool *pool;
77 * /* retrieve the binding pool for the type of the actor */
78 * pool = clutter_binding_pool_find (G_OBJECT_TYPE_NAME (actor));
80 * /* activate any callback matching the key symbol and modifiers
81 * * mask of the key event. the returned value can be directly
82 * * used to signal that the actor has handled the event.
84 * return clutter_binding_pool_activate (pool,
85 * key_event->keyval,
86 * key_event->modifier_state,
90 * The clutter_binding_pool_activate() function will return %FALSE if
91 * no action for the given key binding was found, if the action was
92 * blocked (using clutter_binding_pool_block_action()) or if the
93 * key binding handler returned %FALSE.
95 * #ClutterBindingPool is available since Clutter 1.0
102 #include "clutter-binding-pool.h"
103 #include "clutter-debug.h"
104 #include "clutter-enum-types.h"
105 #include "clutter-marshal.h"
106 #include "clutter-private.h"
108 #define CLUTTER_BINDING_POOL_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), CLUTTER_TYPE_BINDING_POOL, ClutterBindingPoolClass))
109 #define CLUTTER_IS_BINDING_POOL_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), CLUTTER_TYPE_BINDING_POOL))
110 #define CLUTTER_BINDING_POOL_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), CLUTTER_TYPE_BINDING_POOL, ClutterBindingPoolClass))
112 #define BINDING_MOD_MASK ((CLUTTER_SHIFT_MASK | \
113 CLUTTER_CONTROL_MASK | \
114 CLUTTER_MOD1_MASK | \
115 CLUTTER_SUPER_MASK | \
116 CLUTTER_HYPER_MASK | \
117 CLUTTER_META_MASK) | CLUTTER_RELEASE_MASK)
119 typedef struct _ClutterBindingEntry ClutterBindingEntry;
121 static GSList *clutter_binding_pools = NULL;
122 static GQuark key_class_bindings = 0;
124 struct _ClutterBindingPool
126 GObject parent_instance;
128 gchar *name; /* interned string, do not free */
131 GHashTable *entries_hash;
134 struct _ClutterBindingPoolClass
136 GObjectClass parent_class;
139 struct _ClutterBindingEntry
141 gchar *name; /* interned string, do not free */
144 ClutterModifierType modifiers;
148 guint is_blocked : 1;
160 static GParamSpec *obj_props[PROP_LAST];
162 G_DEFINE_TYPE (ClutterBindingPool, clutter_binding_pool, G_TYPE_OBJECT);
165 binding_entry_hash (gconstpointer v)
167 const ClutterBindingEntry *e = v;
177 binding_entry_compare (gconstpointer v1,
180 const ClutterBindingEntry *e1 = v1;
181 const ClutterBindingEntry *e2 = v2;
183 return (e1->key_val == e2->key_val && e1->modifiers == e2->modifiers);
186 static ClutterBindingEntry *
187 binding_entry_new (const gchar *name,
189 ClutterModifierType modifiers)
191 ClutterBindingEntry *entry;
193 modifiers = modifiers & BINDING_MOD_MASK;
195 entry = g_slice_new (ClutterBindingEntry);
196 entry->key_val = key_val;
197 entry->modifiers = modifiers;
198 entry->name = (gchar *) g_intern_string (name);
199 entry->closure = NULL;
200 entry->is_blocked = FALSE;
205 static ClutterBindingEntry *
206 binding_pool_lookup_entry (ClutterBindingPool *pool,
208 ClutterModifierType modifiers)
210 ClutterBindingEntry lookup_entry = { 0, };
212 lookup_entry.key_val = key_val;
213 lookup_entry.modifiers = modifiers;
215 return g_hash_table_lookup (pool->entries_hash, &lookup_entry);
219 binding_entry_free (gpointer data)
223 ClutterBindingEntry *entry = data;
225 g_closure_unref (entry->closure);
227 g_slice_free (ClutterBindingEntry, entry);
232 clutter_binding_pool_finalize (GObject *gobject)
234 ClutterBindingPool *pool = CLUTTER_BINDING_POOL (gobject);
236 /* remove from the pools */
237 clutter_binding_pools = g_slist_remove (clutter_binding_pools, pool);
239 g_hash_table_destroy (pool->entries_hash);
241 g_slist_foreach (pool->entries, (GFunc) binding_entry_free, NULL);
242 g_slist_free (pool->entries);
244 G_OBJECT_CLASS (clutter_binding_pool_parent_class)->finalize (gobject);
248 clutter_binding_pool_set_property (GObject *gobject,
253 ClutterBindingPool *pool = CLUTTER_BINDING_POOL (gobject);
258 pool->name = (gchar *) g_intern_string (g_value_get_string (value));
262 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
268 clutter_binding_pool_get_property (GObject *gobject,
273 ClutterBindingPool *pool = CLUTTER_BINDING_POOL (gobject);
278 g_value_set_string (value, pool->name);
282 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
288 clutter_binding_pool_constructed (GObject *gobject)
290 ClutterBindingPool *pool = CLUTTER_BINDING_POOL (gobject);
292 /* bad monkey! bad, bad monkey! */
293 if (G_UNLIKELY (pool->name == NULL))
294 g_critical ("No name set for ClutterBindingPool %p", pool);
296 if (G_OBJECT_CLASS (clutter_binding_pool_parent_class)->constructed)
297 G_OBJECT_CLASS (clutter_binding_pool_parent_class)->constructed (gobject);
301 clutter_binding_pool_class_init (ClutterBindingPoolClass *klass)
303 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
305 gobject_class->constructed = clutter_binding_pool_constructed;
306 gobject_class->set_property = clutter_binding_pool_set_property;
307 gobject_class->get_property = clutter_binding_pool_get_property;
308 gobject_class->finalize = clutter_binding_pool_finalize;
311 * ClutterBindingPool:name:
313 * The unique name of the #ClutterBindingPool.
317 obj_props[PROP_NAME] =
318 g_param_spec_string ("name",
320 P_("The unique name of the binding pool"),
322 CLUTTER_PARAM_READWRITE |
323 G_PARAM_CONSTRUCT_ONLY);
325 g_object_class_install_properties (gobject_class,
331 clutter_binding_pool_init (ClutterBindingPool *pool)
334 pool->entries = NULL;
335 pool->entries_hash = g_hash_table_new (binding_entry_hash,
336 binding_entry_compare);
338 clutter_binding_pools = g_slist_prepend (clutter_binding_pools, pool);
342 * clutter_binding_pool_new:
343 * @name: the name of the binding pool
345 * Creates a new #ClutterBindingPool that can be used to store
346 * key bindings for an actor. The @name must be a unique identifier
347 * for the binding pool, so that clutter_binding_pool_find() will
348 * be able to return the correct binding pool.
350 * Return value: the newly created binding pool with the given
351 * name. Use g_object_unref() when done.
356 clutter_binding_pool_new (const gchar *name)
358 ClutterBindingPool *pool;
360 g_return_val_if_fail (name != NULL, NULL);
362 pool = clutter_binding_pool_find (name);
363 if (G_UNLIKELY (pool))
365 g_warning ("A binding pool named '%s' is already present "
366 "in the binding pools list",
371 return g_object_new (CLUTTER_TYPE_BINDING_POOL, "name", name, NULL);
375 * clutter_binding_pool_get_for_class:
376 * @klass: a #GObjectClass pointer
378 * Retrieves the #ClutterBindingPool for the given #GObject class
379 * and, eventually, creates it. This function is a wrapper around
380 * clutter_binding_pool_new() and uses the class type name as the
381 * unique name for the binding pool.
383 * Calling this function multiple times will return the same
384 * #ClutterBindingPool.
386 * A binding pool for a class can also be retrieved using
387 * clutter_binding_pool_find() with the class type name:
390 * pool = clutter_binding_pool_find (G_OBJECT_TYPE_NAME (instance));
393 * Return value: (transfer none): the binding pool for the given class.
394 * The returned #ClutterBindingPool is owned by Clutter and should not
400 clutter_binding_pool_get_for_class (gpointer klass)
402 ClutterBindingPool *pool;
404 g_return_val_if_fail (G_IS_OBJECT_CLASS (klass), NULL);
406 if (G_UNLIKELY (key_class_bindings == 0))
407 key_class_bindings = g_quark_from_static_string ("clutter-bindings-set");
409 pool = g_dataset_id_get_data (klass, key_class_bindings);
413 pool = clutter_binding_pool_new (G_OBJECT_CLASS_NAME (klass));
414 g_dataset_id_set_data_full (klass, key_class_bindings,
422 * clutter_binding_pool_find:
423 * @name: the name of the binding pool to find
425 * Finds the #ClutterBindingPool with @name.
427 * Return value: (transfer none): a pointer to the #ClutterBindingPool, or %NULL
432 clutter_binding_pool_find (const gchar *name)
436 g_return_val_if_fail (name != NULL, NULL);
438 for (l = clutter_binding_pools; l != NULL; l = l->next)
440 ClutterBindingPool *pool = l->data;
442 if (g_str_equal (pool->name, (gpointer) name))
450 * clutter_binding_pool_install_action:
451 * @pool: a #ClutterBindingPool
452 * @action_name: the name of the action
453 * @key_val: key symbol
454 * @modifiers: bitmask of modifiers
455 * @callback: function to be called when the action is activated
456 * @data: data to be passed to @callback
457 * @notify: function to be called when the action is removed
460 * Installs a new action inside a #ClutterBindingPool. The action
461 * is bound to @key_val and @modifiers.
463 * The same action name can be used for multiple @key_val, @modifiers
466 * When an action has been activated using clutter_binding_pool_activate()
467 * the passed @callback will be invoked (with @data).
469 * Actions can be blocked with clutter_binding_pool_block_action()
470 * and then unblocked using clutter_binding_pool_unblock_action().
475 clutter_binding_pool_install_action (ClutterBindingPool *pool,
476 const gchar *action_name,
478 ClutterModifierType modifiers,
481 GDestroyNotify notify)
483 ClutterBindingEntry *entry;
486 g_return_if_fail (pool != NULL);
487 g_return_if_fail (action_name != NULL);
488 g_return_if_fail (key_val != 0);
489 g_return_if_fail (callback != NULL);
491 entry = binding_pool_lookup_entry (pool, key_val, modifiers);
492 if (G_UNLIKELY (entry))
494 g_warning ("There already is an action '%s' for the given "
495 "key symbol of %d (modifiers: %d) installed inside "
498 entry->key_val, entry->modifiers);
502 entry = binding_entry_new (action_name, key_val, modifiers);
504 closure = g_cclosure_new (callback, data, (GClosureNotify) notify);
505 entry->closure = g_closure_ref (closure);
506 g_closure_sink (closure);
508 if (G_CLOSURE_NEEDS_MARSHAL (closure))
510 GClosureMarshal marshal;
512 marshal = _clutter_marshal_BOOLEAN__STRING_UINT_FLAGS;
513 g_closure_set_marshal (closure, marshal);
516 pool->entries = g_slist_prepend (pool->entries, entry);
517 g_hash_table_insert (pool->entries_hash, entry, entry);
521 * clutter_binding_pool_install_closure:
522 * @pool: a #ClutterBindingPool
523 * @action_name: the name of the action
524 * @key_val: key symbol
525 * @modifiers: bitmask of modifiers
526 * @closure: a #GClosure
528 * A #GClosure variant of clutter_binding_pool_install_action().
530 * Installs a new action inside a #ClutterBindingPool. The action
531 * is bound to @key_val and @modifiers.
533 * The same action name can be used for multiple @key_val, @modifiers
536 * When an action has been activated using clutter_binding_pool_activate()
537 * the passed @closure will be invoked.
539 * Actions can be blocked with clutter_binding_pool_block_action()
540 * and then unblocked using clutter_binding_pool_unblock_action().
545 clutter_binding_pool_install_closure (ClutterBindingPool *pool,
546 const gchar *action_name,
548 ClutterModifierType modifiers,
551 ClutterBindingEntry *entry;
553 g_return_if_fail (pool != NULL);
554 g_return_if_fail (action_name != NULL);
555 g_return_if_fail (key_val != 0);
556 g_return_if_fail (closure != NULL);
558 entry = binding_pool_lookup_entry (pool, key_val, modifiers);
559 if (G_UNLIKELY (entry))
561 g_warning ("There already is an action '%s' for the given "
562 "key symbol of %d (modifiers: %d) installed inside "
565 entry->key_val, entry->modifiers);
569 entry = binding_entry_new (action_name, key_val, modifiers);
571 entry->closure = g_closure_ref (closure);
572 g_closure_sink (closure);
574 if (G_CLOSURE_NEEDS_MARSHAL (closure))
576 GClosureMarshal marshal;
578 marshal = _clutter_marshal_BOOLEAN__STRING_UINT_FLAGS;
579 g_closure_set_marshal (closure, marshal);
582 pool->entries = g_slist_prepend (pool->entries, entry);
583 g_hash_table_insert (pool->entries_hash, entry, entry);
587 * clutter_binding_pool_override_action:
588 * @pool: a #ClutterBindingPool
589 * @key_val: key symbol
590 * @modifiers: bitmask of modifiers
591 * @callback: function to be called when the action is activated
592 * @data: data to be passed to @callback
593 * @notify: function to be called when the action is removed
596 * Allows overriding the action for @key_val and @modifiers inside a
597 * #ClutterBindingPool. See clutter_binding_pool_install_action().
599 * When an action has been activated using clutter_binding_pool_activate()
600 * the passed @callback will be invoked (with @data).
602 * Actions can be blocked with clutter_binding_pool_block_action()
603 * and then unblocked using clutter_binding_pool_unblock_action().
608 clutter_binding_pool_override_action (ClutterBindingPool *pool,
610 ClutterModifierType modifiers,
613 GDestroyNotify notify)
615 ClutterBindingEntry *entry;
618 g_return_if_fail (pool != NULL);
619 g_return_if_fail (key_val != 0);
620 g_return_if_fail (callback != NULL);
622 entry = binding_pool_lookup_entry (pool, key_val, modifiers);
623 if (G_UNLIKELY (entry == NULL))
625 g_warning ("There is no action for the given key symbol "
626 "of %d (modifiers: %d) installed inside the "
634 g_closure_unref (entry->closure);
635 entry->closure = NULL;
638 closure = g_cclosure_new (callback, data, (GClosureNotify) notify);
639 entry->closure = g_closure_ref (closure);
640 g_closure_sink (closure);
642 if (G_CLOSURE_NEEDS_MARSHAL (closure))
644 GClosureMarshal marshal;
646 marshal = _clutter_marshal_BOOLEAN__STRING_UINT_FLAGS;
647 g_closure_set_marshal (closure, marshal);
652 * clutter_binding_pool_override_closure:
653 * @pool: a #ClutterBindingPool
654 * @key_val: key symbol
655 * @modifiers: bitmask of modifiers
656 * @closure: a #GClosure
658 * A #GClosure variant of clutter_binding_pool_override_action().
660 * Allows overriding the action for @key_val and @modifiers inside a
661 * #ClutterBindingPool. See clutter_binding_pool_install_closure().
663 * When an action has been activated using clutter_binding_pool_activate()
664 * the passed @callback will be invoked (with @data).
666 * Actions can be blocked with clutter_binding_pool_block_action()
667 * and then unblocked using clutter_binding_pool_unblock_action().
672 clutter_binding_pool_override_closure (ClutterBindingPool *pool,
674 ClutterModifierType modifiers,
677 ClutterBindingEntry *entry;
679 g_return_if_fail (pool != NULL);
680 g_return_if_fail (key_val != 0);
681 g_return_if_fail (closure != NULL);
683 entry = binding_pool_lookup_entry (pool, key_val, modifiers);
684 if (G_UNLIKELY (entry == NULL))
686 g_warning ("There is no action for the given key symbol "
687 "of %d (modifiers: %d) installed inside the "
695 g_closure_unref (entry->closure);
696 entry->closure = NULL;
699 entry->closure = g_closure_ref (closure);
700 g_closure_sink (closure);
702 if (G_CLOSURE_NEEDS_MARSHAL (closure))
704 GClosureMarshal marshal;
706 marshal = _clutter_marshal_BOOLEAN__STRING_UINT_FLAGS;
707 g_closure_set_marshal (closure, marshal);
712 * clutter_binding_pool_find_action:
713 * @pool: a #ClutterBindingPool
714 * @key_val: a key symbol
715 * @modifiers: a bitmask for the modifiers
717 * Retrieves the name of the action matching the given key symbol
718 * and modifiers bitmask.
720 * Return value: the name of the action, if found, or %NULL. The
721 * returned string is owned by the binding pool and should never
722 * be modified or freed
727 clutter_binding_pool_find_action (ClutterBindingPool *pool,
729 ClutterModifierType modifiers)
731 ClutterBindingEntry *entry;
733 g_return_val_if_fail (pool != NULL, NULL);
734 g_return_val_if_fail (key_val != 0, NULL);
736 entry = binding_pool_lookup_entry (pool, key_val, modifiers);
744 * clutter_binding_pool_remove_action:
745 * @pool: a #ClutterBindingPool
746 * @key_val: a key symbol
747 * @modifiers: a bitmask for the modifiers
749 * Removes the action matching the given @key_val, @modifiers pair,
755 clutter_binding_pool_remove_action (ClutterBindingPool *pool,
757 ClutterModifierType modifiers)
759 ClutterBindingEntry remove_entry = { 0, };
762 g_return_if_fail (pool != NULL);
763 g_return_if_fail (key_val != 0);
765 modifiers = modifiers & BINDING_MOD_MASK;
767 remove_entry.key_val = key_val;
768 remove_entry.modifiers = modifiers;
770 for (l = pool->entries; l != NULL; l = l->data)
772 ClutterBindingEntry *e = l->data;
774 if (e->key_val == remove_entry.key_val &&
775 e->modifiers == remove_entry.modifiers)
777 pool->entries = g_slist_remove_link (pool->entries, l);
782 g_hash_table_remove (pool->entries_hash, &remove_entry);
786 clutter_binding_entry_invoke (ClutterBindingEntry *entry,
795 GValue result = G_VALUE_INIT;
796 gboolean retval = TRUE;
798 g_value_init (¶ms[0], G_TYPE_OBJECT);
799 g_value_set_object (¶ms[0], gobject);
801 g_value_init (¶ms[1], G_TYPE_STRING);
802 g_value_set_static_string (¶ms[1], entry->name);
804 g_value_init (¶ms[2], G_TYPE_UINT);
805 g_value_set_uint (¶ms[2], entry->key_val);
807 g_value_init (¶ms[3], CLUTTER_TYPE_MODIFIER_TYPE);
808 g_value_set_flags (¶ms[3], entry->modifiers);
810 g_value_init (&result, G_TYPE_BOOLEAN);
812 g_closure_invoke (entry->closure, &result, 4, params, NULL);
814 retval = g_value_get_boolean (&result);
816 g_value_unset (&result);
818 g_value_unset (¶ms[0]);
819 g_value_unset (¶ms[1]);
820 g_value_unset (¶ms[2]);
821 g_value_unset (¶ms[3]);
827 * clutter_binding_pool_activate:
828 * @pool: a #ClutterBindingPool
829 * @key_val: the key symbol
830 * @modifiers: bitmask for the modifiers
831 * @gobject: a #GObject
833 * Activates the callback associated to the action that is
834 * bound to the @key_val and @modifiers pair.
836 * The callback has the following signature:
839 * void (* callback) (GObject *gobject,
840 * const gchar *action_name,
842 * ClutterModifierType modifiers,
843 * gpointer user_data);
846 * Where the #GObject instance is @gobject and the user data
847 * is the one passed when installing the action with
848 * clutter_binding_pool_install_action().
850 * If the action bound to the @key_val, @modifiers pair has been
851 * blocked using clutter_binding_pool_block_action(), the callback
852 * will not be invoked, and this function will return %FALSE.
854 * Return value: %TRUE if an action was found and was activated
859 clutter_binding_pool_activate (ClutterBindingPool *pool,
861 ClutterModifierType modifiers,
864 ClutterBindingEntry *entry = NULL;
866 g_return_val_if_fail (pool != NULL, FALSE);
867 g_return_val_if_fail (key_val != 0, FALSE);
868 g_return_val_if_fail (G_IS_OBJECT (gobject), FALSE);
870 modifiers = (modifiers & BINDING_MOD_MASK);
872 entry = binding_pool_lookup_entry (pool, key_val, modifiers);
876 if (!entry->is_blocked)
877 return clutter_binding_entry_invoke (entry, gobject);
883 * clutter_binding_pool_block_action:
884 * @pool: a #ClutterBindingPool
885 * @action_name: an action name
887 * Blocks all the actions with name @action_name inside @pool.
892 clutter_binding_pool_block_action (ClutterBindingPool *pool,
893 const gchar *action_name)
897 g_return_if_fail (pool != NULL);
898 g_return_if_fail (action_name != NULL);
900 for (l = pool->entries; l != NULL; l = l->next)
902 ClutterBindingEntry *entry = l->data;
904 if (g_str_equal (entry->name, (gpointer) action_name))
905 entry->is_blocked = TRUE;
910 * clutter_binding_pool_unblock_action:
911 * @pool: a #ClutterBindingPool
912 * @action_name: an action name
914 * Unblockes all the actions with name @action_name inside @pool.
916 * Unblocking an action does not cause the callback bound to it to
917 * be invoked in case clutter_binding_pool_activate() was called on
918 * an action previously blocked with clutter_binding_pool_block_action().
923 clutter_binding_pool_unblock_action (ClutterBindingPool *pool,
924 const gchar *action_name)
928 g_return_if_fail (pool != NULL);
929 g_return_if_fail (action_name != NULL);
931 for (l = pool->entries; l != NULL; l = l->next)
933 ClutterBindingEntry *entry = l->data;
935 if (g_str_equal (entry->name, (gpointer) action_name))
936 entry->is_blocked = FALSE;