From 1b1e77b46989ba97bfff8abdfa61df0f514a7eae Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 4 Jan 2011 12:32:04 +0000 Subject: [PATCH 01/41] event/x11: Rework the way we translate X11 events This is a lump commit that is fairly difficult to break down without either breaking bisecting or breaking the test cases. The new design for handling X11 event translation works this way: - ClutterBackend::translate_event() has been added as the central point used by a ClutterBackend implementation to translate a native event into a ClutterEvent; - ClutterEventTranslator is a private interface that should be implemented by backend-specific objects, like stage implementations and ClutterDeviceManager sub-classes, and allows dealing with class-specific event translation; - ClutterStageX11 implements EventTranslator, and deals with the stage-relative X11 events coming from the X11 event source; - ClutterStageGLX overrides EventTranslator, in order to deal with the INTEL_GLX_swap_event extension, and it chains up to the X11 default implementation; - ClutterDeviceManagerX11 has been split into two separate classes, one that deals with core and (optionally) XI1 events, and the other that deals with XI2 events; the selection is done at run-time, since the core+XI1 and XI2 mechanisms are mutually exclusive. All the other backends we officially support still use their own custom event source and translation function, but the end goal is to migrate them to the translate_event() virtual function, and have the event source be a shared part of Clutter core. --- clutter/Makefile.am | 24 +- clutter/clutter-backend-private.h | 8 + clutter/clutter-backend.c | 12 + clutter/clutter-device-manager-private.h | 105 +- clutter/clutter-device-manager.c | 29 + clutter/clutter-device-manager.h | 1 + clutter/clutter-event-translator.c | 38 + clutter/clutter-event-translator.h | 40 + clutter/clutter-event.c | 77 +- clutter/clutter-event.h | 51 - clutter/clutter-input-device.c | 476 +++++++- clutter/clutter-input-device.h | 55 +- clutter/clutter-marshal.list | 1 + clutter/clutter-private.h | 3 + clutter/clutter-types.h | 51 + clutter/glx/clutter-backend-glx.c | 11 +- clutter/glx/clutter-event-glx.c | 105 -- clutter/glx/clutter-event-glx.h | 38 - clutter/glx/clutter-stage-glx.c | 186 +-- clutter/x11/clutter-backend-x11.c | 362 ++++-- clutter/x11/clutter-backend-x11.h | 55 +- clutter/x11/clutter-device-manager-core-x11.c | 745 ++++++++++++ ...11.h => clutter-device-manager-core-x11.h} | 4 +- clutter/x11/clutter-device-manager-x11.c | 354 ------ clutter/x11/clutter-device-manager-xi2.c | 1058 +++++++++++++++++ clutter/x11/clutter-device-manager-xi2.h | 66 + clutter/x11/clutter-event-x11.c | 116 +- clutter/x11/clutter-input-device-core-x11.c | 414 +++++++ clutter/x11/clutter-input-device-core-x11.h | 26 + clutter/x11/clutter-input-device-x11.c | 228 ---- clutter/x11/clutter-input-device-x11.h | 25 - clutter/x11/clutter-input-device-xi2.c | 161 +++ clutter/x11/clutter-input-device-xi2.h | 22 + clutter/x11/clutter-stage-x11.c | 552 ++++++++- clutter/x11/clutter-stage-x11.h | 9 + configure.ac | 48 +- tests/interactive/test-devices.c | 13 + 37 files changed, 4369 insertions(+), 1200 deletions(-) create mode 100644 clutter/clutter-event-translator.c create mode 100644 clutter/clutter-event-translator.h delete mode 100644 clutter/glx/clutter-event-glx.c delete mode 100644 clutter/glx/clutter-event-glx.h create mode 100644 clutter/x11/clutter-device-manager-core-x11.c rename clutter/x11/{clutter-device-manager-x11.h => clutter-device-manager-core-x11.h} (97%) delete mode 100644 clutter/x11/clutter-device-manager-x11.c create mode 100644 clutter/x11/clutter-device-manager-xi2.c create mode 100644 clutter/x11/clutter-device-manager-xi2.h create mode 100644 clutter/x11/clutter-input-device-core-x11.c create mode 100644 clutter/x11/clutter-input-device-core-x11.h delete mode 100644 clutter/x11/clutter-input-device-x11.c delete mode 100644 clutter/x11/clutter-input-device-x11.h create mode 100644 clutter/x11/clutter-input-device-xi2.c create mode 100644 clutter/x11/clutter-input-device-xi2.h diff --git a/clutter/Makefile.am b/clutter/Makefile.am index e75ef7004..a3a61e414 100644 --- a/clutter/Makefile.am +++ b/clutter/Makefile.am @@ -232,6 +232,7 @@ source_h_priv = \ $(srcdir)/clutter-bezier.h \ $(srcdir)/clutter-debug.h \ $(srcdir)/clutter-device-manager-private.h \ + $(srcdir)/clutter-event-translator.h \ $(srcdir)/clutter-id-pool.h \ $(srcdir)/clutter-keysyms-table.h \ $(srcdir)/clutter-master-clock.h \ @@ -247,6 +248,7 @@ source_h_priv = \ # private source code; these should not be introspected source_c_priv = \ + $(srcdir)/clutter-event-translator.c \ $(srcdir)/clutter-id-pool.c \ $(srcdir)/clutter-profile.c \ $(srcdir)/clutter-timeout-interval.c \ @@ -294,9 +296,9 @@ backend_source_built = # X11 backend rules x11_source_c = \ $(srcdir)/x11/clutter-backend-x11.c \ - $(srcdir)/x11/clutter-device-manager-x11.c \ + $(srcdir)/x11/clutter-device-manager-core-x11.c \ $(srcdir)/x11/clutter-event-x11.c \ - $(srcdir)/x11/clutter-input-device-x11.c \ + $(srcdir)/x11/clutter-input-device-core-x11.c \ $(srcdir)/x11/clutter-keymap-x11.c \ $(srcdir)/x11/clutter-stage-x11.c \ $(srcdir)/x11/clutter-x11-texture-pixmap.c \ @@ -309,8 +311,8 @@ x11_source_h = \ x11_source_h_priv = \ $(srcdir)/x11/clutter-backend-x11.h \ - $(srcdir)/x11/clutter-device-manager-x11.h \ - $(srcdir)/x11/clutter-input-device-x11.h \ + $(srcdir)/x11/clutter-device-manager-core-x11.h \ + $(srcdir)/x11/clutter-input-device-core-x11.h \ $(srcdir)/x11/clutter-keymap-x11.h \ $(srcdir)/x11/clutter-settings-x11.h \ $(srcdir)/x11/clutter-stage-x11.h \ @@ -323,6 +325,18 @@ x11_source_c_priv = \ $(srcdir)/x11/xsettings/xsettings-common.h \ $(NULL) +if BUILD_XI2 +x11_source_c += \ + $(srcdir)/x11/clutter-device-manager-xi2.c \ + $(srcdir)/x11/clutter-input-device-xi2.c \ + $(NULL) + +x11_source_h_priv += \ + $(srcdir)/x11/clutter-device-manager-xi2.h \ + $(srcdir)/x11/clutter-input-device-xi2.h \ + $(NULL) +endif # BUILD_XI2 + if SUPPORT_X11 backend_source_h += $(x11_source_h) backend_source_c += $(x11_source_c) @@ -345,7 +359,6 @@ endif # SUPPORT_X11 # GLX backend rules glx_source_c = \ $(srcdir)/glx/clutter-backend-glx.c \ - $(srcdir)/glx/clutter-event-glx.c \ $(srcdir)/glx/clutter-glx-texture-pixmap.c \ $(srcdir)/glx/clutter-stage-glx.c \ $(NULL) @@ -357,7 +370,6 @@ glx_source_h = \ glx_source_h_priv = \ $(srcdir)/glx/clutter-backend-glx.h \ - $(srcdir)/glx/clutter-event-glx.h \ $(srcdir)/glx/clutter-stage-glx.h \ $(NULL) diff --git a/clutter/clutter-backend-private.h b/clutter/clutter-backend-private.h index fb6330161..8d95acf65 100644 --- a/clutter/clutter-backend-private.h +++ b/clutter/clutter-backend-private.h @@ -71,6 +71,10 @@ struct _ClutterBackendClass void (* free_event_data) (ClutterBackend *backend, ClutterEvent *event); + gboolean (* translate_event) (ClutterBackend *backend, + gpointer native, + ClutterEvent *event); + /* signals */ void (* resolution_changed) (ClutterBackend *backend); void (* font_changed) (ClutterBackend *backend); @@ -114,6 +118,10 @@ gfloat _clutter_backend_get_units_per_em (ClutterBackend *backend gint32 _clutter_backend_get_units_serial (ClutterBackend *backend); +gboolean _clutter_backend_translate_event (ClutterBackend *backend, + gpointer native, + ClutterEvent *event); + G_END_DECLS #endif /* __CLUTTER_BACKEND_PRIVATE_H__ */ diff --git a/clutter/clutter-backend.c b/clutter/clutter-backend.c index ff4c9738f..014f2393e 100644 --- a/clutter/clutter-backend.c +++ b/clutter/clutter-backend.c @@ -881,3 +881,15 @@ _clutter_backend_get_units_serial (ClutterBackend *backend) return backend->priv->units_serial; } + +gboolean +_clutter_backend_translate_event (ClutterBackend *backend, + gpointer native, + ClutterEvent *event) +{ + g_return_val_if_fail (CLUTTER_IS_BACKEND (backend), FALSE); + + return CLUTTER_BACKEND_GET_CLASS (backend)->translate_event (backend, + native, + event); +} diff --git a/clutter/clutter-device-manager-private.h b/clutter/clutter-device-manager-private.h index ea21926c0..f3764d72d 100644 --- a/clutter/clutter-device-manager-private.h +++ b/clutter/clutter-device-manager-private.h @@ -25,11 +25,31 @@ #ifndef __CLUTTER_DEVICE_MANAGER_PRIVATE_H__ #define __CLUTTER_DEVICE_MANAGER_PRIVATE_H__ +#include #include #include G_BEGIN_DECLS +typedef struct _ClutterAxisInfo +{ + ClutterInputAxis axis; + + gdouble min_axis; + gdouble max_axis; + + gdouble min_value; + gdouble max_value; + + gdouble resolution; +} ClutterAxisInfo; + +typedef struct _ClutterKeyInfo +{ + guint keyval; + ClutterModifierType modifiers; +} ClutterKeyInfo; + struct _ClutterInputDevice { GObject parent_instance; @@ -37,9 +57,19 @@ struct _ClutterInputDevice gint id; ClutterInputDeviceType device_type; + ClutterInputMode device_mode; gchar *device_name; + ClutterDeviceManager *device_manager; + + ClutterBackend *backend; + + /* the associated device */ + ClutterInputDevice *associated; + + GList *slaves; + /* the actor underneath the pointer */ ClutterActor *cursor_actor; @@ -65,28 +95,69 @@ struct _ClutterInputDevice guint32 previous_time; gint previous_button_number; ClutterModifierType previous_state; + + GArray *axes; + + guint n_keys; + GArray *keys; + gint min_keycode; + gint max_keycode; + + guint has_cursor : 1; }; /* device manager */ -void _clutter_device_manager_add_device (ClutterDeviceManager *device_manager, - ClutterInputDevice *device); -void _clutter_device_manager_remove_device (ClutterDeviceManager *device_manager, - ClutterInputDevice *device); -void _clutter_device_manager_update_devices (ClutterDeviceManager *device_manager); +void _clutter_device_manager_add_device (ClutterDeviceManager *device_manager, + ClutterInputDevice *device); +void _clutter_device_manager_remove_device (ClutterDeviceManager *device_manager, + ClutterInputDevice *device); +void _clutter_device_manager_update_devices (ClutterDeviceManager *device_manager); +void _clutter_device_manager_select_stage_events (ClutterDeviceManager *device_manager, + ClutterStage *stage, + gint event_mask); +ClutterBackend *_clutter_device_manager_get_backend (ClutterDeviceManager *device_manager); /* input device */ -void _clutter_input_device_set_coords (ClutterInputDevice *device, - gint x, - gint y); -void _clutter_input_device_set_state (ClutterInputDevice *device, - ClutterModifierType state); -void _clutter_input_device_set_time (ClutterInputDevice *device, - guint32 time_); -void _clutter_input_device_set_stage (ClutterInputDevice *device, - ClutterStage *stage); -void _clutter_input_device_set_actor (ClutterInputDevice *device, - ClutterActor *actor); -ClutterActor *_clutter_input_device_update (ClutterInputDevice *device); +void _clutter_input_device_set_coords (ClutterInputDevice *device, + gint x, + gint y); +void _clutter_input_device_set_state (ClutterInputDevice *device, + ClutterModifierType state); +void _clutter_input_device_set_time (ClutterInputDevice *device, + guint32 time_); +void _clutter_input_device_set_stage (ClutterInputDevice *device, + ClutterStage *stage); +void _clutter_input_device_set_actor (ClutterInputDevice *device, + ClutterActor *actor); +ClutterActor * _clutter_input_device_update (ClutterInputDevice *device); +void _clutter_input_device_set_keys (ClutterInputDevice *device, + guint n_keys, + gint min_keycode, + gint max_keycode); +guint _clutter_input_device_add_axis (ClutterInputDevice *device, + ClutterInputAxis axis, + gdouble min_value, + gdouble max_value, + gdouble resolution); +ClutterInputAxis _clutter_input_device_get_axis (ClutterInputDevice *device, + guint index_); +void _clutter_input_device_reset_axes (ClutterInputDevice *device); + +void _clutter_input_device_set_associated_device (ClutterInputDevice *device, + ClutterInputDevice *associated); +void _clutter_input_device_add_slave (ClutterInputDevice *master, + ClutterInputDevice *slave); +void _clutter_input_device_remove_slave (ClutterInputDevice *master, + ClutterInputDevice *slave); + +void _clutter_input_device_select_stage_events (ClutterInputDevice *device, + ClutterStage *stage, + gint event_flags); + +gboolean _clutter_input_device_translate_axis (ClutterInputDevice *device, + guint index_, + gint value, + gdouble *axis_value); G_END_DECLS diff --git a/clutter/clutter-device-manager.c b/clutter/clutter-device-manager.c index ccb911d87..da6798334 100644 --- a/clutter/clutter-device-manager.c +++ b/clutter/clutter-device-manager.c @@ -308,6 +308,27 @@ clutter_device_manager_get_core_device (ClutterDeviceManager *device_manager, return manager_class->get_core_device (device_manager, device_type); } +void +_clutter_device_manager_select_stage_events (ClutterDeviceManager *device_manager, + ClutterStage *stage, + gint event_flags) +{ + ClutterDeviceManagerClass *manager_class; + const GSList *devices, *d; + + g_return_if_fail (CLUTTER_IS_DEVICE_MANAGER (device_manager)); + + manager_class = CLUTTER_DEVICE_MANAGER_GET_CLASS (device_manager); + devices = manager_class->get_devices (device_manager); + + for (d = devices; d != NULL; d = d->next) + { + ClutterInputDevice *device = d->data; + + _clutter_input_device_select_stage_events (device, stage, event_flags); + } +} + /* * _clutter_device_manager_add_device: * @device_manager: a #ClutterDeviceManager @@ -405,3 +426,11 @@ _clutter_device_manager_update_devices (ClutterDeviceManager *device_manager) _clutter_input_device_update (device); } } + +ClutterBackend * +_clutter_device_manager_get_backend (ClutterDeviceManager *manager) +{ + g_return_val_if_fail (CLUTTER_IS_DEVICE_MANAGER (manager), NULL); + + return manager->priv->backend; +} diff --git a/clutter/clutter-device-manager.h b/clutter/clutter-device-manager.h index f9546622d..94cde3d17 100644 --- a/clutter/clutter-device-manager.h +++ b/clutter/clutter-device-manager.h @@ -29,6 +29,7 @@ #define __CLUTTER_DEVICE_MANAGER_H__ #include +#include G_BEGIN_DECLS diff --git a/clutter/clutter-event-translator.c b/clutter/clutter-event-translator.c new file mode 100644 index 000000000..b45549679 --- /dev/null +++ b/clutter/clutter-event-translator.c @@ -0,0 +1,38 @@ +#include "config.h" + +#include "clutter-event-translator.h" + +#include "clutter-backend.h" +#include "clutter-private.h" + +#define clutter_event_translator_get_type _clutter_event_translator_get_type + +typedef ClutterEventTranslatorIface ClutterEventTranslatorInterface; + +G_DEFINE_INTERFACE (ClutterEventTranslator, clutter_event_translator, G_TYPE_OBJECT); + +static ClutterTranslateReturn +default_translate_event (ClutterEventTranslator *translator, + gpointer native, + ClutterEvent *event) +{ + return CLUTTER_TRANSLATE_CONTINUE; +} + +static void +clutter_event_translator_default_init (ClutterEventTranslatorIface *iface) +{ + iface->translate_event = default_translate_event; +} + +ClutterTranslateReturn +_clutter_event_translator_translate_event (ClutterEventTranslator *translator, + gpointer native, + ClutterEvent *translated) +{ + ClutterEventTranslatorIface *iface; + + iface = CLUTTER_EVENT_TRANSLATOR_GET_IFACE (translator); + + return iface->translate_event (translator, native, translated); +} diff --git a/clutter/clutter-event-translator.h b/clutter/clutter-event-translator.h new file mode 100644 index 000000000..f7facc7f9 --- /dev/null +++ b/clutter/clutter-event-translator.h @@ -0,0 +1,40 @@ +#ifndef __CLUTTER_EVENT_TRANSLATOR_H__ +#define __CLUTTER_EVENT_TRANSLATOR_H__ + +#include +#include + +G_BEGIN_DECLS + +#define CLUTTER_TYPE_EVENT_TRANSLATOR (_clutter_event_translator_get_type ()) +#define CLUTTER_EVENT_TRANSLATOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_EVENT_TRANSLATOR, ClutterEventTranslator)) +#define CLUTTER_IS_EVENT_TRANSLATOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_EVENT_TRANSLATOR)) +#define CLUTTER_EVENT_TRANSLATOR_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), CLUTTER_TYPE_EVENT_TRANSLATOR, ClutterEventTranslatorIface)) + +typedef struct _ClutterEventTranslator ClutterEventTranslator; +typedef struct _ClutterEventTranslatorIface ClutterEventTranslatorIface; + +typedef enum { + CLUTTER_TRANSLATE_CONTINUE, + CLUTTER_TRANSLATE_REMOVE, + CLUTTER_TRANSLATE_QUEUE +} ClutterTranslateReturn; + +struct _ClutterEventTranslatorIface +{ + GTypeInterface g_iface; + + ClutterTranslateReturn (* translate_event) (ClutterEventTranslator *translator, + gpointer native, + ClutterEvent *translated); +}; + +GType _clutter_event_translator_get_type (void) G_GNUC_CONST; + +ClutterTranslateReturn _clutter_event_translator_translate_event (ClutterEventTranslator *translator, + gpointer native, + ClutterEvent *translated); + +G_END_DECLS + +#endif /* __CLUTTER_EVENT_TRANSLATOR_H__ */ diff --git a/clutter/clutter-event.c b/clutter/clutter-event.c index 12e5825ff..df823dc1d 100644 --- a/clutter/clutter-event.c +++ b/clutter/clutter-event.c @@ -680,6 +680,35 @@ clutter_event_copy (const ClutterEvent *event) new_event = clutter_event_new (CLUTTER_NOTHING); *new_event = *event; + switch (event->type) + { + case CLUTTER_BUTTON_PRESS: + case CLUTTER_BUTTON_RELEASE: + if (event->button.device != NULL && event->button.axes != NULL) + { + gint n_axes; + + n_axes = clutter_input_device_get_n_axes (event->button.device); + new_event->button.axes = g_memdup (event->button.axes, + sizeof (gdouble) * n_axes); + } + break; + + case CLUTTER_MOTION: + if (event->motion.device != NULL && event->motion.axes != NULL) + { + gint n_axes; + + n_axes = clutter_input_device_get_n_axes (event->motion.device); + new_event->motion.axes = g_memdup (event->motion.axes, + sizeof (gdouble) * n_axes); + } + break; + + default: + break; + } + if (is_event_allocated (event)) _clutter_backend_copy_event_data (clutter_get_default_backend (), event, @@ -701,6 +730,21 @@ clutter_event_free (ClutterEvent *event) { _clutter_backend_free_event_data (clutter_get_default_backend (), event); + switch (event->type) + { + case CLUTTER_BUTTON_PRESS: + case CLUTTER_BUTTON_RELEASE: + g_free (event->button.axes); + break; + + case CLUTTER_MOTION: + g_free (event->motion.axes); + break; + + default: + break; + } + g_hash_table_remove (all_events, event); g_slice_free (ClutterEventPrivate, (ClutterEventPrivate *) event); } @@ -756,6 +800,28 @@ clutter_event_peek (void) return g_queue_peek_tail (context->events_queue); } +void +_clutter_event_push (const ClutterEvent *event, + gboolean do_copy) +{ + ClutterMainContext *context = _clutter_context_get_default (); + + /* FIXME: check queue is valid */ + g_assert (context != NULL); + + if (do_copy) + { + ClutterEvent *copy; + + copy = clutter_event_copy (event); + copy->any.flags |= CLUTTER_EVENT_FLAG_SYNTHETIC; + + event = copy; + } + + g_queue_push_head (context->events_queue, (gpointer) event); +} + /** * clutter_event_put: * @event: a #ClutterEvent @@ -771,16 +837,7 @@ clutter_event_peek (void) void clutter_event_put (const ClutterEvent *event) { - ClutterMainContext *context = _clutter_context_get_default (); - ClutterEvent *event_copy; - - /* FIXME: check queue is valid */ - g_return_if_fail (context != NULL); - - event_copy = clutter_event_copy (event); - event_copy->any.flags |= CLUTTER_EVENT_FLAG_SYNTHETIC; - - g_queue_push_head (context->events_queue, event_copy); + _clutter_event_push (event, TRUE); } /** diff --git a/clutter/clutter-event.h b/clutter/clutter-event.h index adeb0406f..38bcd97b6 100644 --- a/clutter/clutter-event.h +++ b/clutter/clutter-event.h @@ -54,57 +54,6 @@ G_BEGIN_DECLS -/** - * ClutterModifierType: - * @CLUTTER_SHIFT_MASK: Mask applied by the Shift key - * @CLUTTER_LOCK_MASK: Mask applied by the Caps Lock key - * @CLUTTER_CONTROL_MASK: Mask applied by the Control key - * @CLUTTER_MOD1_MASK: Mask applied by the first Mod key - * @CLUTTER_MOD2_MASK: Mask applied by the second Mod key - * @CLUTTER_MOD3_MASK: Mask applied by the third Mod key - * @CLUTTER_MOD4_MASK: Mask applied by the fourth Mod key - * @CLUTTER_MOD5_MASK: Mask applied by the fifth Mod key - * @CLUTTER_BUTTON1_MASK: Mask applied by the first pointer button - * @CLUTTER_BUTTON2_MASK: Mask applied by the second pointer button - * @CLUTTER_BUTTON3_MASK: Mask applied by the third pointer button - * @CLUTTER_BUTTON4_MASK: Mask applied by the fourth pointer button - * @CLUTTER_BUTTON5_MASK: Mask applied by the fifth pointer button - * @CLUTTER_SUPER_MASK: Mask applied by the Super key - * @CLUTTER_HYPER_MASK: Mask applied by the Hyper key - * @CLUTTER_META_MASK: Mask applied by the Meta key - * @CLUTTER_RELEASE_MASK: Mask applied during release - * @CLUTTER_MODIFIER_MASK: A mask covering all modifier types - * - * Masks applied to a #ClutterEvent by modifiers. - * - * Since: 0.4 - */ -typedef enum { - CLUTTER_SHIFT_MASK = 1 << 0, - CLUTTER_LOCK_MASK = 1 << 1, - CLUTTER_CONTROL_MASK = 1 << 2, - CLUTTER_MOD1_MASK = 1 << 3, - CLUTTER_MOD2_MASK = 1 << 4, - CLUTTER_MOD3_MASK = 1 << 5, - CLUTTER_MOD4_MASK = 1 << 6, - CLUTTER_MOD5_MASK = 1 << 7, - CLUTTER_BUTTON1_MASK = 1 << 8, - CLUTTER_BUTTON2_MASK = 1 << 9, - CLUTTER_BUTTON3_MASK = 1 << 10, - CLUTTER_BUTTON4_MASK = 1 << 11, - CLUTTER_BUTTON5_MASK = 1 << 12, - - /* bits 15 to 25 are currently unused; bit 29 is used internally */ - - CLUTTER_SUPER_MASK = 1 << 26, - CLUTTER_HYPER_MASK = 1 << 27, - CLUTTER_META_MASK = 1 << 28, - - CLUTTER_RELEASE_MASK = 1 << 30, - - CLUTTER_MODIFIER_MASK = 0x5c001fff -} ClutterModifierType; - /** * ClutterEventFlags: * @CLUTTER_EVENT_NONE: No flag set diff --git a/clutter/clutter-input-device.c b/clutter/clutter-input-device.c index 0d2652fe8..415efcf89 100644 --- a/clutter/clutter-input-device.c +++ b/clutter/clutter-input-device.c @@ -35,11 +35,13 @@ #include "config.h" #endif +#include "clutter-input-device.h" + #include "clutter-actor-private.h" #include "clutter-debug.h" #include "clutter-device-manager-private.h" #include "clutter-enum-types.h" -#include "clutter-input-device.h" +#include "clutter-marshal.h" #include "clutter-private.h" #include "clutter-stage-private.h" @@ -47,17 +49,67 @@ enum { PROP_0, + PROP_BACKEND, + PROP_ID, - PROP_DEVICE_TYPE, PROP_NAME, + PROP_DEVICE_TYPE, + PROP_DEVICE_MANAGER, + PROP_DEVICE_MODE, + + PROP_HAS_CURSOR, + + PROP_N_AXES, + PROP_LAST }; -static GParamSpec *obj_props[PROP_LAST]; +enum +{ + SELECT_STAGE_EVENTS, + + LAST_SIGNAL +}; + +static GParamSpec *obj_props[PROP_LAST] = { NULL, }; + +static guint device_signals[LAST_SIGNAL] = { 0, }; G_DEFINE_TYPE (ClutterInputDevice, clutter_input_device, G_TYPE_OBJECT); +static void +clutter_input_device_dispose (GObject *gobject) +{ + ClutterInputDevice *device = CLUTTER_INPUT_DEVICE (gobject); + + g_free (device->device_name); + + if (device->device_mode == CLUTTER_INPUT_MODE_SLAVE) + _clutter_input_device_remove_slave (device->associated, device); + + if (device->associated != NULL) + { + _clutter_input_device_set_associated_device (device->associated, NULL); + g_object_unref (device->associated); + device->associated = NULL; + } + + if (device->axes != NULL) + { + g_array_free (device->axes, TRUE); + device->axes = NULL; + } + + if (device->keys != NULL) + { + g_array_free (device->keys, TRUE); + device->keys = NULL; + } + + G_OBJECT_CLASS (clutter_input_device_parent_class)->dispose (gobject); +} + static void clutter_input_device_set_property (GObject *gobject, guint prop_id, @@ -76,8 +128,24 @@ clutter_input_device_set_property (GObject *gobject, self->device_type = g_value_get_enum (value); break; + case PROP_DEVICE_MANAGER: + self->device_manager = g_value_get_object (value); + break; + + case PROP_DEVICE_MODE: + self->device_mode = g_value_get_enum (value); + break; + + case PROP_BACKEND: + self->backend = g_value_get_object (value); + break; + case PROP_NAME: - self->device_name = g_strdup (g_value_get_string (value)); + self->device_name = g_value_dup_string (value); + break; + + case PROP_HAS_CURSOR: + self->has_cursor = g_value_get_boolean (value); break; default: @@ -104,10 +172,33 @@ clutter_input_device_get_property (GObject *gobject, g_value_set_enum (value, self->device_type); break; + case PROP_DEVICE_MANAGER: + g_value_set_object (value, self->device_manager); + break; + + case PROP_DEVICE_MODE: + g_value_set_enum (value, self->device_mode); + break; + + case PROP_BACKEND: + g_value_set_object (value, self->backend); + break; + case PROP_NAME: g_value_set_string (value, self->device_name); break; + case PROP_HAS_CURSOR: + g_value_set_boolean (value, self->has_cursor); + break; + + case PROP_N_AXES: + if (self->axes != NULL) + g_value_set_uint (value, self->axes->len); + else + g_value_set_uint (value, 0); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; @@ -118,10 +209,6 @@ static void clutter_input_device_class_init (ClutterInputDeviceClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - GParamSpec *pspec; - - gobject_class->set_property = clutter_input_device_set_property; - gobject_class->get_property = clutter_input_device_get_property; /** * ClutterInputDevice:id: @@ -130,15 +217,14 @@ clutter_input_device_class_init (ClutterInputDeviceClass *klass) * * Since: 1.2 */ - pspec = g_param_spec_int ("id", - P_("Id"), - P_("Unique identifier of the device"), - -1, G_MAXINT, - 0, - CLUTTER_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY); - obj_props[PROP_ID] = pspec; - g_object_class_install_property (gobject_class, PROP_ID, pspec); + obj_props[PROP_ID] = + g_param_spec_int ("id", + P_("Id"), + P_("Unique identifier of the device"), + -1, G_MAXINT, + 0, + CLUTTER_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY); /** * ClutterInputDevice:name: @@ -147,14 +233,13 @@ clutter_input_device_class_init (ClutterInputDeviceClass *klass) * * Since: 1.2 */ - pspec = g_param_spec_string ("name", - P_("Name"), - P_("The name of the device"), - NULL, - CLUTTER_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY); - obj_props[PROP_NAME] = pspec; - g_object_class_install_property (gobject_class, PROP_NAME, pspec); + obj_props[PROP_NAME] = + g_param_spec_string ("name", + P_("Name"), + P_("The name of the device"), + NULL, + CLUTTER_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY); /** * ClutterInputDevice:device-type: @@ -163,15 +248,67 @@ clutter_input_device_class_init (ClutterInputDeviceClass *klass) * * Since: 1.2 */ - pspec = g_param_spec_enum ("device-type", - P_("Device Type"), - P_("The type of the device"), - CLUTTER_TYPE_INPUT_DEVICE_TYPE, - CLUTTER_POINTER_DEVICE, - CLUTTER_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY); - obj_props[PROP_DEVICE_TYPE] = pspec; - g_object_class_install_property (gobject_class, PROP_DEVICE_TYPE, pspec); + obj_props[PROP_DEVICE_TYPE] = + g_param_spec_enum ("device-type", + P_("Device Type"), + P_("The type of the device"), + CLUTTER_TYPE_INPUT_DEVICE_TYPE, + CLUTTER_POINTER_DEVICE, + CLUTTER_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY); + + obj_props[PROP_DEVICE_MANAGER] = + g_param_spec_object ("device-manager", + P_("Device Manager"), + P_("The device manager instance"), + CLUTTER_TYPE_DEVICE_MANAGER, + CLUTTER_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + + obj_props[PROP_DEVICE_MODE] = + g_param_spec_enum ("device-mode", + P_("Device Mode"), + P_("The mode of the device"), + CLUTTER_TYPE_INPUT_MODE, + CLUTTER_INPUT_MODE_FLOATING, + CLUTTER_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + + obj_props[PROP_HAS_CURSOR] = + g_param_spec_boolean ("has-cursor", + P_("Has Cursor"), + P_("Whether the device has a cursor"), + FALSE, + CLUTTER_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + + obj_props[PROP_N_AXES] = + g_param_spec_uint ("n-axes", + P_("Number of Axes"), + P_("The number of axes on the device"), + 0, G_MAXUINT, + 0, + CLUTTER_PARAM_READABLE); + + obj_props[PROP_BACKEND] = + g_param_spec_object ("backend", + P_("Backend"), + P_("The backend instance"), + CLUTTER_TYPE_BACKEND, + CLUTTER_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + + gobject_class->dispose = clutter_input_device_dispose; + gobject_class->set_property = clutter_input_device_set_property; + gobject_class->get_property = clutter_input_device_get_property; + g_object_class_install_properties (gobject_class, PROP_LAST, obj_props); + + device_signals[SELECT_STAGE_EVENTS] = + g_signal_new (I_("select-stage-events"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + 0, + NULL, NULL, + _clutter_marshal_VOID__OBJECT_INT, + G_TYPE_NONE, 2, + CLUTTER_TYPE_STAGE, + G_TYPE_INT); } static void @@ -187,6 +324,9 @@ clutter_input_device_init (ClutterInputDevice *self) self->current_y = self->previous_y = -1; self->current_button_number = self->previous_button_number = -1; self->current_state = self->previous_state = 0; + + self->min_keycode = 0; + self->max_keycode = G_MAXUINT; } /* @@ -483,7 +623,6 @@ clutter_input_device_get_device_coords (ClutterInputDevice *device, gint *y) { g_return_if_fail (CLUTTER_IS_INPUT_DEVICE (device)); - g_return_if_fail (device->device_type == CLUTTER_POINTER_DEVICE); if (x) *x = device->current_x; @@ -514,7 +653,8 @@ _clutter_input_device_update (ClutterInputDevice *device) ClutterActor *old_cursor_actor; gint x, y; - g_return_val_if_fail (device->device_type == CLUTTER_POINTER_DEVICE, NULL); + if (device->device_type == CLUTTER_KEYBOARD_DEVICE) + return NULL; stage = device->stage; if (G_UNLIKELY (stage == NULL)) @@ -700,3 +840,267 @@ clutter_input_device_update_from_event (ClutterInputDevice *device, if (update_stage) _clutter_input_device_set_stage (device, event_stage); } + +void +_clutter_input_device_reset_axes (ClutterInputDevice *device) +{ + if (device->axes != NULL) + { + g_array_free (device->axes, TRUE); + + g_object_notify_by_pspec (G_OBJECT (device), obj_props[PROP_N_AXES]); + } +} + +guint +_clutter_input_device_add_axis (ClutterInputDevice *device, + ClutterInputAxis axis, + gdouble minimum, + gdouble maximum, + gdouble resolution) +{ + ClutterAxisInfo info; + guint pos; + + if (device->axes == NULL) + device->axes = g_array_new (FALSE, TRUE, sizeof (ClutterAxisInfo)); + + info.axis = axis; + info.min_value = minimum; + info.max_value = maximum; + info.resolution = resolution; + + switch (axis) + { + case CLUTTER_INPUT_AXIS_X: + case CLUTTER_INPUT_AXIS_Y: + info.min_axis = 0; + info.max_axis = 0; + break; + + case CLUTTER_INPUT_AXIS_XTILT: + case CLUTTER_INPUT_AXIS_YTILT: + info.min_axis = -1; + info.max_axis = 1; + break; + + default: + info.min_axis = 0; + info.max_axis = 1; + } + + device->axes = g_array_append_val (device->axes, info); + pos = device->axes->len - 1; + + g_object_notify_by_pspec (G_OBJECT (device), obj_props[PROP_N_AXES]); + + return pos; +} + +gboolean +_clutter_input_device_translate_axis (ClutterInputDevice *device, + guint index_, + gint value, + gdouble *axis_value) +{ + ClutterAxisInfo *info; + gdouble width; + gdouble real_value; + + if (device->axes == NULL || index_ >= device->axes->len) + return FALSE; + + info = &g_array_index (device->axes, ClutterAxisInfo, index_); + + if (info->axis == CLUTTER_INPUT_AXIS_X || + info->axis == CLUTTER_INPUT_AXIS_Y) + return FALSE; + + width = info->max_value - info->min_value; + real_value = (info->max_axis * (value - info->min_value) + + info->min_axis * (info->max_value - value)) + / width; + + if (axis_value) + *axis_value = real_value; + + return TRUE; +} + +ClutterInputAxis +_clutter_input_device_get_axis (ClutterInputDevice *device, + guint index_) +{ + ClutterAxisInfo *info; + + if (device->axes == NULL) + return CLUTTER_INPUT_AXIS_IGNORE; + + if (index_ >= device->axes->len) + return CLUTTER_INPUT_AXIS_IGNORE; + + info = &g_array_index (device->axes, ClutterAxisInfo, index_); + + return info->axis; +} + +guint +clutter_input_device_get_n_axes (ClutterInputDevice *device) +{ + g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), 0); + + if (device->axes != NULL) + return device->axes->len; + + return 0; +} + +void +_clutter_input_device_set_keys (ClutterInputDevice *device, + guint n_keys, + gint min_keycode, + gint max_keycode) +{ + if (device->keys != NULL) + g_array_free (device->keys, TRUE); + + device->n_keys = n_keys; + device->keys = g_array_sized_new (FALSE, TRUE, + sizeof (ClutterKeyInfo), + n_keys); + + device->min_keycode = min_keycode; + device->max_keycode = max_keycode; +} + +guint +clutter_input_device_get_n_keys (ClutterInputDevice *device) +{ + g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), 0); + + if (device->keys != NULL) + return device->keys->len; + + return 0; +} + +void +clutter_input_device_set_key (ClutterInputDevice *device, + guint index_, + guint keyval, + ClutterModifierType modifiers) +{ + ClutterKeyInfo *key_info; + + g_return_if_fail (CLUTTER_IS_INPUT_DEVICE (device)); + g_return_if_fail (index_ < device->n_keys); + g_return_if_fail (keyval >= device->min_keycode && + keyval <= device->max_keycode); + + key_info = &g_array_index (device->keys, ClutterKeyInfo, index_); + key_info->keyval = keyval; + key_info->modifiers = modifiers; +} + +gboolean +clutter_input_device_get_key (ClutterInputDevice *device, + guint index_, + guint *keyval, + ClutterModifierType *modifiers) +{ + ClutterKeyInfo *key_info; + + g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), FALSE); + + if (device->keys == NULL) + return FALSE; + + if (index_ > device->keys->len) + return FALSE; + + key_info = &g_array_index (device->keys, ClutterKeyInfo, index_); + + if (!key_info->keyval && !key_info->modifiers) + return FALSE; + + if (keyval) + *keyval = key_info->keyval; + + if (modifiers) + *modifiers = key_info->modifiers; + + return TRUE; +} + +void +_clutter_input_device_add_slave (ClutterInputDevice *master, + ClutterInputDevice *slave) +{ + if (g_list_find (master->slaves, slave) == NULL) + master->slaves = g_list_prepend (master->slaves, slave); +} + +void +_clutter_input_device_remove_slave (ClutterInputDevice *master, + ClutterInputDevice *slave) +{ + if (g_list_find (master->slaves, slave) != NULL) + master->slaves = g_list_remove (master->slaves, slave); +} + +GList * +clutter_input_device_get_slave_devices (ClutterInputDevice *device) +{ + g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), NULL); + + return g_list_copy (device->slaves); +} + +void +_clutter_input_device_set_associated_device (ClutterInputDevice *device, + ClutterInputDevice *associated) +{ + if (device->associated == associated) + return; + + if (device->associated != NULL) + g_object_unref (device->associated); + + device->associated = associated; + if (device->associated != NULL) + g_object_ref (device->associated); + + CLUTTER_NOTE (MISC, "Associating device '%s' to device '%s'", + clutter_input_device_get_device_name (device), + device->associated != NULL + ? clutter_input_device_get_device_name (device->associated) + : "(none)"); + + if (device->device_mode != CLUTTER_INPUT_MODE_MASTER) + { + if (device->associated != NULL) + device->device_mode = CLUTTER_INPUT_MODE_SLAVE; + else + device->device_mode = CLUTTER_INPUT_MODE_FLOATING; + + g_object_notify_by_pspec (G_OBJECT (device), obj_props[PROP_DEVICE_MODE]); + } +} + +ClutterInputDevice * +clutter_input_device_get_associated_device (ClutterInputDevice *device) +{ + g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), NULL); + + return device->associated; +} + +void +_clutter_input_device_select_stage_events (ClutterInputDevice *device, + ClutterStage *stage, + gint event_mask) +{ + g_signal_emit (device, device_signals[SELECT_STAGE_EVENTS], 0, + stage, + event_mask); +} diff --git a/clutter/clutter-input-device.h b/clutter/clutter-input-device.h index a07983e01..e04d47d16 100644 --- a/clutter/clutter-input-device.h +++ b/clutter/clutter-input-device.h @@ -74,10 +74,29 @@ typedef enum { CLUTTER_TABLET_DEVICE, CLUTTER_TOUCHPAD_DEVICE, CLUTTER_TOUCHSCREEN_DEVICE, + CLUTTER_PEN_DEVICE, + CLUTTER_ERASER_DEVICE, + CLUTTER_CURSOR_DEVICE, CLUTTER_N_DEVICE_TYPES } ClutterInputDeviceType; +typedef enum { + CLUTTER_INPUT_MODE_MASTER, + CLUTTER_INPUT_MODE_SLAVE, + CLUTTER_INPUT_MODE_FLOATING +} ClutterInputMode; + +typedef enum { + CLUTTER_INPUT_AXIS_IGNORE, + CLUTTER_INPUT_AXIS_X, + CLUTTER_INPUT_AXIS_Y, + CLUTTER_INPUT_AXIS_PRESSURE, + CLUTTER_INPUT_AXIS_XTILT, + CLUTTER_INPUT_AXIS_YTILT, + CLUTTER_INPUT_AXIS_WHEEL +} ClutterInputAxis; + /** * ClutterInputDeviceClass: * @@ -94,18 +113,32 @@ struct _ClutterInputDeviceClass GType clutter_input_device_get_type (void) G_GNUC_CONST; -ClutterInputDeviceType clutter_input_device_get_device_type (ClutterInputDevice *device); -gint clutter_input_device_get_device_id (ClutterInputDevice *device); -void clutter_input_device_get_device_coords (ClutterInputDevice *device, - gint *x, - gint *y); -ClutterActor * clutter_input_device_get_pointer_actor (ClutterInputDevice *device); -ClutterStage * clutter_input_device_get_pointer_stage (ClutterInputDevice *device); -G_CONST_RETURN gchar * clutter_input_device_get_device_name (ClutterInputDevice *device); +ClutterInputDeviceType clutter_input_device_get_device_type (ClutterInputDevice *device); +gint clutter_input_device_get_device_id (ClutterInputDevice *device); +void clutter_input_device_get_device_coords (ClutterInputDevice *device, + gint *x, + gint *y); +ClutterActor * clutter_input_device_get_pointer_actor (ClutterInputDevice *device); +ClutterStage * clutter_input_device_get_pointer_stage (ClutterInputDevice *device); +G_CONST_RETURN gchar * clutter_input_device_get_device_name (ClutterInputDevice *device); -void clutter_input_device_update_from_event (ClutterInputDevice *device, - ClutterEvent *event, - gboolean update_stage); +guint clutter_input_device_get_n_axes (ClutterInputDevice *device); + +void clutter_input_device_set_key (ClutterInputDevice *device, + guint index_, + guint keyval, + ClutterModifierType modifiers); +gboolean clutter_input_device_get_key (ClutterInputDevice *device, + guint index_, + guint *keyval, + ClutterModifierType *modifiers); + +ClutterInputDevice * clutter_input_device_get_associated_device (ClutterInputDevice *device); +GList * clutter_input_device_get_slave_devices (ClutterInputDevice *device); + +void clutter_input_device_update_from_event (ClutterInputDevice *device, + ClutterEvent *event, + gboolean update_stage); G_END_DECLS diff --git a/clutter/clutter-marshal.list b/clutter/clutter-marshal.list index e5d9d5cd9..49745effc 100644 --- a/clutter/clutter-marshal.list +++ b/clutter/clutter-marshal.list @@ -13,6 +13,7 @@ VOID:INT,INT,INT,INT VOID:OBJECT VOID:OBJECT,FLOAT,FLOAT VOID:OBJECT,FLOAT,FLOAT,FLAGS +VOID:OBJECT,INT VOID:OBJECT,PARAM VOID:OBJECT,POINTER VOID:OBJECT,UINT diff --git a/clutter/clutter-private.h b/clutter/clutter-private.h index 8cf48c57b..79b6dcba9 100644 --- a/clutter/clutter-private.h +++ b/clutter/clutter-private.h @@ -228,6 +228,9 @@ void _clutter_event_set_platform_data (ClutterEvent *event, gpointer data); gpointer _clutter_event_get_platform_data (const ClutterEvent *event); +void _clutter_event_push (const ClutterEvent *event, + gboolean do_copy); + void _clutter_util_fully_transform_vertices (const CoglMatrix *modelview, const CoglMatrix *projection, const int *viewport, diff --git a/clutter/clutter-types.h b/clutter/clutter-types.h index b5951c5a5..8ad726198 100644 --- a/clutter/clutter-types.h +++ b/clutter/clutter-types.h @@ -489,6 +489,57 @@ void clutter_paint_volume_union (ClutterPaintVolume gboolean clutter_paint_volume_set_from_allocation (ClutterPaintVolume *pv, ClutterActor *actor); +/** + * ClutterModifierType: + * @CLUTTER_SHIFT_MASK: Mask applied by the Shift key + * @CLUTTER_LOCK_MASK: Mask applied by the Caps Lock key + * @CLUTTER_CONTROL_MASK: Mask applied by the Control key + * @CLUTTER_MOD1_MASK: Mask applied by the first Mod key + * @CLUTTER_MOD2_MASK: Mask applied by the second Mod key + * @CLUTTER_MOD3_MASK: Mask applied by the third Mod key + * @CLUTTER_MOD4_MASK: Mask applied by the fourth Mod key + * @CLUTTER_MOD5_MASK: Mask applied by the fifth Mod key + * @CLUTTER_BUTTON1_MASK: Mask applied by the first pointer button + * @CLUTTER_BUTTON2_MASK: Mask applied by the second pointer button + * @CLUTTER_BUTTON3_MASK: Mask applied by the third pointer button + * @CLUTTER_BUTTON4_MASK: Mask applied by the fourth pointer button + * @CLUTTER_BUTTON5_MASK: Mask applied by the fifth pointer button + * @CLUTTER_SUPER_MASK: Mask applied by the Super key + * @CLUTTER_HYPER_MASK: Mask applied by the Hyper key + * @CLUTTER_META_MASK: Mask applied by the Meta key + * @CLUTTER_RELEASE_MASK: Mask applied during release + * @CLUTTER_MODIFIER_MASK: A mask covering all modifier types + * + * Masks applied to a #ClutterEvent by modifiers. + * + * Since: 0.4 + */ +typedef enum { + CLUTTER_SHIFT_MASK = 1 << 0, + CLUTTER_LOCK_MASK = 1 << 1, + CLUTTER_CONTROL_MASK = 1 << 2, + CLUTTER_MOD1_MASK = 1 << 3, + CLUTTER_MOD2_MASK = 1 << 4, + CLUTTER_MOD3_MASK = 1 << 5, + CLUTTER_MOD4_MASK = 1 << 6, + CLUTTER_MOD5_MASK = 1 << 7, + CLUTTER_BUTTON1_MASK = 1 << 8, + CLUTTER_BUTTON2_MASK = 1 << 9, + CLUTTER_BUTTON3_MASK = 1 << 10, + CLUTTER_BUTTON4_MASK = 1 << 11, + CLUTTER_BUTTON5_MASK = 1 << 12, + + /* bits 15 to 25 are currently unused; bit 29 is used internally */ + + CLUTTER_SUPER_MASK = 1 << 26, + CLUTTER_HYPER_MASK = 1 << 27, + CLUTTER_META_MASK = 1 << 28, + + CLUTTER_RELEASE_MASK = 1 << 30, + + CLUTTER_MODIFIER_MASK = 0x5c001fff +} ClutterModifierType; + G_END_DECLS #endif /* __CLUTTER_TYPES_H__ */ diff --git a/clutter/glx/clutter-backend-glx.c b/clutter/glx/clutter-backend-glx.c index d7a6117e5..6e5258d91 100644 --- a/clutter/glx/clutter-backend-glx.c +++ b/clutter/glx/clutter-backend-glx.c @@ -37,12 +37,12 @@ #include #include "clutter-backend-glx.h" -#include "clutter-event-glx.h" #include "clutter-stage-glx.h" #include "clutter-glx.h" #include "clutter-profile.h" #include "clutter-debug.h" +#include "clutter-event-translator.h" #include "clutter-event.h" #include "clutter-main.h" #include "clutter-private.h" @@ -108,8 +108,8 @@ clutter_backend_glx_post_parse (ClutterBackend *backend, /* XXX: Technically we should require >= GLX 1.3 support but for a long * time Mesa has exported a hybrid GLX, exporting extensions specified * to require GLX 1.3, but still reporting 1.2 via glXQueryVersion. */ - if (!glXQueryVersion (backend_x11->xdpy, &glx_major, &glx_minor) - || !(glx_major == 1 && glx_minor >= 2)) + if (!glXQueryVersion (backend_x11->xdpy, &glx_major, &glx_minor) || + !(glx_major == 1 && glx_minor >= 2)) { g_set_error (error, CLUTTER_INIT_ERROR, CLUTTER_INIT_ERROR_BACKEND, @@ -787,6 +787,7 @@ clutter_backend_glx_create_stage (ClutterBackend *backend, GError **error) { ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (backend); + ClutterEventTranslator *translator; ClutterStageWindow *stage_window; ClutterStageX11 *stage_x11; @@ -799,6 +800,9 @@ clutter_backend_glx_create_stage (ClutterBackend *backend, stage_x11 = CLUTTER_STAGE_X11 (stage_window); stage_x11->wrapper = wrapper; + translator = CLUTTER_EVENT_TRANSLATOR (stage_x11); + _clutter_backend_x11_add_event_translator (backend_x11, translator); + CLUTTER_NOTE (BACKEND, "GLX stage created[%p] (dpy:%p, screen:%d, root:%u, wrap:%p)", stage_window, @@ -831,7 +835,6 @@ _clutter_backend_glx_class_init (ClutterBackendGLXClass *klass) backend_class->ensure_context = clutter_backend_glx_ensure_context; backendx11_class->get_visual_info = clutter_backend_glx_get_visual_info; - backendx11_class->handle_event = _clutter_backend_glx_handle_event; } static void diff --git a/clutter/glx/clutter-event-glx.c b/clutter/glx/clutter-event-glx.c deleted file mode 100644 index dadb3b130..000000000 --- a/clutter/glx/clutter-event-glx.c +++ /dev/null @@ -1,105 +0,0 @@ -/* Clutter. - * An OpenGL based 'interactive canvas' library. - * Authored By Matthew Allum - * Copyright (C) 2006-2007 OpenedHand - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see . - * - * - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "clutter-x11.h" -#include "clutter-stage-x11.h" -#include "clutter-backend-x11.h" -#include "clutter-stage-glx.h" -#include "clutter-backend-glx.h" - -#include "clutter-private.h" -#include "clutter-stage-private.h" - -#include -#include - -#include - -#include - -#include - -gboolean -_clutter_backend_glx_handle_event (ClutterBackendX11 *backend_x11, - XEvent *xevent) -{ -#ifdef GLX_INTEL_swap_event - ClutterBackendGLX *backend_glx = CLUTTER_BACKEND_GLX (backend_x11); - ClutterStageManager *stage_manager; - GLXBufferSwapComplete *swap_complete_event; - const GSList *l; - - if (xevent->type != (backend_glx->event_base + GLX_BufferSwapComplete)) - return FALSE; /* Unhandled */ - - swap_complete_event = (GLXBufferSwapComplete *)xevent; - -#if 0 - { - const char *event_name; - if (swap_complete_event->event_type == GLX_EXCHANGE_COMPLETE_INTEL) - event_name = "GLX_EXCHANGE_COMPLETE"; - else if (swap_complete_event->event_type == GLX_BLIT_COMPLETE_INTEL) - event_name = "GLX_BLIT_COMPLETE"; - else - { - g_assert (swap_complete_event->event_type == GLX_FLIP_COMPLETE_INTEL); - event_name = "GLX_FLIP_COMPLETE"; - } - - g_print ("XXX: clutter_backend_glx_event_handle event = %s\n", - event_name); - } -#endif - - stage_manager = clutter_stage_manager_get_default (); - - for (l = clutter_stage_manager_peek_stages (stage_manager); l; l = l->next) - { - ClutterStageWindow *stage_win = _clutter_stage_get_window (l->data); - ClutterStageGLX *stage_glx = CLUTTER_STAGE_GLX (stage_win); - ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_win); - - if (stage_x11->xwin == swap_complete_event->drawable) - { - /* Early versions of the swap_event implementation in Mesa - * deliver BufferSwapComplete event when not selected for, - * so if we get a swap event we aren't expecting, just ignore it. - * - * https://bugs.freedesktop.org/show_bug.cgi?id=27962 - */ - if (stage_glx->pending_swaps > 0) - stage_glx->pending_swaps--; - - return TRUE; - } - } - - return TRUE; -#else - return FALSE; -#endif -} - diff --git a/clutter/glx/clutter-event-glx.h b/clutter/glx/clutter-event-glx.h deleted file mode 100644 index 79fa272f2..000000000 --- a/clutter/glx/clutter-event-glx.h +++ /dev/null @@ -1,38 +0,0 @@ -/* Clutter. - * An OpenGL based 'interactive canvas' library. - * Copyright (C) 2009 Intel Corporation. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see . - * - * - */ - -#ifndef __CLUTTER_EVENT_GLX_H__ -#define __CLUTTER_EVENT_GLX_H__ - -#include -#include -#include -#include - -G_BEGIN_DECLS - -gboolean -_clutter_backend_glx_handle_event (ClutterBackendX11 *backend, - XEvent *xevent); - -G_END_DECLS - -#endif /* __CLUTTER_EVENT_GLX_H__ */ - diff --git a/clutter/glx/clutter-stage-glx.c b/clutter/glx/clutter-stage-glx.c index ae3c8cda1..e0954b2e9 100644 --- a/clutter/glx/clutter-stage-glx.c +++ b/clutter/glx/clutter-stage-glx.c @@ -30,6 +30,7 @@ #include "clutter-actor-private.h" #include "clutter-debug.h" +#include "clutter-device-manager.h" #include "clutter-event.h" #include "clutter-enum-types.h" #include "clutter-feature.h" @@ -51,7 +52,8 @@ #include #endif -static void clutter_stage_window_iface_init (ClutterStageWindowIface *iface); +static void clutter_stage_window_iface_init (ClutterStageWindowIface *iface); +static void clutter_event_translator_iface_init (ClutterEventTranslatorIface *iface); static ClutterStageWindowIface *clutter_stage_glx_parent_iface = NULL; @@ -59,7 +61,9 @@ G_DEFINE_TYPE_WITH_CODE (ClutterStageGLX, _clutter_stage_glx, CLUTTER_TYPE_STAGE_X11, G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_STAGE_WINDOW, - clutter_stage_window_iface_init)); + clutter_stage_window_iface_init) + G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_EVENT_TRANSLATOR, + clutter_event_translator_iface_init)); static void clutter_stage_glx_unrealize (ClutterStageWindow *stage_window) @@ -70,7 +74,7 @@ clutter_stage_glx_unrealize (ClutterStageWindow *stage_window) ClutterStageGLX *stage_glx = CLUTTER_STAGE_GLX (stage_window); /* Note unrealize should free up any backend stage related resources */ - CLUTTER_NOTE (BACKEND, "Unrealizing stage"); + CLUTTER_NOTE (BACKEND, "Unrealizing GLX stage [%p]", stage_glx); clutter_x11_trap_x_errors (); @@ -80,19 +84,11 @@ clutter_stage_glx_unrealize (ClutterStageWindow *stage_window) stage_glx->glxwin = None; } - if (!stage_x11->is_foreign_xwin && stage_x11->xwin != None) - { - XDestroyWindow (backend_x11->xdpy, stage_x11->xwin); - stage_x11->xwin = None; - } - else - stage_x11->xwin = None; + _clutter_stage_x11_destroy_window_untrapped (stage_x11); XSync (backend_x11->xdpy, False); clutter_x11_untrap_x_errors (); - - CLUTTER_MARK (); } static gboolean @@ -103,7 +99,6 @@ clutter_stage_glx_realize (ClutterStageWindow *stage_window) ClutterBackendX11 *backend_x11; ClutterBackendGLX *backend_glx; ClutterBackend *backend; - int event_flags; CLUTTER_NOTE (ACTOR, "Realizing stage '%s' [%p]", G_OBJECT_TYPE_NAME (stage_window), @@ -113,62 +108,8 @@ clutter_stage_glx_realize (ClutterStageWindow *stage_window) backend_glx = CLUTTER_BACKEND_GLX (backend); backend_x11 = CLUTTER_BACKEND_X11 (backend); - if (stage_x11->xwin == None) - { - XSetWindowAttributes xattr; - unsigned long mask; - XVisualInfo *xvisinfo; - gfloat width, height; - - CLUTTER_NOTE (MISC, "Creating stage X window"); - - xvisinfo = clutter_backend_x11_get_visual_info (backend_x11); - if (xvisinfo == NULL) - { - g_critical ("Unable to find suitable GL visual."); - return FALSE; - } - - /* window attributes */ - xattr.background_pixel = WhitePixel (backend_x11->xdpy, - backend_x11->xscreen_num); - xattr.border_pixel = 0; - xattr.colormap = XCreateColormap (backend_x11->xdpy, - backend_x11->xwin_root, - xvisinfo->visual, - AllocNone); - mask = CWBorderPixel | CWColormap; - - /* Call get_size - this will either get the geometry size (which - * before we create the window is set to 640x480), or if a size - * is set, it will get that. This lets you set a size on the - * stage before it's realized. - */ - clutter_actor_get_size (CLUTTER_ACTOR (stage_x11->wrapper), - &width, - &height); - stage_x11->xwin_width = (gint)width; - stage_x11->xwin_height = (gint)height; - - stage_x11->xwin = XCreateWindow (backend_x11->xdpy, - backend_x11->xwin_root, - 0, 0, - stage_x11->xwin_width, - stage_x11->xwin_height, - 0, - xvisinfo->depth, - InputOutput, - xvisinfo->visual, - mask, &xattr); - - CLUTTER_NOTE (BACKEND, "Stage [%p], window: 0x%x, size: %dx%d", - stage_window, - (unsigned int) stage_x11->xwin, - stage_x11->xwin_width, - stage_x11->xwin_height); - - XFree (xvisinfo); - } + if (!_clutter_stage_x11_create_window (stage_x11)) + return FALSE; if (stage_glx->glxwin == None) { @@ -190,52 +131,12 @@ clutter_stage_glx_realize (ClutterStageWindow *stage_window) } } - /* the masks for the events we want to select on a stage window; - * KeyPressMask and KeyReleaseMask are necessary even with XI1 - * because key events are broken with that extension, and will - * be fixed by XI2 - */ - event_flags = StructureNotifyMask - | FocusChangeMask - | ExposureMask - | PropertyChangeMask - | EnterWindowMask - | LeaveWindowMask - | KeyPressMask - | KeyReleaseMask; - - /* if we don't use XI1 then we also want core pointer events */ - if (!clutter_x11_has_xinput ()) - event_flags |= (ButtonPressMask | ButtonReleaseMask | PointerMotionMask); -#ifdef HAVE_XINPUT - else - _clutter_x11_select_events (stage_x11->xwin); -#endif - - /* we unconditionally select input events even with event retrieval - * disabled because we need to guarantee that the Clutter internal - * state is maintained when calling clutter_x11_handle_event() without - * requiring applications or embedding toolkits to select events - * themselves. if we did that, we'd have to document the events to be - * selected, and also update applications and embedding toolkits each - * time we added a new mask, or a new class of events. - * - * see: http://bugzilla.clutter-project.org/show_bug.cgi?id=998 - * for the rationale of why we did conditional selection. it is now - * clear that a compositor should clear out the input region, since - * it cannot assume a perfectly clean slate coming from us. - * - * see: http://bugzilla.clutter-project.org/show_bug.cgi?id=2228 - * for an example of things that break if we do conditional event - * selection. - */ - XSelectInput (backend_x11->xdpy, stage_x11->xwin, event_flags); - #ifdef GLX_INTEL_swap_event if (clutter_feature_available (CLUTTER_FEATURE_SWAP_EVENTS)) { - GLXDrawable drawable = - stage_glx->glxwin ? stage_glx->glxwin : stage_x11->xwin; + GLXDrawable drawable = stage_glx->glxwin + ? stage_glx->glxwin + : stage_x11->xwin; /* similarly to above, we unconditionally select this event * because we rely on it to advance the master clock, and @@ -247,14 +148,6 @@ clutter_stage_glx_realize (ClutterStageWindow *stage_window) } #endif /* GLX_INTEL_swap_event */ - /* no user resize.. */ - clutter_stage_x11_fix_window_size (stage_x11, - stage_x11->xwin_width, - stage_x11->xwin_height); - clutter_stage_x11_set_wm_protocols (stage_x11); - - CLUTTER_NOTE (BACKEND, "Successfully realized stage"); - /* chain up to the StageX11 implementation */ return clutter_stage_glx_parent_iface->realize (stage_window); } @@ -439,6 +332,57 @@ clutter_stage_window_iface_init (ClutterStageWindowIface *iface) /* the rest is inherited from ClutterStageX11 */ } +static ClutterEventTranslatorIface *event_translator_parent_iface = NULL; + +static ClutterTranslateReturn +clutter_stage_glx_translate_event (ClutterEventTranslator *translator, + gpointer native, + ClutterEvent *event) +{ +#ifdef GLX_INTEL_swap_event + ClutterBackendGLX *backend_glx; + XEvent *xevent = native; + + backend_glx = CLUTTER_BACKEND_GLX (clutter_get_default_backend ()); + + if (xevent->type == (backend_glx->event_base + GLX_BufferSwapComplete)) + { + ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (translator); + ClutterStageGLX *stage_glx = CLUTTER_STAGE_GLX (translator); + GLXBufferSwapComplete *swap_complete_event; + + swap_complete_event = (GLXBufferSwapComplete *) xevent; + + if (stage_x11->xwin == swap_complete_event->drawable) + { + /* Early versions of the swap_event implementation in Mesa + * deliver BufferSwapComplete event when not selected for, + * so if we get a swap event we aren't expecting, just ignore it. + * + * https://bugs.freedesktop.org/show_bug.cgi?id=27962 + */ + if (stage_glx->pending_swaps > 0) + stage_glx->pending_swaps--; + + return CLUTTER_TRANSLATE_REMOVE; + } + } +#endif + + /* chain up to the common X11 implementation */ + return event_translator_parent_iface->translate_event (translator, + native, + event); +} + +static void +clutter_event_translator_iface_init (ClutterEventTranslatorIface *iface) +{ + event_translator_parent_iface = g_type_interface_peek_parent (iface); + + iface->translate_event = clutter_stage_glx_translate_event; +} + #ifdef HAVE_DRM static int drm_wait_vblank(int fd, drm_wait_vblank_t *vbl) @@ -491,7 +435,7 @@ wait_for_vblank (ClutterBackendGLX *backend_glx) void _clutter_stage_glx_redraw (ClutterStageGLX *stage_glx, - ClutterStage *stage) + ClutterStage *stage) { ClutterBackend *backend; ClutterBackendX11 *backend_x11; diff --git a/clutter/x11/clutter-backend-x11.c b/clutter/x11/clutter-backend-x11.c index 5efb1150d..428644b2d 100644 --- a/clutter/x11/clutter-backend-x11.c +++ b/clutter/x11/clutter-backend-x11.c @@ -38,8 +38,8 @@ #include #include "clutter-backend-x11.h" -#include "clutter-device-manager-x11.h" -#include "clutter-input-device-x11.h" +#include "clutter-device-manager-core-x11.h" +#include "clutter-device-manager-xi2.h" #include "clutter-settings-x11.h" #include "clutter-stage-x11.h" #include "clutter-x11.h" @@ -54,12 +54,16 @@ #include #endif +#if HAVE_XINPUT_2 +#include +#endif + #include "cogl/cogl.h" #include "cogl/cogl-internal.h" #include "clutter-backend.h" #include "clutter-debug.h" -#include "clutter-device-manager.h" +#include "clutter-device-manager-private.h" #include "clutter-event.h" #include "clutter-main.h" #include "clutter-private.h" @@ -120,6 +124,30 @@ xsettings_filter (XEvent *xevent, return CLUTTER_X11_FILTER_CONTINUE; } +static ClutterX11FilterReturn +cogl_xlib_filter (XEvent *xevent, + ClutterEvent *event, + gpointer data) +{ + CoglXlibFilterReturn ret; + ClutterX11FilterReturn retval; + + ret = _cogl_xlib_handle_event (xevent); + switch (ret) + { + case COGL_XLIB_FILTER_REMOVE: + retval = CLUTTER_X11_FILTER_REMOVE; + break; + + case COGL_XLIB_FILTER_CONTINUE: + default: + retval = CLUTTER_X11_FILTER_CONTINUE; + break; + } + + return retval; +} + static void clutter_backend_x11_xsettings_notify (const char *name, XSettingsAction action, @@ -192,6 +220,62 @@ clutter_backend_x11_xsettings_notify (const char *name, g_object_thaw_notify (G_OBJECT (settings)); } +static void +clutter_backend_x11_create_device_manager (ClutterBackendX11 *backend_x11) +{ + if (G_UNLIKELY (backend_x11->device_manager == NULL)) + { + ClutterEventTranslator *translator; + +#if defined(HAVE_XINPUT) || defined(HAVE_XINPUT_2) + if (clutter_enable_xinput) + { + int event_base, first_event, first_error; + + if (XQueryExtension (backend_x11->xdpy, "XInputExtension", + &event_base, + &first_event, + &first_error)) + { + int major = 2; + int minor = 0; + + if (XIQueryVersion (backend_x11->xdpy, &major, &minor) != BadRequest) + { + CLUTTER_NOTE (BACKEND, "Creating XI2 device manager"); + + backend_x11->device_manager = + g_object_new (CLUTTER_TYPE_DEVICE_MANAGER_XI2, + "backend", backend_x11, + "opcode", event_base, + NULL); + } + else + { + CLUTTER_NOTE (BACKEND, "Creating Core+XI device manager"); + backend_x11->device_manager = + g_object_new (CLUTTER_TYPE_DEVICE_MANAGER_X11, + "backend", backend_x11, + "event-base", first_event, + NULL); + } + } + } + else +#endif /* HAVE_XINPUT || HAVE_XINPUT_2 */ + { + CLUTTER_NOTE (BACKEND, "Creating Core device manager"); + backend_x11->device_manager = + g_object_new (CLUTTER_TYPE_DEVICE_MANAGER_X11, + "backend", backend_x11, + NULL); + } + + translator = CLUTTER_EVENT_TRANSLATOR (backend_x11->device_manager); + _clutter_backend_x11_add_event_translator (backend_x11, translator); + } +} + gboolean clutter_backend_x11_pre_parse (ClutterBackend *backend, GError **error) @@ -215,6 +299,13 @@ clutter_backend_x11_pre_parse (ClutterBackend *backend, env_string = NULL; } + env_string = g_getenv ("CLUTTER_ENABLE_XINPUT"); + if (env_string) + { + clutter_enable_xinput = TRUE; + env_string = NULL; + } + return TRUE; } @@ -271,6 +362,9 @@ clutter_backend_x11_post_parse (ClutterBackend *backend, CoglTexturePixmapX11 */ _cogl_xlib_set_display (backend_x11->xdpy); + /* add event filter for Cogl events */ + clutter_x11_add_filter (cogl_xlib_filter, NULL); + if (clutter_screen == -1) backend_x11->xscreen = DefaultScreenOfDisplay (backend_x11->xdpy); else @@ -278,6 +372,8 @@ clutter_backend_x11_post_parse (ClutterBackend *backend, clutter_screen); backend_x11->xscreen_num = XScreenNumberOfScreen (backend_x11->xscreen); + backend_x11->xscreen_width = WidthOfScreen (backend_x11->xscreen); + backend_x11->xscreen_height = HeightOfScreen (backend_x11->xscreen); backend_x11->xwin_root = RootWindow (backend_x11->xdpy, backend_x11->xscreen_num); @@ -289,12 +385,8 @@ clutter_backend_x11_post_parse (ClutterBackend *backend, g_object_set (settings, "font-dpi", (int) dpi * 1024, NULL); - /* register input devices */ - backend_x11->device_manager = - g_object_new (CLUTTER_TYPE_DEVICE_MANAGER_X11, - "use-xinput-1", clutter_enable_xinput, - "backend", backend_x11, - NULL); + /* create the device manager */ + clutter_backend_x11_create_device_manager (backend_x11); /* register keymap */ backend_x11->keymap = @@ -310,6 +402,7 @@ clutter_backend_x11_post_parse (ClutterBackend *backend, NULL, backend_x11); + /* add event filter for XSETTINGS events */ clutter_x11_add_filter (xsettings_filter, backend_x11); if (clutter_synchronise) @@ -374,7 +467,7 @@ static const GOptionEntry entries[] = G_OPTION_ARG_NONE, &clutter_synchronise, N_("Make X calls synchronous"), NULL }, -#ifdef HAVE_XINPUT +#if defined(HAVE_XINPUT) || defined(HAVE_XINPUT_2) { "enable-xinput", 0, 0, @@ -480,36 +573,128 @@ clutter_backend_x11_free_event_data (ClutterBackend *backend, _clutter_event_x11_free (event_x11); } -gboolean -clutter_backend_x11_handle_event (ClutterBackendX11 *backend_x11, - XEvent *xevent) -{ - return FALSE; -} - static ClutterDeviceManager * clutter_backend_x11_get_device_manager (ClutterBackend *backend) { ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (backend); - if (G_UNLIKELY (backend_x11->device_manager == NULL)) - { - backend_x11->device_manager = - g_object_new (CLUTTER_TYPE_DEVICE_MANAGER_X11, - "use-xinput-1", clutter_enable_xinput, - "backend", backend_x11, - NULL); - } + clutter_backend_x11_create_device_manager (backend_x11); return backend_x11->device_manager; } +static void +update_last_event_time (ClutterBackendX11 *backend_x11, + XEvent *xevent) +{ + Time current_time = CurrentTime; + Time last_time = backend_x11->last_event_time; + + switch (xevent->type) + { + case KeyPress: + case KeyRelease: + current_time = xevent->xkey.time; + break; + + case ButtonPress: + case ButtonRelease: + current_time = xevent->xbutton.time; + break; + + case MotionNotify: + current_time = xevent->xmotion.time; + break; + + case EnterNotify: + case LeaveNotify: + current_time = xevent->xcrossing.time; + break; + + case PropertyNotify: + current_time = xevent->xproperty.time; + break; + + default: + break; + } + + /* only change the current event time if it's after the previous event + * time, or if it is at least 30 seconds earlier - in case the system + * clock was changed + */ + if ((current_time != CurrentTime) && + (current_time > last_time || (last_time - current_time > (30 * 1000)))) + backend_x11->last_event_time = current_time; +} + +static gboolean +clutter_backend_x11_translate_event (ClutterBackend *backend, + gpointer native, + ClutterEvent *event) +{ + ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (backend); + XEvent *xevent = native; + GList *l; + + if (backend_x11->event_filters) + { + GSList *node = backend_x11->event_filters; + + while (node != NULL) + { + ClutterX11EventFilter *filter = node->data; + + switch (filter->func (xevent, event, filter->data)) + { + case CLUTTER_X11_FILTER_CONTINUE: + break; + + case CLUTTER_X11_FILTER_TRANSLATE: + return TRUE; + + case CLUTTER_X11_FILTER_REMOVE: + return FALSE; + + default: + break; + } + + node = node->next; + } + } + + /* we update the event time only for events that can + * actually reach Clutter's event queue + */ + update_last_event_time (backend_x11, xevent); + + for (l = backend_x11->event_translators; + l != NULL; + l = l->next) + { + ClutterEventTranslator *translator = l->data; + ClutterTranslateReturn retval; + + retval = _clutter_event_translator_translate_event (translator, + native, + event); + + if (retval == CLUTTER_TRANSLATE_QUEUE) + return TRUE; + + if (retval == CLUTTER_TRANSLATE_REMOVE) + return FALSE; + } + + return FALSE; +} + static void clutter_backend_x11_class_init (ClutterBackendX11Class *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterBackendClass *backend_class = CLUTTER_BACKEND_CLASS (klass); - ClutterBackendX11Class *backendx11_class = CLUTTER_BACKEND_X11_CLASS (klass); gobject_class->constructor = clutter_backend_x11_constructor; gobject_class->dispose = clutter_backend_x11_dispose; @@ -523,8 +708,7 @@ clutter_backend_x11_class_init (ClutterBackendX11Class *klass) backend_class->get_device_manager = clutter_backend_x11_get_device_manager; backend_class->copy_event_data = clutter_backend_x11_copy_event_data; backend_class->free_event_data = clutter_backend_x11_free_event_data; - - backendx11_class->handle_event = clutter_backend_x11_handle_event; + backend_class->translate_event = clutter_backend_x11_translate_event; } static void @@ -634,18 +818,12 @@ clutter_x11_set_display (Display *xdpy) * want to use clutter_x11_has_xinput() to see if support was enabled. * * Since: 0.8 + * + * Deprecated: 1.6: This function does not do anything. */ void clutter_x11_enable_xinput (void) { - if (_clutter_context_is_initialized ()) - { - g_critical ("clutter_x11_enable_xinput() can only be called " - "before clutter_init()"); - return; - } - - clutter_enable_xinput = TRUE; } /** @@ -815,33 +993,6 @@ clutter_x11_remove_filter (ClutterX11FilterFunc func, } } -void -_clutter_x11_select_events (Window xwin) -{ -#ifdef HAVE_XINPUT - ClutterDeviceManager *manager; - const GSList *l; - - if (G_UNLIKELY (backend_singleton == NULL)) - { - g_critical ("X11 backend has not been initialised"); - - return; - } - - manager = clutter_device_manager_get_default (); - - for (l = clutter_device_manager_peek_devices (manager); - l != NULL; - l = l->next) - { - ClutterInputDevice *device = l->data; - - _clutter_input_device_x11_select_events (device, backend_singleton, xwin); - } -#endif /* HAVE_XINPUT */ -} - ClutterInputDevice * _clutter_x11_get_device_for_xid (XID id) { @@ -884,18 +1035,14 @@ clutter_x11_get_input_devices (void) * and XInput support is available at run time. * * Since: 0.8 + * + * Deprecated: 1.6 */ gboolean clutter_x11_has_xinput (void) { -#ifdef HAVE_XINPUT - if (backend_singleton == NULL) - { - g_critical ("X11 backend has not been initialised"); - return FALSE; - } - - return backend_singleton->have_xinput; +#if defined(HAVE_XINPUT) || defined(HAVE_XINPUT_2) + return TRUE; #else return FALSE; #endif @@ -1031,3 +1178,76 @@ clutter_x11_get_visual_info (void) return clutter_backend_x11_get_visual_info (backend_x11); } + +void +_clutter_backend_x11_add_event_translator (ClutterBackendX11 *backend_x11, + ClutterEventTranslator *translator) +{ + if (g_list_find (backend_x11->event_translators, translator) != NULL) + return; + + backend_x11->event_translators = + g_list_prepend (backend_x11->event_translators, translator); +} + +void +_clutter_backend_x11_remove_event_translator (ClutterBackendX11 *backend_x11, + ClutterEventTranslator *translator) +{ + if (g_list_find (backend_x11->event_translators, translator) == NULL) + return; + + backend_x11->event_translators = + g_list_remove (backend_x11->event_translators, translator); +} + +gboolean +_clutter_x11_input_device_translate_screen_coord (ClutterInputDevice *device, + gint stage_root_x, + gint stage_root_y, + guint index_, + gdouble value, + gdouble *axis_value) +{ + ClutterAxisInfo *info; + ClutterBackendX11 *backend_x11; + gdouble width, scale, offset; + + backend_x11 = CLUTTER_BACKEND_X11 (device->backend); + + if (device->axes == NULL || index_ >= device->axes->len) + return FALSE; + + info = &g_array_index (device->axes, ClutterAxisInfo, index_); + if (info->axis != CLUTTER_INPUT_AXIS_X || + info->axis != CLUTTER_INPUT_AXIS_Y) + { + return FALSE; + } + + width = info->max_value - info->min_value; + + if (info->axis == CLUTTER_INPUT_AXIS_X) + { + if (width > 0) + scale = backend_x11->xscreen_width / width; + else + scale = 1; + + offset = - stage_root_x; + } + else + { + if (width > 0) + scale = backend_x11->xscreen_height / width; + else + scale = 1; + + offset = - stage_root_y; + } + + if (axis_value) + *axis_value = offset + scale * (value - info->min_value); + + return TRUE; +} diff --git a/clutter/x11/clutter-backend-x11.h b/clutter/x11/clutter-backend-x11.h index 1947803a7..5d710d713 100644 --- a/clutter/x11/clutter-backend-x11.h +++ b/clutter/x11/clutter-backend-x11.h @@ -28,8 +28,11 @@ #include #include "clutter-x11.h" + #include "clutter-backend-private.h" +#include "clutter-event-translator.h" #include "clutter-keymap-x11.h" + #include "xsettings/xsettings-client.h" G_BEGIN_DECLS @@ -43,23 +46,39 @@ G_BEGIN_DECLS typedef struct _ClutterBackendX11 ClutterBackendX11; typedef struct _ClutterBackendX11Class ClutterBackendX11Class; +typedef struct _ClutterEventX11 ClutterEventX11; +typedef struct _ClutterX11EventFilter ClutterX11EventFilter; -typedef struct _ClutterX11EventFilter +struct _ClutterX11EventFilter { ClutterX11FilterFunc func; gpointer data; -} ClutterX11EventFilter; +}; + +struct _ClutterEventX11 +{ + /* additional fields for Key events */ + gint key_group; + + guint key_is_modifier : 1; + guint num_lock_set : 1; + guint caps_lock_set : 1; +}; struct _ClutterBackendX11 { ClutterBackend parent_instance; Display *xdpy; - Window xwin_root; + gchar *display_name; + Screen *xscreen; int xscreen_num; - gchar *display_name; + int xscreen_width; + int xscreen_height; + + Window xwin_root; /* event source */ GSource *event_source; @@ -93,6 +112,8 @@ struct _ClutterBackendX11 int xkb_event_base; gboolean use_xkb; gboolean have_xkb_autorepeat; + + GList *event_translators; }; struct _ClutterBackendX11Class @@ -105,18 +126,8 @@ struct _ClutterBackendX11Class * may need to be handled differently for different backends. */ XVisualInfo *(* get_visual_info) (ClutterBackendX11 *backend); - - /* - * Different X11 backends may care about some special events so they all have - * a chance to intercept them. - */ - gboolean (*handle_event) (ClutterBackendX11 *backend, - XEvent *xevent); }; -/* platform-specific event data */ -typedef struct _ClutterEventX11 ClutterEventX11; - void _clutter_backend_x11_events_init (ClutterBackend *backend); void _clutter_backend_x11_events_uninit (ClutterBackend *backend); @@ -163,6 +174,22 @@ _clutter_event_x11_copy (ClutterEventX11 *event_x11); void _clutter_event_x11_free (ClutterEventX11 *event_x11); +void +_clutter_backend_x11_add_event_translator (ClutterBackendX11 *backend_x11, + ClutterEventTranslator *translator); + +void +_clutter_backend_x11_remove_event_translator (ClutterBackendX11 *backend_x11, + ClutterEventTranslator *translator); + +gboolean +_clutter_x11_input_device_translate_screen_coord (ClutterInputDevice *device, + gint stage_root_x, + gint stage_root_y, + guint index_, + gdouble value, + gdouble *axis_value); + G_END_DECLS #endif /* __CLUTTER_BACKEND_X11_H__ */ diff --git a/clutter/x11/clutter-device-manager-core-x11.c b/clutter/x11/clutter-device-manager-core-x11.c new file mode 100644 index 000000000..8d005c63f --- /dev/null +++ b/clutter/x11/clutter-device-manager-core-x11.c @@ -0,0 +1,745 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Copyright (C) 2009 Intel Corp. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Author: Emmanuele Bassi + */ + +#include "config.h" + +#include "clutter-device-manager-core-x11.h" + +#include "clutter-backend-x11.h" +#include "clutter-input-device-core-x11.h" +#include "clutter-stage-x11.h" + +#include "clutter-backend.h" +#include "clutter-debug.h" +#include "clutter-device-manager-private.h" +#include "clutter-event-translator.h" +#include "clutter-stage-private.h" +#include "clutter-private.h" + +#ifdef HAVE_XINPUT +#include + +/* old versions of XI.h don't define these */ +#ifndef IsXExtensionKeyboard +#define IsXExtensionKeyboard 3 +#define IsXExtensionPointer 4 +#endif + +#endif /* HAVE_XINPUT */ + +enum +{ + PROP_0, + + PROP_EVENT_BASE, + + PROP_LAST +}; + +static GParamSpec *obj_props[PROP_LAST] = { NULL, }; + +static void clutter_event_translator_iface_init (ClutterEventTranslatorIface *iface); + +G_DEFINE_TYPE_WITH_CODE (ClutterDeviceManagerX11, + clutter_device_manager_x11, + CLUTTER_TYPE_DEVICE_MANAGER, + G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_EVENT_TRANSLATOR, + clutter_event_translator_iface_init)); + +#ifdef HAVE_XINPUT +static void +translate_class_info (ClutterInputDevice *device, + XDeviceInfo *info) +{ + XAnyClassPtr any_class; + gint i; + + any_class = info->inputclassinfo; + + for (i = 0; i < info->num_classes; i++) + { + switch (any_class->class) + { + case ButtonClass: + break; + + case KeyClass: + { + XKeyInfo *xk_info = (XKeyInfo *) any_class; + guint n_keys; + + n_keys = xk_info->max_keycode - xk_info->min_keycode + 1; + _clutter_input_device_set_keys (device, n_keys, + xk_info->min_keycode, + xk_info->max_keycode); + } + break; + + case ValuatorClass: + { + XValuatorInfo *xv_info = (XValuatorInfo *) any_class; + gint j; + + for (j = 0; j < xv_info->num_axes; j++) + { + ClutterInputAxis axis; + + switch (j) + { + case 0: + axis = CLUTTER_INPUT_AXIS_X; + break; + + case 1: + axis = CLUTTER_INPUT_AXIS_Y; + break; + + case 2: + axis = CLUTTER_INPUT_AXIS_PRESSURE; + break; + + case 3: + axis = CLUTTER_INPUT_AXIS_XTILT; + break; + + case 4: + axis = CLUTTER_INPUT_AXIS_YTILT; + break; + + case 5: + axis = CLUTTER_INPUT_AXIS_WHEEL; + break; + + default: + axis = CLUTTER_INPUT_AXIS_IGNORE; + break; + } + + _clutter_input_device_add_axis (device, axis, + xv_info->axes[j].min_value, + xv_info->axes[j].max_value, + xv_info->axes[j].resolution); + } + } + break; + } + + any_class = (XAnyClassPtr) (((char *) any_class) + any_class->length); + } +} + +static ClutterInputDevice * +create_device (ClutterDeviceManagerX11 *manager_x11, + ClutterBackendX11 *backend_x11, + XDeviceInfo *info) +{ + ClutterInputDeviceType source; + ClutterInputDevice *retval; + + if (info->use != IsXExtensionPointer && + info->use != IsXExtensionKeyboard) + return NULL; + + if (info->use == IsXExtensionKeyboard) + source = CLUTTER_KEYBOARD_DEVICE; + else + { + gchar *name; + + name = g_ascii_strdown (info->name, -1); + + if (strstr (name, "eraser") != NULL) + source = CLUTTER_ERASER_DEVICE; + else if (strstr (name, "cursor") != NULL) + source = CLUTTER_CURSOR_DEVICE; + else if (strstr (name, "wacom") != NULL || strstr (name, "pen") != NULL) + source = CLUTTER_PEN_DEVICE; + else + source = CLUTTER_POINTER_DEVICE; + + g_free (name); + } + + retval = g_object_new (CLUTTER_TYPE_INPUT_DEVICE_X11, + "name", info->name, + "id", info->id, + "has-cursor", FALSE, + "device-manager", manager_x11, + "device-type", source, + "device-mode", CLUTTER_INPUT_MODE_FLOATING, + "backend", backend_x11, + NULL); + translate_class_info (retval, info); + + CLUTTER_NOTE (BACKEND, + "XI Device '%s' (id: %d) created", + info->name, + (int) info->id); + + return retval; +} +#endif /* HAVE_XINPUT */ + +static inline void +translate_key_event (ClutterBackendX11 *backend_x11, + ClutterDeviceManagerX11 *manager_x11, + ClutterEvent *event, + XEvent *xevent) +{ + ClutterEventX11 *event_x11; + char buffer[256 + 1]; + int n; + + event->key.type = xevent->xany.type == KeyPress ? CLUTTER_KEY_PRESS + : CLUTTER_KEY_RELEASE; + event->key.time = xevent->xkey.time; + event->key.device = manager_x11->core_keyboard; + + /* KeyEvents have platform specific data associated to them */ + event_x11 = _clutter_event_x11_new (); + _clutter_event_set_platform_data (event, event_x11); + + event->key.modifier_state = (ClutterModifierType) xevent->xkey.state; + event->key.hardware_keycode = xevent->xkey.keycode; + + /* keyval is the key ignoring all modifiers ('1' vs. '!') */ + event->key.keyval = + _clutter_keymap_x11_translate_key_state (backend_x11->keymap, + event->key.hardware_keycode, + event->key.modifier_state, + NULL); + + event_x11->key_group = + _clutter_keymap_x11_get_key_group (backend_x11->keymap, + event->key.modifier_state); + event_x11->key_is_modifier = + _clutter_keymap_x11_get_is_modifier (backend_x11->keymap, + event->key.hardware_keycode); + event_x11->num_lock_set = + _clutter_keymap_x11_get_num_lock_state (backend_x11->keymap); + event_x11->caps_lock_set = + _clutter_keymap_x11_get_caps_lock_state (backend_x11->keymap); + + /* unicode_value is the printable representation */ + n = XLookupString (&xevent->xkey, buffer, sizeof (buffer) - 1, NULL, NULL); + + if (n != NoSymbol) + { + event->key.unicode_value = g_utf8_get_char_validated (buffer, n); + if ((event->key.unicode_value != -1) && + (event->key.unicode_value != -2)) + goto out; + } + else + event->key.unicode_value = (gunichar)'\0'; + +out: + CLUTTER_NOTE (EVENT, + "%s: win:0x%x, key: %12s (%d)", + event->any.type == CLUTTER_KEY_PRESS + ? "key press " + : "key release", + (unsigned int) xevent->xkey.window, + event->key.keyval ? buffer : "(none)", + event->key.keyval); + return; +} + +#ifdef HAVE_XINPUT +static ClutterInputDevice * +get_device_from_event (ClutterDeviceManagerX11 *manager_x11, + XEvent *xevent) +{ + guint32 device_id; + + device_id = ((XDeviceButtonEvent *) xevent)->deviceid; + + return g_hash_table_lookup (manager_x11->devices_by_id, + GINT_TO_POINTER (device_id)); +} +#endif /* HAVE_XINPUT */ + +static ClutterTranslateReturn +clutter_device_manager_x11_translate_event (ClutterEventTranslator *translator, + gpointer native, + ClutterEvent *event) +{ + ClutterDeviceManagerX11 *manager_x11; + ClutterBackendX11 *backend_x11; + ClutterInputDevice *device; + ClutterStageX11 *stage_x11; + ClutterTranslateReturn res; + ClutterStage *stage; + XEvent *xevent; + + manager_x11 = CLUTTER_DEVICE_MANAGER_X11 (translator); + backend_x11 = CLUTTER_BACKEND_X11 (clutter_get_default_backend ()); + device = NULL; + + xevent = native; + + stage = clutter_x11_get_stage_from_window (xevent->xany.window); + if (stage == NULL) + return CLUTTER_TRANSLATE_CONTINUE; + + if (CLUTTER_ACTOR_IN_DESTRUCTION (stage)) + return CLUTTER_TRANSLATE_CONTINUE; + + stage_x11 = CLUTTER_STAGE_X11 (_clutter_stage_get_window (stage)); + + event->any.stage = stage; + + res = CLUTTER_TRANSLATE_CONTINUE; + +#ifdef HAVE_XINPUT + device = get_device_from_event (manager_x11, xevent); + if (device != NULL) + { + ClutterInputDeviceX11 *device_x11; + gboolean retval; + + device_x11 = CLUTTER_INPUT_DEVICE_X11 (device); + retval = _clutter_input_device_x11_translate_xi_event (device_x11, + stage_x11, + xevent, + event); + if (retval) + return CLUTTER_TRANSLATE_QUEUE; + } +#endif /* HAVE_XINPUT */ + + switch (xevent->type) + { + case KeyPress: + translate_key_event (backend_x11, manager_x11, event, xevent); + _clutter_stage_x11_set_user_time (stage_x11, xevent->xkey.time); + res = CLUTTER_TRANSLATE_QUEUE; + break; + + case KeyRelease: + /* old-style X11 terminals require that even modern X11 send + * KeyPress/KeyRelease pairs when auto-repeating. for this + * reason modern(-ish) API like XKB has a way to detect + * auto-repeat and do a single KeyRelease at the end of a + * KeyPress sequence. + * + * this check emulates XKB's detectable auto-repeat; we peek + * the next event and check if it's a KeyPress for the same key + * and timestamp - and then ignore it if it matches the + * KeyRelease + * + * if we have XKB, and autorepeat is enabled, then this becomes + * a no-op + */ + if (!backend_x11->have_xkb_autorepeat && XPending (xevent->xkey.display)) + { + XEvent next_event; + + XPeekEvent (xevent->xkey.display, &next_event); + + if (next_event.type == KeyPress && + next_event.xkey.keycode == xevent->xkey.keycode && + next_event.xkey.time == xevent->xkey.time) + { + res = CLUTTER_TRANSLATE_REMOVE; + break; + } + } + + translate_key_event (backend_x11, manager_x11, event, xevent); + res = CLUTTER_TRANSLATE_QUEUE; + break; + + case ButtonPress: + CLUTTER_NOTE (EVENT, + "button press: win: 0x%x, coords: %d, %d, button: %d", + (unsigned int) stage_x11->xwin, + xevent->xbutton.x, + xevent->xbutton.y, + xevent->xbutton.button); + + switch (xevent->xbutton.button) + { + case 4: /* up */ + case 5: /* down */ + case 6: /* left */ + case 7: /* right */ + event->scroll.type = CLUTTER_SCROLL; + + if (xevent->xbutton.button == 4) + event->scroll.direction = CLUTTER_SCROLL_UP; + else if (xevent->xbutton.button == 5) + event->scroll.direction = CLUTTER_SCROLL_DOWN; + else if (xevent->xbutton.button == 6) + event->scroll.direction = CLUTTER_SCROLL_LEFT; + else + event->scroll.direction = CLUTTER_SCROLL_RIGHT; + + event->scroll.time = xevent->xbutton.time; + event->scroll.x = xevent->xbutton.x; + event->scroll.y = xevent->xbutton.y; + event->scroll.modifier_state = xevent->xbutton.state; + event->scroll.device = manager_x11->core_pointer; + break; + + default: + event->button.type = event->type = CLUTTER_BUTTON_PRESS; + event->button.time = xevent->xbutton.time; + event->button.x = xevent->xbutton.x; + event->button.y = xevent->xbutton.y; + event->button.modifier_state = xevent->xbutton.state; + event->button.button = xevent->xbutton.button; + event->button.device = manager_x11->core_pointer; + event->button.axes = NULL; + break; + } + + _clutter_stage_x11_set_user_time (stage_x11, xevent->xbutton.time); + res = CLUTTER_TRANSLATE_QUEUE; + break; + + case ButtonRelease: + CLUTTER_NOTE (EVENT, + "button press: win: 0x%x, coords: %d, %d, button: %d", + (unsigned int) stage_x11->xwin, + xevent->xbutton.x, + xevent->xbutton.y, + xevent->xbutton.button); + + /* scroll events don't have a corresponding release */ + if (xevent->xbutton.button == 4 || + xevent->xbutton.button == 5 || + xevent->xbutton.button == 6 || + xevent->xbutton.button == 7) + { + res = CLUTTER_TRANSLATE_REMOVE; + break; + } + + event->button.type = event->type = CLUTTER_BUTTON_RELEASE; + event->button.time = xevent->xbutton.time; + event->button.x = xevent->xbutton.x; + event->button.y = xevent->xbutton.y; + event->button.modifier_state = xevent->xbutton.state; + event->button.button = xevent->xbutton.button; + event->button.axes = NULL; + event->button.device = manager_x11->core_pointer; + res = CLUTTER_TRANSLATE_QUEUE; + break; + + case MotionNotify: + CLUTTER_NOTE (EVENT, + "motion: win: 0x%x, coords: %d, %d", + (unsigned int) stage_x11->xwin, + xevent->xmotion.x, + xevent->xmotion.y); + + event->motion.type = event->type = CLUTTER_MOTION; + event->motion.time = xevent->xmotion.time; + event->motion.x = xevent->xmotion.x; + event->motion.y = xevent->xmotion.y; + event->motion.modifier_state = xevent->xmotion.state; + event->motion.device = manager_x11->core_pointer; + event->motion.axes = NULL; + res = CLUTTER_TRANSLATE_QUEUE; + break; + + case EnterNotify: + /* we know that we are entering the stage here */ + _clutter_input_device_set_stage (manager_x11->core_pointer, stage); + CLUTTER_NOTE (EVENT, "Entering the stage"); + + /* Convert enter notifies to motion events because X + doesn't emit the corresponding motion notify */ + event->motion.type = event->type = CLUTTER_MOTION; + event->motion.time = xevent->xcrossing.time; + event->motion.x = xevent->xcrossing.x; + event->motion.y = xevent->xcrossing.y; + event->motion.modifier_state = xevent->xcrossing.state; + event->motion.source = CLUTTER_ACTOR (stage); + event->motion.device = manager_x11->core_pointer; + res = CLUTTER_TRANSLATE_QUEUE; + break; + + case LeaveNotify: + if (manager_x11->core_pointer->stage == NULL) + { + CLUTTER_NOTE (EVENT, "Discarding LeaveNotify for " + "ButtonRelease event off-stage"); + res = CLUTTER_TRANSLATE_REMOVE; + break; + } + + /* we know that we are leaving the stage here */ + _clutter_input_device_set_stage (manager_x11->core_pointer, NULL); + CLUTTER_NOTE (EVENT, "Leaving the stage (time:%u)", + event->crossing.time); + + event->crossing.type = CLUTTER_LEAVE; + event->crossing.time = xevent->xcrossing.time; + event->crossing.x = xevent->xcrossing.x; + event->crossing.y = xevent->xcrossing.y; + event->crossing.source = CLUTTER_ACTOR (stage); + event->crossing.device = manager_x11->core_pointer; + res = CLUTTER_TRANSLATE_QUEUE; + break; + + default: + break; + } + + return res; +} + +static void +clutter_event_translator_iface_init (ClutterEventTranslatorIface *iface) +{ + iface->translate_event = clutter_device_manager_x11_translate_event; +} + +static void +clutter_device_manager_x11_constructed (GObject *gobject) +{ + ClutterDeviceManagerX11 *manager_x11; + ClutterBackendX11 *backend_x11; + ClutterDeviceManager *manager; +#ifdef HAVE_XINPUT + XDeviceInfo *x_devices = NULL; + int i, n_devices; +#endif /* HAVE_XINPUT */ + + manager = CLUTTER_DEVICE_MANAGER (gobject); + manager_x11 = CLUTTER_DEVICE_MANAGER_X11 (gobject); + + g_object_get (gobject, "backend", &backend_x11, NULL); + g_assert (backend_x11 != NULL); + +#ifdef HAVE_XINPUT + x_devices = XListInputDevices (backend_x11->xdpy, &n_devices); + if (n_devices == 0) + { + CLUTTER_NOTE (BACKEND, "No XInput devices found"); + goto default_device; + } + + for (i = 0; i < n_devices; i++) + { + XDeviceInfo *info = x_devices + i; + ClutterInputDevice *device; + + CLUTTER_NOTE (BACKEND, + "Considering device %li with type %d, %d of %d", + info->id, + info->use, + i, n_devices); + + device = create_device (manager_x11, backend_x11, info); + if (device != NULL) + _clutter_device_manager_add_device (manager, device); + } + + XFreeDeviceList (x_devices); + +default_device: +#endif /* HAVE_XINPUT */ + + /* fallback code in case: + * + * - we do not have XInput support compiled in + * - we do not have the XInput extension + * + * we register two default devices, one for the pointer + * and one for the keyboard. this block must also be + * executed for the XInput support because XI does not + * cover core devices + */ + manager_x11->core_pointer = + g_object_new (CLUTTER_TYPE_INPUT_DEVICE, + "name", "Core Pointer", + "has-cursor", TRUE, + "device-type", CLUTTER_POINTER_DEVICE, + "device-manager", manager_x11, + "device-mode", CLUTTER_INPUT_MODE_MASTER, + "backend", backend_x11, + NULL); + CLUTTER_NOTE (BACKEND, "Added core pointer device"); + + manager_x11->core_keyboard = + g_object_new (CLUTTER_TYPE_INPUT_DEVICE, + "name", "Core Keyboard", + "has-cursor", FALSE, + "device-type", CLUTTER_KEYBOARD_DEVICE, + "device-manager", manager_x11, + "device-mode", CLUTTER_INPUT_MODE_MASTER, + "backend", backend_x11, + NULL); + CLUTTER_NOTE (BACKEND, "Added core keyboard device"); + + /* associate core devices */ + _clutter_input_device_set_associated_device (manager_x11->core_pointer, + manager_x11->core_keyboard); + _clutter_input_device_set_associated_device (manager_x11->core_keyboard, + manager_x11->core_pointer); + + if (G_OBJECT_CLASS (clutter_device_manager_x11_parent_class)->constructed) + G_OBJECT_CLASS (clutter_device_manager_x11_parent_class)->constructed (gobject); +} + +static void +clutter_device_manager_x11_add_device (ClutterDeviceManager *manager, + ClutterInputDevice *device) +{ + ClutterDeviceManagerX11 *manager_x11 = CLUTTER_DEVICE_MANAGER_X11 (manager); + + manager_x11->devices = g_slist_prepend (manager_x11->devices, device); + g_hash_table_replace (manager_x11->devices_by_id, + GINT_TO_POINTER (device->id), + device); + + /* blow the cache */ + g_slist_free (manager_x11->all_devices); + manager_x11->all_devices = NULL; +} + +static void +clutter_device_manager_x11_remove_device (ClutterDeviceManager *manager, + ClutterInputDevice *device) +{ + ClutterDeviceManagerX11 *manager_x11 = CLUTTER_DEVICE_MANAGER_X11 (manager); + + g_hash_table_remove (manager_x11->devices_by_id, + GINT_TO_POINTER (device->id)); + manager_x11->devices = g_slist_remove (manager_x11->devices, device); + + /* blow the cache */ + g_slist_free (manager_x11->all_devices); + manager_x11->all_devices = NULL; +} + +static const GSList * +clutter_device_manager_x11_get_devices (ClutterDeviceManager *manager) +{ + ClutterDeviceManagerX11 *manager_x11 = CLUTTER_DEVICE_MANAGER_X11 (manager); + + /* cache the devices list so that we can keep the core pointer + * and keyboard outside of the ManagerX11:devices list + */ + if (manager_x11->all_devices == NULL) + { + GSList *all_devices = manager_x11->devices; + + all_devices = g_slist_prepend (all_devices, manager_x11->core_keyboard); + all_devices = g_slist_prepend (all_devices, manager_x11->core_pointer); + + manager_x11->all_devices = all_devices; + } + + return CLUTTER_DEVICE_MANAGER_X11 (manager)->all_devices; +} + +static ClutterInputDevice * +clutter_device_manager_x11_get_core_device (ClutterDeviceManager *manager, + ClutterInputDeviceType type) +{ + ClutterDeviceManagerX11 *manager_x11; + + manager_x11 = CLUTTER_DEVICE_MANAGER_X11 (manager); + + switch (type) + { + case CLUTTER_POINTER_DEVICE: + return manager_x11->core_pointer; + + case CLUTTER_KEYBOARD_DEVICE: + return manager_x11->core_keyboard; + + default: + return NULL; + } + + return NULL; +} + +static ClutterInputDevice * +clutter_device_manager_x11_get_device (ClutterDeviceManager *manager, + gint id) +{ + ClutterDeviceManagerX11 *manager_x11 = CLUTTER_DEVICE_MANAGER_X11 (manager); + + return g_hash_table_lookup (manager_x11->devices_by_id, + GINT_TO_POINTER (id)); +} + +static void +clutter_device_manager_x11_set_property (GObject *gobject, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + ClutterDeviceManagerX11 *manager_x11 = CLUTTER_DEVICE_MANAGER_X11 (gobject); + + switch (prop_id) + { + case PROP_EVENT_BASE: + manager_x11->xi_event_base = g_value_get_int (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + break; + } +} + +static void +clutter_device_manager_x11_class_init (ClutterDeviceManagerX11Class *klass) +{ + ClutterDeviceManagerClass *manager_class; + GObjectClass *gobject_class; + + obj_props[PROP_EVENT_BASE] = + g_param_spec_int ("event-base", + "Event Base", + "The first XI event", + -1, G_MAXINT, + -1, + CLUTTER_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY); + + gobject_class = G_OBJECT_CLASS (klass); + gobject_class->constructed = clutter_device_manager_x11_constructed; + gobject_class->set_property = clutter_device_manager_x11_set_property; + + g_object_class_install_properties (gobject_class, PROP_LAST, obj_props); + + manager_class = CLUTTER_DEVICE_MANAGER_CLASS (klass); + manager_class->add_device = clutter_device_manager_x11_add_device; + manager_class->remove_device = clutter_device_manager_x11_remove_device; + manager_class->get_devices = clutter_device_manager_x11_get_devices; + manager_class->get_core_device = clutter_device_manager_x11_get_core_device; + manager_class->get_device = clutter_device_manager_x11_get_device; +} + +static void +clutter_device_manager_x11_init (ClutterDeviceManagerX11 *self) +{ + self->devices_by_id = g_hash_table_new (NULL, NULL); +} diff --git a/clutter/x11/clutter-device-manager-x11.h b/clutter/x11/clutter-device-manager-core-x11.h similarity index 97% rename from clutter/x11/clutter-device-manager-x11.h rename to clutter/x11/clutter-device-manager-core-x11.h index f1fe81930..8d659679f 100644 --- a/clutter/x11/clutter-device-manager-x11.h +++ b/clutter/x11/clutter-device-manager-core-x11.h @@ -42,6 +42,8 @@ struct _ClutterDeviceManagerX11 { ClutterDeviceManager parent_instance; + GHashTable *devices_by_id; + /* the list of transient devices */ GSList *devices; @@ -53,7 +55,7 @@ struct _ClutterDeviceManagerX11 ClutterInputDevice *core_pointer; ClutterInputDevice *core_keyboard; - guint use_xinput_1 : 1; + int xi_event_base; }; struct _ClutterDeviceManagerX11Class diff --git a/clutter/x11/clutter-device-manager-x11.c b/clutter/x11/clutter-device-manager-x11.c deleted file mode 100644 index 5c5b2b53a..000000000 --- a/clutter/x11/clutter-device-manager-x11.c +++ /dev/null @@ -1,354 +0,0 @@ -/* - * Clutter. - * - * An OpenGL based 'interactive canvas' library. - * - * Copyright (C) 2009 Intel Corp. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see . - * - * Author: Emmanuele Bassi - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "clutter-backend-x11.h" -#include "clutter-device-manager-x11.h" -#include "clutter-input-device-x11.h" -#include "clutter-stage-x11.h" - -#include "clutter-backend.h" -#include "clutter-debug.h" -#include "clutter-device-manager-private.h" -#include "clutter-private.h" - -#ifdef HAVE_XINPUT -#include -#endif - -enum -{ - PROP_0, - - PROP_USE_XINPUT_1, - - PROP_LAST -}; - -static GParamSpec *obj_props[PROP_LAST]; - -G_DEFINE_TYPE (ClutterDeviceManagerX11, - clutter_device_manager_x11, - CLUTTER_TYPE_DEVICE_MANAGER); - -static void -clutter_device_manager_x11_constructed (GObject *gobject) -{ - ClutterDeviceManagerX11 *manager_x11; - ClutterBackendX11 *backend_x11; - ClutterDeviceManager *manager; - ClutterInputDevice *device; -#ifdef HAVE_XINPUT - XDeviceInfo *x_devices = NULL; - int res, opcode, event, error; - int i, n_devices; -#endif /* HAVE_XINPUT */ - - manager = CLUTTER_DEVICE_MANAGER (gobject); - manager_x11 = CLUTTER_DEVICE_MANAGER_X11 (gobject); - if (!manager_x11->use_xinput_1) - { - CLUTTER_NOTE (BACKEND, "XInput support not enabled"); - goto default_device; - } - - g_object_get (gobject, "backend", &backend_x11, NULL); - g_assert (backend_x11 != NULL); - -#ifdef HAVE_XINPUT - res = XQueryExtension (backend_x11->xdpy, "XInputExtension", - &opcode, - &event, - &error); - if (!res) - { - CLUTTER_NOTE (BACKEND, "No XInput extension available"); - goto default_device; - } - - backend_x11->xi_event_base = event; - - x_devices = XListInputDevices (backend_x11->xdpy, &n_devices); - if (n_devices == 0) - { - CLUTTER_NOTE (BACKEND, "No XInput devices found"); - goto default_device; - } - - for (i = 0; i < n_devices; i++) - { - XDeviceInfo *info = x_devices + i; - - CLUTTER_NOTE (BACKEND, - "Considering device %li with type %d, %d of %d", - info->id, - info->use, - i, n_devices); - - /* we only want 'raw' devices, not virtual ones */ - if (info->use == IsXExtensionPointer || - /* info->use == IsXExtensionKeyboard || XInput1 is broken */ - info->use == IsXExtensionDevice) - { - ClutterInputDeviceType device_type; - gint n_events = 0; - - switch (info->use) - { - case IsXExtensionPointer: - device_type = CLUTTER_POINTER_DEVICE; - break; - - /* XInput1 is broken for keyboards */ - case IsXExtensionKeyboard: - device_type = CLUTTER_KEYBOARD_DEVICE; - break; - - case IsXExtensionDevice: - default: - device_type = CLUTTER_EXTENSION_DEVICE; - break; - } - - device = g_object_new (CLUTTER_TYPE_INPUT_DEVICE_X11, - "id", info->id, - "device-type", device_type, - "name", info->name, - NULL); - n_events = _clutter_input_device_x11_construct (device, backend_x11); - - _clutter_device_manager_add_device (manager, device); - - if (info->use == IsXExtensionPointer && n_events > 0) - backend_x11->have_xinput = TRUE; - } - } - - XFree (x_devices); -#endif /* HAVE_XINPUT */ - -default_device: - /* fallback code in case: - * - * - we do not have XInput support compiled in - * - we do not have XInput support enabled - * - we do not have the XInput extension - * - * we register two default devices, one for the pointer - * and one for the keyboard. this block must also be - * executed for the XInput support because XI does not - * cover core devices - */ - device = g_object_new (CLUTTER_TYPE_INPUT_DEVICE_X11, - "id", 0, - "name", "Core Pointer", - "device-type", CLUTTER_POINTER_DEVICE, - "is-core", TRUE, - NULL); - CLUTTER_NOTE (BACKEND, "Added core pointer device"); - manager_x11->core_pointer = device; - - device = g_object_new (CLUTTER_TYPE_INPUT_DEVICE_X11, - "id", 1, - "name", "Core Keyboard", - "device-type", CLUTTER_KEYBOARD_DEVICE, - "is-core", TRUE, - NULL); - CLUTTER_NOTE (BACKEND, "Added core keyboard device"); - manager_x11->core_keyboard = device; - - if (G_OBJECT_CLASS (clutter_device_manager_x11_parent_class)->constructed) - G_OBJECT_CLASS (clutter_device_manager_x11_parent_class)->constructed (gobject); -} - -static void -clutter_device_manager_x11_add_device (ClutterDeviceManager *manager, - ClutterInputDevice *device) -{ - ClutterDeviceManagerX11 *manager_x11 = CLUTTER_DEVICE_MANAGER_X11 (manager); - - manager_x11->devices = g_slist_prepend (manager_x11->devices, device); - - /* blow the cache */ - g_slist_free (manager_x11->all_devices); - manager_x11->all_devices = NULL; -} - -static void -clutter_device_manager_x11_remove_device (ClutterDeviceManager *manager, - ClutterInputDevice *device) -{ - ClutterDeviceManagerX11 *manager_x11 = CLUTTER_DEVICE_MANAGER_X11 (manager); - - manager_x11->devices = g_slist_remove (manager_x11->devices, device); - - /* blow the cache */ - g_slist_free (manager_x11->all_devices); - manager_x11->all_devices = NULL; -} - -static const GSList * -clutter_device_manager_x11_get_devices (ClutterDeviceManager *manager) -{ - ClutterDeviceManagerX11 *manager_x11 = CLUTTER_DEVICE_MANAGER_X11 (manager); - - /* cache the devices list so that we can keep the core pointer - * and keyboard outside of the ManagerX11:devices list - */ - if (manager_x11->all_devices == NULL) - { - GSList *all_devices; - - all_devices = manager_x11->devices; - all_devices = g_slist_prepend (all_devices, manager_x11->core_keyboard); - all_devices = g_slist_prepend (all_devices, manager_x11->core_pointer); - - manager_x11->all_devices = all_devices; - } - - return CLUTTER_DEVICE_MANAGER_X11 (manager)->all_devices; -} - -static ClutterInputDevice * -clutter_device_manager_x11_get_core_device (ClutterDeviceManager *manager, - ClutterInputDeviceType type) -{ - ClutterDeviceManagerX11 *manager_x11; - - manager_x11 = CLUTTER_DEVICE_MANAGER_X11 (manager); - - switch (type) - { - case CLUTTER_POINTER_DEVICE: - return manager_x11->core_pointer; - - case CLUTTER_KEYBOARD_DEVICE: - return manager_x11->core_keyboard; - - case CLUTTER_EXTENSION_DEVICE: - default: - return NULL; - } - - return NULL; -} - -static ClutterInputDevice * -clutter_device_manager_x11_get_device (ClutterDeviceManager *manager, - gint id) -{ - ClutterDeviceManagerX11 *manager_x11 = CLUTTER_DEVICE_MANAGER_X11 (manager); - GSList *l; - - for (l = manager_x11->devices; l != NULL; l = l->next) - { - ClutterInputDevice *device = l->data; - - if (clutter_input_device_get_device_id (device) == id) - return device; - } - - return NULL; -} - -static void -clutter_device_manager_x11_set_property (GObject *gobject, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - ClutterDeviceManagerX11 *manager_x11; - - manager_x11 = CLUTTER_DEVICE_MANAGER_X11 (gobject); - - switch (prop_id) - { - case PROP_USE_XINPUT_1: - manager_x11->use_xinput_1 = g_value_get_boolean (value); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); - break; - } -} - -static void -clutter_device_manager_x11_get_property (GObject *gobject, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - ClutterDeviceManagerX11 *manager_x11; - - manager_x11 = CLUTTER_DEVICE_MANAGER_X11 (gobject); - - switch (prop_id) - { - case PROP_USE_XINPUT_1: - g_value_set_boolean (value, manager_x11->use_xinput_1); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); - break; - } -} - -static void -clutter_device_manager_x11_class_init (ClutterDeviceManagerX11Class *klass) -{ - ClutterDeviceManagerClass *manager_class; - GObjectClass *gobject_class; - GParamSpec *pspec; - - gobject_class = G_OBJECT_CLASS (klass); - gobject_class->set_property = clutter_device_manager_x11_set_property; - gobject_class->get_property = clutter_device_manager_x11_get_property; - gobject_class->constructed = clutter_device_manager_x11_constructed; - - manager_class = CLUTTER_DEVICE_MANAGER_CLASS (klass); - manager_class->add_device = clutter_device_manager_x11_add_device; - manager_class->remove_device = clutter_device_manager_x11_remove_device; - manager_class->get_devices = clutter_device_manager_x11_get_devices; - manager_class->get_core_device = clutter_device_manager_x11_get_core_device; - manager_class->get_device = clutter_device_manager_x11_get_device; - - pspec = g_param_spec_boolean ("use-xinput-1", - "Use XInput 1", - "Use the XInput 1.0 extension", - FALSE, - CLUTTER_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY); - obj_props[PROP_USE_XINPUT_1] = pspec; - g_object_class_install_property (gobject_class, PROP_USE_XINPUT_1, pspec); -} - -static void -clutter_device_manager_x11_init (ClutterDeviceManagerX11 *self) -{ - self->use_xinput_1 = FALSE; -} diff --git a/clutter/x11/clutter-device-manager-xi2.c b/clutter/x11/clutter-device-manager-xi2.c new file mode 100644 index 000000000..7ffe3c391 --- /dev/null +++ b/clutter/x11/clutter-device-manager-xi2.c @@ -0,0 +1,1058 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Copyright (C) 2009 Intel Corp. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Author: Emmanuele Bassi + */ + +#include "config.h" + +#include "clutter-device-manager-xi2.h" + +#include "clutter-backend-x11.h" +#include "clutter-input-device-xi2.h" +#include "clutter-stage-x11.h" + +#include "clutter-backend.h" +#include "clutter-debug.h" +#include "clutter-device-manager-private.h" +#include "clutter-event-translator.h" +#include "clutter-stage-private.h" +#include "clutter-private.h" + +#include + +enum +{ + PROP_0, + + PROP_OPCODE, + + PROP_LAST +}; + +static GParamSpec *obj_props[PROP_LAST] = { NULL, }; + +static const char *clutter_input_axis_atom_names[] = { + "Abs X", /* CLUTTER_INPUT_AXIS_X */ + "Abs Y", /* CLUTTER_INPUT_AXIS_Y */ + "Abs Pressure", /* CLUTTER_INPUT_AXIS_PRESSURE */ + "Abs Tilt X", /* CLUTTER_INPUT_AXIS_XTILT */ + "Abs Tilt Y", /* CLUTTER_INPUT_AXIS_YTILT */ + "Abs Wheel", /* CLUTTER_INPUT_AXIS_WHEEL */ +}; + +#define N_AXIS_ATOMS G_N_ELEMENTS (clutter_input_axis_atom_names) + +static Atom clutter_input_axis_atoms[N_AXIS_ATOMS] = { 0, }; + +static void clutter_event_translator_iface_init (ClutterEventTranslatorIface *iface); + +#define clutter_device_manager_xi2_get_type _clutter_device_manager_xi2_get_type + +G_DEFINE_TYPE_WITH_CODE (ClutterDeviceManagerXI2, + clutter_device_manager_xi2, + CLUTTER_TYPE_DEVICE_MANAGER, + G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_EVENT_TRANSLATOR, + clutter_event_translator_iface_init)); + +static void +translate_valuator_class (Display *xdisplay, + ClutterInputDevice *device, + XIValuatorClassInfo *class) +{ + static gboolean atoms_initialized = FALSE; + ClutterInputAxis axis, i; + + if (G_UNLIKELY (!atoms_initialized)) + { + XInternAtoms (xdisplay, + (char **) clutter_input_axis_atom_names, N_AXIS_ATOMS, + False, + clutter_input_axis_atoms); + + atoms_initialized = TRUE; + } + + for (i = CLUTTER_INPUT_AXIS_IGNORE; + i <= CLUTTER_INPUT_AXIS_WHEEL; + i += 1) + { + if (clutter_input_axis_atoms[i] == class->label) + { + axis = i; + break; + } + } + + _clutter_input_device_add_axis (device, axis, + class->min, + class->max, + class->resolution); +} + +static void +translate_device_classes (Display *xdisplay, + ClutterInputDevice *device, + XIAnyClassInfo **classes, + guint n_classes) +{ + gint i; + + for (i = 0; i < n_classes; i++) + { + XIAnyClassInfo *class_info = classes[i]; + + switch (class_info->type) + { + case XIKeyClass: + { + XIKeyClassInfo *key_info = (XIKeyClassInfo *) class_info; + gint j; + + _clutter_input_device_set_keys (device, + key_info->num_keycodes, + 0, + G_MAXUINT); + + for (j = 0; j < key_info->num_keycodes; j++) + { + clutter_input_device_set_key (device, j, + key_info->keycodes[i], + 0); + } + } + break; + + case XIValuatorClass: + translate_valuator_class (xdisplay, device, + (XIValuatorClassInfo *) class_info); + break; + + default: + break; + } + } +} + +static ClutterInputDevice * +create_device (ClutterDeviceManagerXI2 *manager_xi2, + ClutterBackendX11 *backend_x11, + XIDeviceInfo *info) +{ + ClutterInputDeviceType source; + ClutterInputDevice *retval; + ClutterInputMode mode; + + if (info->use == XIMasterKeyboard || info->use == XISlaveKeyboard) + source = CLUTTER_KEYBOARD_DEVICE; + else + { + gchar *name; + + name = g_ascii_strdown (info->name, -1); + + if (strstr (name, "eraser") != NULL) + source = CLUTTER_ERASER_DEVICE; + else if (strstr (name, "cursor") != NULL) + source = CLUTTER_CURSOR_DEVICE; + else if (strstr (name, "wacom") != NULL || strstr (name, "pen") != NULL) + source = CLUTTER_PEN_DEVICE; + else + source = CLUTTER_POINTER_DEVICE; + + g_free (name); + } + + switch (info->use) + { + case XIMasterKeyboard: + case XIMasterPointer: + mode = CLUTTER_INPUT_MODE_MASTER; + break; + + case XISlaveKeyboard: + case XISlavePointer: + mode = CLUTTER_INPUT_MODE_SLAVE; + break; + + case XIFloatingSlave: + default: + mode = CLUTTER_INPUT_MODE_FLOATING; + break; + } + + retval = g_object_new (CLUTTER_TYPE_INPUT_DEVICE_XI2, + "name", info->name, + "id", info->deviceid, + "has-cursor", (info->use == XIMasterPointer), + "device-manager", manager_xi2, + "device-type", source, + "device-mode", mode, + "backend", backend_x11, + NULL); + + translate_device_classes (backend_x11->xdpy, retval, + info->classes, + info->num_classes); + + CLUTTER_NOTE (BACKEND, "Created device '%s' (id: %d, has-cursor: %s)", + info->name, + info->deviceid, + info->use == XIMasterPointer ? "yes" : "no"); + + return retval; +} + +static ClutterInputDevice * +add_device (ClutterDeviceManagerXI2 *manager_xi2, + ClutterBackendX11 *backend_x11, + XIDeviceInfo *info, + gboolean in_construction) +{ + ClutterInputDevice *device; + + device = create_device (manager_xi2, backend_x11, info); + + /* we don't go through the DeviceManager::add_device() vfunc because + * that emits the signal, and we only do it conditionally + * + * FIXME: add a boolean "emit_signal" argument to the add_device() + * wrapper in ClutterDeviceManager, and emit the signal only if true + */ + g_hash_table_replace (manager_xi2->devices_by_id, + GINT_TO_POINTER (info->deviceid), + g_object_ref (device)); + + if (info->use == XIMasterPointer || + info->use == XIMasterKeyboard) + { + manager_xi2->master_devices = + g_list_prepend (manager_xi2->master_devices, device); + } + else if (info->use == XISlavePointer || + info->use == XISlaveKeyboard || + info->use == XIFloatingSlave) + { + manager_xi2->slave_devices = + g_list_prepend (manager_xi2->slave_devices, device); + } + else + g_warning ("Unhandled device: %s", + clutter_input_device_get_device_name (device)); + + /* relationships between devices and signal emissions are not + * necessary while we're constructing the device manager instance + */ + if (!in_construction) + { + if (info->use == XISlavePointer || info->use == XISlaveKeyboard) + { + ClutterInputDevice *master; + + master = g_hash_table_lookup (manager_xi2->devices_by_id, + GINT_TO_POINTER (info->attachment)); + _clutter_input_device_set_associated_device (device, master); + _clutter_input_device_add_slave (master, device); + } + + g_signal_emit_by_name (manager_xi2, "device-added", device); + } + + return device; +} + +static void +remove_device (ClutterDeviceManagerXI2 *manager_xi2, + gint device_id) +{ + ClutterInputDevice *device; + + device = g_hash_table_lookup (manager_xi2->devices_by_id, + GINT_TO_POINTER (device_id)); + + if (device != NULL) + { + manager_xi2->master_devices = + g_list_remove (manager_xi2->master_devices, device); + manager_xi2->slave_devices = + g_list_remove (manager_xi2->slave_devices, device); + + g_signal_emit_by_name (manager_xi2, "device-removed", device); + + g_object_run_dispose (G_OBJECT (device)); + + g_hash_table_remove (manager_xi2->devices_by_id, + GINT_TO_POINTER (device_id)); + } +} + +static void +translate_hierarchy_event (ClutterBackendX11 *backend_x11, + ClutterDeviceManagerXI2 *manager_xi2, + XIHierarchyEvent *ev) +{ + ClutterInputDevice *device; + int i; + + for (i = 0; i < ev->num_info; i++) + { + if (ev->info[i].flags & XIDeviceEnabled) + { + XIDeviceInfo *info; + int n_devices; + + info = XIQueryDevice (backend_x11->xdpy, + ev->info[i].deviceid, + &n_devices); + device = add_device (manager_xi2, backend_x11, &info[0], FALSE); + } + else if (ev->info[i].flags & XIDeviceDisabled) + { + remove_device (manager_xi2, ev->info[i].deviceid); + } + else if ((ev->info[i].flags & XISlaveAttached) || + (ev->info[i].flags & XISlaveDetached)) + { + ClutterInputDevice *master, *slave; + XIDeviceInfo *info; + int n_devices; + + slave = g_hash_table_lookup (manager_xi2->devices_by_id, + GINT_TO_POINTER (ev->info[i].deviceid)); + master = clutter_input_device_get_associated_device (slave); + + /* detach the slave in both cases */ + if (master != NULL) + { + _clutter_input_device_remove_slave (master, slave); + _clutter_input_device_set_associated_device (slave, NULL); + } + + /* and attach the slave to the new master if needed */ + if (ev->info[i].flags & XISlaveAttached) + { + info = XIQueryDevice (backend_x11->xdpy, + ev->info[i].deviceid, + &n_devices); + master = g_hash_table_lookup (manager_xi2->devices_by_id, + GINT_TO_POINTER (info->attachment)); + _clutter_input_device_set_associated_device (slave, master); + _clutter_input_device_add_slave (master, slave); + + XIFreeDeviceInfo (info); + } + } + } +} + +static void +clutter_device_manager_xi2_select_events (ClutterDeviceManager *manager, + Window xwindow, + XIEventMask *event_mask) +{ + Display *xdisplay; + + xdisplay = clutter_x11_get_default_display (); + + XISelectEvents (xdisplay, xwindow, event_mask, 1); +} + +static ClutterStage * +get_event_stage (ClutterEventTranslator *translator, + XIEvent *xi_event) +{ + Window xwindow = None; + + switch (xi_event->evtype) + { + case XI_KeyPress: + case XI_KeyRelease: + case XI_ButtonPress: + case XI_ButtonRelease: + case XI_Motion: + { + XIDeviceEvent *xev = (XIDeviceEvent *) xi_event; + + xwindow = xev->event; + } + break; + + case XI_Enter: + case XI_Leave: + case XI_FocusIn: + case XI_FocusOut: + { + XIEnterEvent *xev = (XIEnterEvent *) xi_event; + + xwindow = xev->event; + } + break; + + default: + break; + } + + if (xwindow == None) + return NULL; + + return clutter_x11_get_stage_from_window (xwindow); +} + +/* + * print_key_sym: Translate a symbol to its printable form if any + * @symbol: the symbol to translate + * @buffer: the buffer where to put the translated string + * @len: size of the buffer + * + * Translates @symbol into a printable representation in @buffer, if possible. + * + * Return value: The number of bytes of the translated string, 0 if the + * symbol can't be printed + * + * Note: The code is derived from libX11's src/KeyBind.c + * Copyright 1985, 1987, 1998 The Open Group + * + * Note: This code works for Latin-1 symbols. clutter_keysym_to_unicode() + * does the work for the other keysyms. + */ +static int +print_keysym (uint32_t symbol, + char *buffer, + int len) +{ + unsigned long high_bytes; + unsigned char c; + + high_bytes = symbol >> 8; + if (!(len && + ((high_bytes == 0) || + ((high_bytes == 0xFF) && + (((symbol >= CLUTTER_KEY_BackSpace) && + (symbol <= CLUTTER_KEY_Clear)) || + (symbol == CLUTTER_KEY_Return) || + (symbol == CLUTTER_KEY_Escape) || + (symbol == CLUTTER_KEY_KP_Space) || + (symbol == CLUTTER_KEY_KP_Tab) || + (symbol == CLUTTER_KEY_KP_Enter) || + ((symbol >= CLUTTER_KEY_KP_Multiply) && + (symbol <= CLUTTER_KEY_KP_9)) || + (symbol == CLUTTER_KEY_KP_Equal) || + (symbol == CLUTTER_KEY_Delete)))))) + return 0; + + /* if X keysym, convert to ascii by grabbing low 7 bits */ + if (symbol == CLUTTER_KEY_KP_Space) + c = CLUTTER_KEY_space & 0x7F; /* patch encoding botch */ + else if (high_bytes == 0xFF) + c = symbol & 0x7F; + else + c = symbol & 0xFF; + + buffer[0] = c; + return 1; +} + +static gdouble * +translate_axes (ClutterInputDevice *device, + gdouble x, + gdouble y, + ClutterStageX11 *stage_x11, + XIValuatorState *valuators) +{ + guint n_axes = clutter_input_device_get_n_axes (device); + guint i; + gdouble *retval; + double *values; + + retval = g_new0 (gdouble, n_axes); + values = valuators->values; + + for (i = 0; i < valuators->mask_len * 8; i++) + { + ClutterInputAxis axis; + gdouble val; + + if (!XIMaskIsSet (valuators->mask, i)) + continue; + + axis = _clutter_input_device_get_axis (device, i); + val = *values++; + + switch (axis) + { + case CLUTTER_INPUT_AXIS_X: + retval[i] = x; + break; + + case CLUTTER_INPUT_AXIS_Y: + retval[i] = y; + break; + + default: + _clutter_input_device_translate_axis (device, i, val, &retval[i]); + break; + } + } + + return retval; +} + +static ClutterTranslateReturn +clutter_device_manager_xi2_translate_event (ClutterEventTranslator *translator, + gpointer native, + ClutterEvent *event) +{ + ClutterDeviceManagerXI2 *manager_xi2 = CLUTTER_DEVICE_MANAGER_XI2 (translator); + ClutterTranslateReturn retval = CLUTTER_TRANSLATE_CONTINUE; + ClutterBackendX11 *backend_x11; + ClutterStageX11 *stage_x11; + ClutterStage *stage; + ClutterInputDevice *device; + XGenericEventCookie *cookie; + XIEvent *xi_event; + XEvent *xevent; + + backend_x11 = CLUTTER_BACKEND_X11 (clutter_get_default_backend ()); + + xevent = native; + + cookie = &xevent->xcookie; + + if (!XGetEventData (backend_x11->xdpy, cookie)) + return CLUTTER_TRANSLATE_CONTINUE; + + if (cookie->type != GenericEvent || + cookie->extension != manager_xi2->opcode) + { + XFreeEventData (backend_x11->xdpy, cookie); + return CLUTTER_TRANSLATE_CONTINUE; + } + + xi_event = (XIEvent *) cookie->data; + + stage = get_event_stage (translator, xi_event); + if (stage == NULL || CLUTTER_ACTOR_IN_DESTRUCTION (stage)) + { + XFreeEventData (backend_x11->xdpy, cookie); + return CLUTTER_TRANSLATE_CONTINUE; + } + + stage_x11 = CLUTTER_STAGE_X11 (_clutter_stage_get_window (stage)); + + event->any.stage = stage; + + switch (xi_event->evtype) + { + case XI_HierarchyChanged: + { + XIHierarchyEvent *xev = (XIHierarchyEvent *) xi_event; + + translate_hierarchy_event (backend_x11, manager_xi2, xev); + } + retval = CLUTTER_TRANSLATE_REMOVE; + break; + + case XI_DeviceChanged: + { + XIDeviceChangedEvent *xev = (XIDeviceChangedEvent *) xi_event; + + device = g_hash_table_lookup (manager_xi2->devices_by_id, + GINT_TO_POINTER (xev->deviceid)); + _clutter_input_device_reset_axes (device); + translate_device_classes (backend_x11->xdpy, + device, + xev->classes, + xev->num_classes); + } + retval = CLUTTER_TRANSLATE_REMOVE; + break; + + case XI_KeyPress: + case XI_KeyRelease: + { + XIDeviceEvent *xev = (XIDeviceEvent *) xi_event; + ClutterEventX11 *event_x11; + char buffer[7] = { 0, }; + gunichar n; + + event->key.type = event->type = (xev->evtype == XI_KeyPress) + ? CLUTTER_KEY_PRESS + : CLUTTER_KEY_RELEASE; + + event->key.time = xev->time; + event->key.stage = stage; + event->key.modifier_state = + _clutter_input_device_xi2_translate_state (&xev->mods, &xev->buttons); + event->key.hardware_keycode = xev->detail; + + /* keyval is the key ignoring all modifiers ('1' vs. '!') */ + event->key.keyval = + _clutter_keymap_x11_translate_key_state (backend_x11->keymap, + event->key.hardware_keycode, + event->key.modifier_state, + NULL); + + /* KeyEvents have platform specific data associated to them */ + event_x11 = _clutter_event_x11_new (); + _clutter_event_set_platform_data (event, event_x11); + + event_x11->key_group = + _clutter_keymap_x11_get_key_group (backend_x11->keymap, + event->key.modifier_state); + event_x11->key_is_modifier = + _clutter_keymap_x11_get_is_modifier (backend_x11->keymap, + event->key.hardware_keycode); + event_x11->num_lock_set = + _clutter_keymap_x11_get_num_lock_state (backend_x11->keymap); + event_x11->caps_lock_set = + _clutter_keymap_x11_get_caps_lock_state (backend_x11->keymap); + + device = g_hash_table_lookup (manager_xi2->devices_by_id, + GINT_TO_POINTER (xev->deviceid)); + event->key.device = device; + + /* XXX keep this in sync with the evdev device manager */ + n = print_keysym (event->key.keyval, buffer, sizeof (buffer)); + if (n == 0) + { + /* not printable */ + event->key.unicode_value = (gunichar) '\0'; + } + else + { + event->key.unicode_value = g_utf8_get_char_validated (buffer, n); + if (event->key.unicode_value == -1 || + event->key.unicode_value == -2) + event->key.unicode_value = (gunichar) '\0'; + } + + CLUTTER_NOTE (EVENT, "%s: win:0x%x, key: %12s (%d)", + event->any.type == CLUTTER_KEY_PRESS + ? "key press " + : "key release", + (unsigned int) stage_x11->xwin, + event->key.keyval ? buffer : "(none)", + event->key.keyval); + + if (xi_event->evtype == XI_KeyPress) + _clutter_stage_x11_set_user_time (stage_x11, event->key.time); + + retval = CLUTTER_TRANSLATE_QUEUE; + } + break; + + case XI_ButtonPress: + case XI_ButtonRelease: + { + XIDeviceEvent *xev = (XIDeviceEvent *) xi_event; + + switch (xev->detail) + { + case 4: + case 5: + case 6: + case 7: + event->scroll.type = event->type = CLUTTER_SCROLL; + + if (xev->detail == 4) + event->scroll.direction = CLUTTER_SCROLL_UP; + else if (xev->detail == 5) + event->scroll.direction = CLUTTER_SCROLL_DOWN; + else if (xev->detail == 6) + event->scroll.direction = CLUTTER_SCROLL_LEFT; + else + event->scroll.direction = CLUTTER_SCROLL_RIGHT; + + event->scroll.stage = stage; + + event->scroll.time = xev->time; + event->scroll.x = xev->event_x; + event->scroll.y = xev->event_y; + event->scroll.device = + g_hash_table_lookup (manager_xi2->devices_by_id, + GINT_TO_POINTER (xev->deviceid)); + event->scroll.modifier_state = + _clutter_input_device_xi2_translate_state (&xev->mods, + &xev->buttons); + event->scroll.axes = translate_axes (event->scroll.device, + event->scroll.x, + event->scroll.y, + stage_x11, + &xev->valuators); + break; + + default: + event->button.type = event->type = + (xi_event->evtype == XI_ButtonPress) ? CLUTTER_BUTTON_PRESS + : CLUTTER_BUTTON_RELEASE; + + event->button.stage = stage; + + event->button.time = xev->time; + event->button.x = xev->event_x; + event->button.y = xev->event_y; + event->button.button = xev->detail; + event->button.device = + g_hash_table_lookup (manager_xi2->devices_by_id, + GINT_TO_POINTER (xev->deviceid)); + event->button.modifier_state = + _clutter_input_device_xi2_translate_state (&xev->mods, + &xev->buttons); + event->button.axes = translate_axes (event->button.device, + event->button.x, + event->button.y, + stage_x11, + &xev->valuators); + break; + } + + CLUTTER_NOTE (EVENT, + "%s: win:0x%x, device:%s (button:%d, x:%.2f, y:%.2f)", + event->any.type == CLUTTER_BUTTON_PRESS + ? "button press " + : "button release", + (unsigned int) stage_x11->xwin, + event->button.device->device_name, + event->button.button, + event->button.x, + event->button.y); + + if (xi_event->evtype == XI_ButtonPress) + _clutter_stage_x11_set_user_time (stage_x11, event->button.time); + + retval = CLUTTER_TRANSLATE_QUEUE; + } + break; + + case XI_Motion: + { + XIDeviceEvent *xev = (XIDeviceEvent *) xi_event; + + event->motion.type = event->type = CLUTTER_MOTION; + + event->motion.stage = stage; + + event->motion.time = xev->time; + event->motion.x = xev->event_x; + event->motion.y = xev->event_y; + event->motion.device = + g_hash_table_lookup (manager_xi2->devices_by_id, + GINT_TO_POINTER (xev->deviceid)); + event->motion.modifier_state = + _clutter_input_device_xi2_translate_state (&xev->mods, + &xev->buttons); + + CLUTTER_NOTE (EVENT, "motion: win:0x%x device:%s (x:%.2f, y:%.2f)", + (unsigned int) stage_x11->xwin, + event->motion.device->device_name, + event->motion.x, + event->motion.y); + + retval = CLUTTER_TRANSLATE_QUEUE; + } + break; + + case XI_Enter: + case XI_Leave: + { + XIEnterEvent *xev = (XIEnterEvent *) xi_event; + + device = g_hash_table_lookup (manager_xi2->devices_by_id, + GINT_TO_POINTER (xev->deviceid)); + + if (xi_event->evtype == XI_Enter) + { + _clutter_input_device_set_stage (device, stage); + + event->motion.type = event->type = CLUTTER_MOTION; + + event->motion.stage = stage; + event->motion.source = CLUTTER_ACTOR (stage); + + event->motion.time = xev->time; + event->motion.x = xev->event_x; + event->motion.y = xev->event_y; + event->motion.device = device; + event->motion.modifier_state = + _clutter_input_device_xi2_translate_state (&xev->mods, + &xev->buttons); + } + else + { + if (device->stage == NULL) + { + CLUTTER_NOTE (EVENT, + "Discarding Leave for ButtonRelease " + "event off-stage"); + + retval = CLUTTER_TRANSLATE_REMOVE; + break; + } + + _clutter_input_device_set_stage (device, NULL); + + event->crossing.type = event->type = CLUTTER_LEAVE; + + event->crossing.source = CLUTTER_ACTOR (stage); + + event->crossing.time = xev->time; + event->crossing.x = xev->event_x; + event->crossing.y = xev->event_y; + event->crossing.device = device; + } + + retval = CLUTTER_TRANSLATE_QUEUE; + } + break; + + case XI_FocusIn: + case XI_FocusOut: + retval = CLUTTER_TRANSLATE_CONTINUE; + break; + } + + XFreeEventData (backend_x11->xdpy, cookie); + + return retval; +} + +static void +clutter_event_translator_iface_init (ClutterEventTranslatorIface *iface) +{ + iface->translate_event = clutter_device_manager_xi2_translate_event; +} + +static void +clutter_device_manager_xi2_add_device (ClutterDeviceManager *manager, + ClutterInputDevice *device) +{ +} + +static void +clutter_device_manager_xi2_remove_device (ClutterDeviceManager *manager, + ClutterInputDevice *device) +{ + ClutterDeviceManagerXI2 *manager_xi2 = CLUTTER_DEVICE_MANAGER_XI2 (manager); + gint device_id; + + device_id = clutter_input_device_get_device_id (device); + + manager_xi2->master_devices = + g_list_remove (manager_xi2->master_devices, device); + manager_xi2->slave_devices = + g_list_remove (manager_xi2->slave_devices, device); + + g_signal_emit_by_name (manager_xi2, "device-removed", device); + + g_object_run_dispose (G_OBJECT (device)); + + g_hash_table_remove (manager_xi2->devices_by_id, + GINT_TO_POINTER (device_id)); +} + +static const GSList * +clutter_device_manager_xi2_get_devices (ClutterDeviceManager *manager) +{ + ClutterDeviceManagerXI2 *manager_xi2 = CLUTTER_DEVICE_MANAGER_XI2 (manager); + GSList *all_devices = NULL; + GList *l; + + if (manager_xi2->all_devices != NULL) + return manager_xi2->all_devices; + + for (l = manager_xi2->master_devices; l != NULL; l = l->next) + all_devices = g_slist_prepend (all_devices, l->data); + + for (l = manager_xi2->slave_devices; l != NULL; l = l->next) + all_devices = g_slist_prepend (all_devices, l->data); + + manager_xi2->all_devices = g_slist_reverse (all_devices); + + return manager_xi2->all_devices; +} + +static ClutterInputDevice * +clutter_device_manager_xi2_get_device (ClutterDeviceManager *manager, + gint id) +{ + ClutterDeviceManagerXI2 *manager_xi2 = CLUTTER_DEVICE_MANAGER_XI2 (manager); + + return g_hash_table_lookup (manager_xi2->devices_by_id, + GINT_TO_POINTER (id)); +} + +static ClutterInputDevice * +clutter_device_manager_xi2_get_core_device (ClutterDeviceManager *manager, + ClutterInputDeviceType device_type) +{ + return NULL; +} + +static void +relate_masters (gpointer key, + gpointer value, + gpointer data) +{ + ClutterDeviceManagerXI2 *manager_xi2 = data; + ClutterInputDevice *device, *relative; + + device = g_hash_table_lookup (manager_xi2->devices_by_id, key); + relative = g_hash_table_lookup (manager_xi2->devices_by_id, value); + + _clutter_input_device_set_associated_device (device, relative); + _clutter_input_device_set_associated_device (relative, device); +} + +static void +relate_slaves (gpointer key, + gpointer value, + gpointer data) +{ + ClutterDeviceManagerXI2 *manager_xi2 = data; + ClutterInputDevice *master, *slave; + + master = g_hash_table_lookup (manager_xi2->devices_by_id, key); + slave = g_hash_table_lookup (manager_xi2->devices_by_id, value); + + _clutter_input_device_set_associated_device (slave, master); + _clutter_input_device_add_slave (master, slave); +} + +static void +clutter_device_manager_xi2_constructed (GObject *gobject) +{ + ClutterDeviceManagerXI2 *manager_xi2 = CLUTTER_DEVICE_MANAGER_XI2 (gobject); + ClutterDeviceManager *manager = CLUTTER_DEVICE_MANAGER (gobject); + ClutterBackendX11 *backend_x11; + GHashTable *masters, *slaves; + XIDeviceInfo *info; + XIEventMask event_mask; + unsigned char mask[2] = { 0, }; + int n_devices, i; + + backend_x11 = + CLUTTER_BACKEND_X11 (_clutter_device_manager_get_backend (manager)); + + masters = g_hash_table_new (NULL, NULL); + slaves = g_hash_table_new (NULL, NULL); + + info = XIQueryDevice (backend_x11->xdpy, XIAllDevices, &n_devices); + + for (i = 0; i < n_devices; i++) + { + XIDeviceInfo *xi_device = &info[i]; + ClutterInputDevice *device; + + device = add_device (manager_xi2, backend_x11, xi_device, TRUE); + + if (xi_device->use == XIMasterPointer || + xi_device->use == XIMasterKeyboard) + { + g_hash_table_insert (masters, + GINT_TO_POINTER (xi_device->deviceid), + GINT_TO_POINTER (xi_device->attachment)); + } + else if (xi_device->use == XISlavePointer || + xi_device->use == XISlaveKeyboard) + { + g_hash_table_insert (slaves, + GINT_TO_POINTER (xi_device->deviceid), + GINT_TO_POINTER (xi_device->attachment)); + } + } + + XIFreeDeviceInfo (info); + + g_hash_table_foreach (masters, relate_masters, manager_xi2); + g_hash_table_destroy (masters); + + g_hash_table_foreach (slaves, relate_slaves, manager_xi2); + g_hash_table_destroy (slaves); + + XISetMask (mask, XI_HierarchyChanged); + XISetMask (mask, XI_DeviceChanged); + + event_mask.deviceid = XIAllDevices; + event_mask.mask_len = sizeof (mask); + event_mask.mask = mask; + + clutter_device_manager_xi2_select_events (manager, + clutter_x11_get_root_window (), + &event_mask); + + if (G_OBJECT_CLASS (clutter_device_manager_xi2_parent_class)->constructed) + G_OBJECT_CLASS (clutter_device_manager_xi2_parent_class)->constructed (gobject); +} + +static void +clutter_device_manager_xi2_set_property (GObject *gobject, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + ClutterDeviceManagerXI2 *manager_xi2 = CLUTTER_DEVICE_MANAGER_XI2 (gobject); + + switch (prop_id) + { + case PROP_OPCODE: + manager_xi2->opcode = g_value_get_int (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + break; + } +} + +static void +clutter_device_manager_xi2_class_init (ClutterDeviceManagerXI2Class *klass) +{ + ClutterDeviceManagerClass *manager_class; + GObjectClass *gobject_class; + + obj_props[PROP_OPCODE] = + g_param_spec_int ("opcode", + "Opcode", + "The XI2 opcode", + -1, G_MAXINT, + -1, + CLUTTER_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY); + + gobject_class = G_OBJECT_CLASS (klass); + gobject_class->constructed = clutter_device_manager_xi2_constructed; + gobject_class->set_property = clutter_device_manager_xi2_set_property; + + g_object_class_install_properties (gobject_class, PROP_LAST, obj_props); + + manager_class = CLUTTER_DEVICE_MANAGER_CLASS (klass); + manager_class->add_device = clutter_device_manager_xi2_add_device; + manager_class->remove_device = clutter_device_manager_xi2_remove_device; + manager_class->get_devices = clutter_device_manager_xi2_get_devices; + manager_class->get_core_device = clutter_device_manager_xi2_get_core_device; + manager_class->get_device = clutter_device_manager_xi2_get_device; +} + +static void +clutter_device_manager_xi2_init (ClutterDeviceManagerXI2 *self) +{ + self->devices_by_id = g_hash_table_new_full (NULL, NULL, + NULL, + (GDestroyNotify) g_object_unref); +} diff --git a/clutter/x11/clutter-device-manager-xi2.h b/clutter/x11/clutter-device-manager-xi2.h new file mode 100644 index 000000000..f516fccf0 --- /dev/null +++ b/clutter/x11/clutter-device-manager-xi2.h @@ -0,0 +1,66 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Copyright (C) 2009 Intel Corp. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Author: Emmanuele Bassi + */ + +#ifndef __CLUTTER_DEVICE_MANAGER_XI2_H__ +#define __CLUTTER_DEVICE_MANAGER_XI2_H__ + +#include + +G_BEGIN_DECLS + +#define CLUTTER_TYPE_DEVICE_MANAGER_XI2 (_clutter_device_manager_xi2_get_type ()) +#define CLUTTER_DEVICE_MANAGER_XI2(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_DEVICE_MANAGER_XI2, ClutterDeviceManagerXI2)) +#define CLUTTER_IS_DEVICE_MANAGER_XI2(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_DEVICE_MANAGER_XI2)) +#define CLUTTER_DEVICE_MANAGER_XI2_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_DEVICE_MANAGER_XI2, ClutterDeviceManagerXI2Class)) +#define CLUTTER_IS_DEVICE_MANAGER_XI2_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_DEVICE_MANAGER_XI2)) +#define CLUTTER_DEVICE_MANAGER_XI2_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_DEVICE_MANAGER_XI2, ClutterDeviceManagerXI2Class)) + +typedef struct _ClutterDeviceManagerXI2 ClutterDeviceManagerXI2; +typedef struct _ClutterDeviceManagerXI2Class ClutterDeviceManagerXI2Class; + +struct _ClutterDeviceManagerXI2 +{ + ClutterDeviceManager parent_instance; + + GHashTable *devices_by_id; + + GSList *all_devices; + + GList *master_devices; + GList *slave_devices; + + ClutterInputDevice *client_pointer; + + int opcode; +}; + +struct _ClutterDeviceManagerXI2Class +{ + ClutterDeviceManagerClass parent_class; +}; + +GType _clutter_device_manager_xi2_get_type (void) G_GNUC_CONST; + +G_END_DECLS + +#endif /* __CLUTTER_DEVICE_MANAGER_XI2_H__ */ diff --git a/clutter/x11/clutter-event-x11.c b/clutter/x11/clutter-event-x11.c index 77b7f4fc5..b5bd31306 100644 --- a/clutter/x11/clutter-event-x11.c +++ b/clutter/x11/clutter-event-x11.c @@ -63,6 +63,7 @@ #include #endif +#if 0 /* XEMBED protocol support for toolkit embedding */ #define XEMBED_MAPPED (1 << 0) #define MAX_SUPPORTED_XEMBED_VERSION 1 @@ -83,6 +84,7 @@ #define XEMBED_ACTIVATE_ACCELERATOR 14 static Window ParentEmbedderWin = None; +#endif typedef struct _ClutterEventSource ClutterEventSource; @@ -94,16 +96,6 @@ struct _ClutterEventSource GPollFD event_poll_fd; }; -struct _ClutterEventX11 -{ - /* additional fields for Key events */ - gint key_group; - - guint key_is_modifier : 1; - guint num_lock_set : 1; - guint caps_lock_set : 1; -}; - ClutterEventX11 * _clutter_event_x11_new (void) { @@ -148,7 +140,6 @@ clutter_event_source_new (ClutterBackend *backend) GSource *source = g_source_new (&event_funcs, sizeof (ClutterEventSource)); ClutterEventSource *event_source = (ClutterEventSource *) source; - g_source_set_name (source, "Clutter X11 Event"); event_source->backend = backend; return source; @@ -160,6 +151,7 @@ check_xpending (ClutterBackend *backend) return XPending (CLUTTER_BACKEND_X11 (backend)->xdpy); } +#if 0 static gboolean xembed_send_message (ClutterBackendX11 *backend_x11, Window window, @@ -208,6 +200,7 @@ xembed_set_info (ClutterBackendX11 *backend_x11, backend_x11->atom_XEMBED_INFO, 32, PropModeReplace, (unsigned char *) list, 2); } +#endif void _clutter_backend_x11_events_init (ClutterBackend *backend) @@ -216,6 +209,7 @@ _clutter_backend_x11_events_init (ClutterBackend *backend) GSource *source; ClutterEventSource *event_source; int connection_number; + gchar *name; connection_number = ConnectionNumber (backend_x11->xdpy); CLUTTER_NOTE (EVENT, "Connection number: %d", connection_number); @@ -224,6 +218,11 @@ _clutter_backend_x11_events_init (ClutterBackend *backend) event_source = (ClutterEventSource *) source; g_source_set_priority (source, CLUTTER_PRIORITY_EVENTS); + name = g_strdup_printf ("Clutter X11 Event (connection: %d)", + connection_number); + g_source_set_name (source, name); + g_free (name); + event_source->event_poll_fd.fd = connection_number; event_source->event_poll_fd.events = G_IO_IN; @@ -252,6 +251,7 @@ _clutter_backend_x11_events_uninit (ClutterBackend *backend) } } +#if 0 static void update_last_event_time (ClutterBackendX11 *backend_x11, XEvent *xevent) @@ -426,51 +426,6 @@ handle_wm_protocols_event (ClutterBackendX11 *backend_x11, return FALSE; } -static gboolean -handle_xembed_event (ClutterBackendX11 *backend_x11, - XEvent *xevent) -{ - ClutterActor *stage; - - stage = clutter_stage_get_default (); - - switch (xevent->xclient.data.l[1]) - { - case XEMBED_EMBEDDED_NOTIFY: - CLUTTER_NOTE (EVENT, "got XEMBED_EMBEDDED_NOTIFY from %lx", - xevent->xclient.data.l[3]); - - ParentEmbedderWin = xevent->xclient.data.l[3]; - - clutter_actor_realize (stage); - clutter_actor_show (stage); - - xembed_set_info (backend_x11, - clutter_x11_get_stage_window (CLUTTER_STAGE (stage)), - XEMBED_MAPPED); - break; - case XEMBED_WINDOW_ACTIVATE: - CLUTTER_NOTE (EVENT, "got XEMBED_WINDOW_ACTIVATE"); - break; - case XEMBED_WINDOW_DEACTIVATE: - CLUTTER_NOTE (EVENT, "got XEMBED_WINDOW_DEACTIVATE"); - break; - case XEMBED_FOCUS_IN: - CLUTTER_NOTE (EVENT, "got XEMBED_FOCUS_IN"); - if (ParentEmbedderWin) - xembed_send_message (backend_x11, ParentEmbedderWin, - XEMBED_FOCUS_NEXT, - 0, 0, 0); - break; - default: - CLUTTER_NOTE (EVENT, "got unknown XEMBED message"); - break; - } - - /* do not propagate the XEMBED events to the stage */ - return FALSE; -} - static gboolean clipped_redraws_cool_off_cb (void *data) { @@ -1135,34 +1090,24 @@ event_translate (ClutterBackend *backend, out: return res; } +#endif static void events_queue (ClutterBackend *backend) { ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (backend); - ClutterBackendX11Class *backend_x11_class = - CLUTTER_BACKEND_X11_GET_CLASS (backend_x11); - ClutterEvent *event; - Display *xdisplay = backend_x11->xdpy; - XEvent xevent; - ClutterMainContext *clutter_context; - - clutter_context = _clutter_context_get_default (); + Display *xdisplay = backend_x11->xdpy; + ClutterEvent *event; + XEvent xevent; while (!clutter_events_pending () && XPending (xdisplay)) { XNextEvent (xdisplay, &xevent); - if (backend_x11_class->handle_event (backend_x11, &xevent)) - continue; - event = clutter_event_new (CLUTTER_NOTHING); - if (event_translate (backend, event, &xevent)) - { - /* push directly here to avoid copy of queue_put */ - g_queue_push_head (clutter_context->events_queue, event); - } + if (_clutter_backend_translate_event (backend, &xevent, event)) + _clutter_event_push (event, FALSE); else clutter_event_free (event); } @@ -1192,12 +1137,12 @@ events_queue (ClutterBackend *backend) ClutterX11FilterReturn clutter_x11_handle_event (XEvent *xevent) { - ClutterBackend *backend; ClutterBackendX11Class *backend_x11_class; - ClutterEvent *event; - ClutterMainContext *clutter_context; + ClutterMainContext *clutter_context; ClutterX11FilterReturn result; - gint spin = 1; + ClutterBackend *backend; + ClutterEvent *event; + gint spin = 1; /* The return values here are someone approximate; we return * CLUTTER_X11_FILTER_REMOVE if a clutter event is @@ -1213,22 +1158,16 @@ clutter_x11_handle_event (XEvent *xevent) clutter_threads_enter (); clutter_context = _clutter_context_get_default (); - backend = clutter_context->backend; + backend = clutter_get_default_backend (); backend_x11_class = CLUTTER_BACKEND_X11_GET_CLASS (backend); - /* If the backend just observed the event and didn't want it - * removed it could return FALSE, so assume that a TRUE return - * means that our caller should also do no further processing. */ - if (backend_x11_class->handle_event (CLUTTER_BACKEND_X11(backend), xevent)) - return CLUTTER_X11_FILTER_REMOVE; - event = clutter_event_new (CLUTTER_NOTHING); - if (event_translate (backend, event, xevent)) + if (_clutter_backend_translate_event (backend, xevent, event)) { - /* push directly here to avoid copy of queue_put */ + _clutter_event_push (event, FALSE); + result = CLUTTER_X11_FILTER_REMOVE; - g_queue_push_head (clutter_context->events_queue, event); } else { @@ -1253,7 +1192,7 @@ clutter_x11_handle_event (XEvent *xevent) --spin; } - out: +out: clutter_threads_leave (); return result; @@ -1312,8 +1251,7 @@ clutter_event_dispatch (GSource *source, /* Pop an event off the queue if any */ event = clutter_event_get (); - - if (event) + if (event != NULL) { /* forward the event into clutter for emission etc. */ clutter_do_event (event); diff --git a/clutter/x11/clutter-input-device-core-x11.c b/clutter/x11/clutter-input-device-core-x11.c new file mode 100644 index 000000000..4c1bf2fa8 --- /dev/null +++ b/clutter/x11/clutter-input-device-core-x11.c @@ -0,0 +1,414 @@ +#include "config.h" + +#include "clutter-input-device-core-x11.h" + +#include "clutter-debug.h" +#include "clutter-device-manager-private.h" +#include "clutter-private.h" +#include "clutter-stage-private.h" + +#include "clutter-backend-x11.h" +#include "clutter-stage-x11.h" + +#ifdef HAVE_XINPUT +#include +#endif + +#define MAX_DEVICE_CLASSES 13 + +typedef struct _ClutterInputDeviceClass ClutterInputDeviceX11Class; + +/* a specific X11 input device */ +struct _ClutterInputDeviceX11 +{ + ClutterInputDevice device; + +#ifdef HAVE_XINPUT + XDevice *xdevice; + + XEventClass event_classes[MAX_DEVICE_CLASSES]; + int num_classes; + + int button_press_type; + int button_release_type; + int motion_notify_type; + int state_notify_type; + int key_press_type; + int key_release_type; +#endif /* HAVE_XINPUT */ + + gint *axis_data; +}; + +#define clutter_input_device_x11_get_type _clutter_input_device_x11_get_type + +G_DEFINE_TYPE (ClutterInputDeviceX11, + clutter_input_device_x11, + CLUTTER_TYPE_INPUT_DEVICE); + +static void +clutter_input_device_x11_select_stage_events (ClutterInputDevice *device, + ClutterStage *stage, + gint event_mask) +{ +#if HAVE_XINPUT + ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (device->backend); + ClutterInputDeviceX11 *device_x11; + ClutterStageX11 *stage_x11; + XEventClass class; + gint i; + + device_x11 = CLUTTER_INPUT_DEVICE_X11 (device); + + stage_x11 = CLUTTER_STAGE_X11 (_clutter_stage_get_window (stage)); + + i = 0; + + if (event_mask & ButtonPressMask) + { + DeviceButtonPress (device_x11->xdevice, + device_x11->button_press_type, + class); + if (class != 0) + device_x11->event_classes[i++] = class; + + DeviceButtonPressGrab (device_x11->xdevice, 0, class); + if (class != 0) + device_x11->event_classes[i++] = class; + } + + if (event_mask & ButtonReleaseMask) + { + DeviceButtonRelease (device_x11->xdevice, + device_x11->button_release_type, + class); + if (class != 0) + device_x11->event_classes[i++] = class; + } + + if (event_mask & PointerMotionMask) + { + DeviceMotionNotify (device_x11->xdevice, + device_x11->motion_notify_type, + class); + if (class != 0) + device_x11->event_classes[i++] = class; + + DeviceStateNotify (device_x11->xdevice, + device_x11->state_notify_type, + class); + if (class != 0) + device_x11->event_classes[i++] = class; + } + + if (event_mask & KeyPressMask) + { + DeviceKeyPress (device_x11->xdevice, + device_x11->key_press_type, + class); + if (class != 0) + device_x11->event_classes[i++] = class; + } + + if (event_mask & KeyReleaseMask) + { + DeviceKeyRelease (device_x11->xdevice, + device_x11->key_release_type, + class); + if (class != 0) + device_x11->event_classes[i++] = class; + } + + device_x11->num_classes = i; + + XSelectExtensionEvent (backend_x11->xdpy, + stage_x11->xwin, + device_x11->event_classes, + device_x11->num_classes); +#endif /* HAVE_XINPUT */ +} + +static void +clutter_input_device_x11_dispose (GObject *gobject) +{ + ClutterInputDeviceX11 *device_x11 = CLUTTER_INPUT_DEVICE_X11 (gobject); + +#ifdef HAVE_XINPUT + if (device_x11->xdevice) + { + ClutterInputDevice *device = CLUTTER_INPUT_DEVICE (gobject); + ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (device->backend); + + XCloseDevice (backend_x11->xdpy, device_x11->xdevice); + device_x11->xdevice = NULL; + } +#endif /* HAVE_XINPUT */ + + g_free (device_x11->axis_data); + + G_OBJECT_CLASS (clutter_input_device_x11_parent_class)->dispose (gobject); +} + +static void +clutter_input_device_x11_constructed (GObject *gobject) +{ +#ifdef HAVE_XINPUT + ClutterInputDeviceX11 *device_x11 = CLUTTER_INPUT_DEVICE_X11 (gobject); + ClutterInputDevice *device = CLUTTER_INPUT_DEVICE (gobject); + ClutterBackendX11 *backend_x11; + + backend_x11 = CLUTTER_BACKEND_X11 (device->backend); + + clutter_x11_trap_x_errors (); + + device_x11->xdevice = XOpenDevice (backend_x11->xdpy, device->id); + + if (clutter_x11_untrap_x_errors ()) + { + g_warning ("Device '%s' cannot be opened", + clutter_input_device_get_device_name (device)); + } +#endif /* HAVE_XINPUT */ + + if (G_OBJECT_CLASS (clutter_input_device_x11_parent_class)->constructed) + G_OBJECT_CLASS (clutter_input_device_x11_parent_class)->constructed (gobject); +} + +static void +clutter_input_device_x11_class_init (ClutterInputDeviceX11Class *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->constructed = clutter_input_device_x11_constructed; + gobject_class->dispose = clutter_input_device_x11_dispose; + + g_signal_override_class_handler ("select-stage-events", + CLUTTER_TYPE_INPUT_DEVICE_X11, + G_CALLBACK (clutter_input_device_x11_select_stage_events)); +} + +static void +clutter_input_device_x11_init (ClutterInputDeviceX11 *self) +{ +} + +#ifdef HAVE_XINPUT +static void +update_axes (ClutterInputDeviceX11 *device_x11, + guint n_axes, + gint first_axis, + gint *axes_data) +{ + ClutterInputDevice *device = CLUTTER_INPUT_DEVICE (device_x11); + gint i; + + if (device_x11->axis_data == NULL) + { + device_x11->axis_data = + g_new0 (gint, clutter_input_device_get_n_axes (device)); + } + + for (i = 0; i < n_axes; i++) + device_x11->axis_data[first_axis + i] = axes_data[i]; +} + +static void +translate_axes (ClutterInputDeviceX11 *device_x11, + ClutterStageX11 *stage_x11, + gdouble *event_axes, + gfloat *event_x, + gfloat *event_y) +{ + ClutterInputDevice *device = CLUTTER_INPUT_DEVICE (device_x11); + gint root_x, root_y; + gint n_axes, i; + gfloat x, y; + + if (!_clutter_stage_x11_get_root_coords (stage_x11, &root_x, &root_y)) + return; + + x = y = 0.0f; + n_axes = clutter_input_device_get_n_axes (device); + + for (i = 0; i < n_axes; i++) + { + ClutterInputAxis axis; + + axis = _clutter_input_device_get_axis (device, i); + + switch (axis) + { + case CLUTTER_INPUT_AXIS_X: + case CLUTTER_INPUT_AXIS_Y: + _clutter_x11_input_device_translate_screen_coord (device, + root_x, root_y, + i, + device_x11->axis_data[i], + &event_axes[i]); + if (axis == CLUTTER_INPUT_AXIS_X) + x = event_axes[i]; + else if (axis == CLUTTER_INPUT_AXIS_Y) + y = event_axes[i]; + break; + + default: + _clutter_input_device_translate_axis (device, i, + device_x11->axis_data[i], + &event_axes[i]); + break; + } + } + + if (event_x) + *event_x = x; + + if (event_y) + *event_y = y; +} + +/* + * translate_state: + * @state: the keyboard state of the core device + * @device_state: the button state of the device + * + * Trivially translates the state and the device state into a + * single bitmask. + */ +static guint +translate_state (guint state, + guint device_state) +{ + return device_state | (state & 0xff); +} +#endif /* HAVE_XINPUT */ + +gboolean +_clutter_input_device_x11_translate_xi_event (ClutterInputDeviceX11 *device_x11, + ClutterStageX11 *stage_x11, + XEvent *xevent, + ClutterEvent *event) +{ +#ifdef HAVE_XINPUT + ClutterInputDevice *device = CLUTTER_INPUT_DEVICE (device_x11); + + if ((xevent->type == device_x11->button_press_type) || + (xevent->type == device_x11->button_release_type)) + { + XDeviceButtonEvent *xdbe = (XDeviceButtonEvent *) xevent; + + event->button.type = event->type = + (xdbe->type == device_x11->button_press_type) ? CLUTTER_BUTTON_PRESS + : CLUTTER_BUTTON_RELEASE; + event->button.device = device; + event->button.time = xdbe->time; + event->button.button = xdbe->button; + event->button.modifier_state = + translate_state (xdbe->state, xdbe->device_state); + + event->button.axes = + g_new0 (gdouble, clutter_input_device_get_n_axes (device)); + + update_axes (device_x11, + xdbe->axes_count, + xdbe->first_axis, + xdbe->axis_data); + translate_axes (device_x11, stage_x11, + event->button.axes, + &event->button.x, + &event->button.y); + + _clutter_stage_x11_set_user_time (stage_x11, event->button.time); + + return TRUE; + } + + if ((xevent->type == device_x11->key_press_type) || + (xevent->type == device_x11->key_release_type)) + { + XDeviceKeyEvent *xdke = (XDeviceKeyEvent *) xevent; + + if (xdke->keycode < device->min_keycode || + xdke->keycode >= device->max_keycode) + { + g_warning ("Invalid device key code received: %d", xdke->keycode); + return FALSE; + } + + clutter_input_device_get_key (device, + xdke->keycode - device->min_keycode, + &event->key.keyval, + &event->key.modifier_state); + if (event->key.keyval == 0) + return FALSE; + + event->key.type = event->type = + (xdke->type == device_x11->key_press_type) ? CLUTTER_KEY_PRESS + : CLUTTER_KEY_RELEASE; + event->key.time = xdke->time; + event->key.modifier_state |= + translate_state (xdke->state, xdke->device_state); + event->key.device = device; + +#if 0 + if ((event->key.keyval >= 0x20) && (event->key.keyval <= 0xff)) + { + event->key.unicode = (gunichar) event->key.keyval; + } +#endif + + _clutter_stage_x11_set_user_time (stage_x11, event->key.time); + + return TRUE; + } + + if (xevent->type == device_x11->motion_notify_type) + { + XDeviceMotionEvent *xdme = (XDeviceMotionEvent *) xevent; + + event->motion.type = event->type = CLUTTER_MOTION; + event->motion.time = xdme->time; + event->motion.modifier_state = + translate_state (xdme->state, xdme->device_state); + event->motion.device = device; + + event->motion.axes = + g_new0 (gdouble, clutter_input_device_get_n_axes (device)); + + update_axes (device_x11, + xdme->axes_count, + xdme->first_axis, + xdme->axis_data); + translate_axes (device_x11, stage_x11, + event->motion.axes, + &event->motion.x, + &event->motion.y); + + _clutter_stage_x11_set_user_time (stage_x11, event->motion.time); + + return TRUE; + } + + if (xevent->type == device_x11->state_notify_type) + { + XDeviceStateNotifyEvent *xdse = (XDeviceStateNotifyEvent *) xevent; + XInputClass *input_class = (XInputClass *) xdse->data; + gint n_axes = clutter_input_device_get_n_axes (device); + int i; + + for (i = 0; i < xdse->num_classes; i++) + { + if (input_class->class == ValuatorClass) + { + int *axis_data = ((XValuatorState *) input_class)->valuators; + + update_axes (device_x11, n_axes, 0, axis_data); + } + + input_class = + (XInputClass *)(((char *) input_class) + input_class->length); + } + } +#endif /* HAVE_XINPUT */ + + return FALSE; +} diff --git a/clutter/x11/clutter-input-device-core-x11.h b/clutter/x11/clutter-input-device-core-x11.h new file mode 100644 index 000000000..fc8610677 --- /dev/null +++ b/clutter/x11/clutter-input-device-core-x11.h @@ -0,0 +1,26 @@ +#ifndef __CLUTTER_INPUT_DEVICE_X11_H__ +#define __CLUTTER_INPUT_DEVICE_X11_H__ + +#include +#include + +#include "clutter-stage-x11.h" + +G_BEGIN_DECLS + +#define CLUTTER_TYPE_INPUT_DEVICE_X11 (_clutter_input_device_x11_get_type ()) +#define CLUTTER_INPUT_DEVICE_X11(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_INPUT_DEVICE_X11, ClutterInputDeviceX11)) +#define CLUTTER_IS_INPUT_DEVICE_X11(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_INPUT_DEVICE_X11)) + +typedef struct _ClutterInputDeviceX11 ClutterInputDeviceX11; + +GType _clutter_input_device_x11_get_type (void) G_GNUC_CONST; + +gboolean _clutter_input_device_x11_translate_xi_event (ClutterInputDeviceX11 *device_x11, + ClutterStageX11 *stage_x11, + XEvent *xevent, + ClutterEvent *event); + +G_END_DECLS + +#endif /* __CLUTTER_INPUT_DEVICE_X11_H__ */ diff --git a/clutter/x11/clutter-input-device-x11.c b/clutter/x11/clutter-input-device-x11.c deleted file mode 100644 index 342950c59..000000000 --- a/clutter/x11/clutter-input-device-x11.c +++ /dev/null @@ -1,228 +0,0 @@ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "clutter-input-device-x11.h" - -#include "clutter-debug.h" -#include "clutter-device-manager-private.h" -#include "clutter-private.h" - -#ifdef HAVE_XINPUT -#include -#endif - -typedef struct _ClutterInputDeviceClass ClutterInputDeviceX11Class; - -/* a specific X11 input device */ -struct _ClutterInputDeviceX11 -{ - ClutterInputDevice device; - -#ifdef HAVE_XINPUT - XDevice *xdevice; - XEventClass xevent_list[5]; /* MAX 5 event types */ - int num_events; -#endif - - guint is_core : 1; -}; - -enum -{ - PROP_0, - - PROP_IS_CORE, - - PROP_LAST -}; - -static GParamSpec *obj_props[PROP_LAST]; - -G_DEFINE_TYPE (ClutterInputDeviceX11, - clutter_input_device_x11, - CLUTTER_TYPE_INPUT_DEVICE); - -static void -clutter_input_device_x11_set_property (GObject *gobject, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - ClutterInputDeviceX11 *self = CLUTTER_INPUT_DEVICE_X11 (gobject); - - switch (prop_id) - { - case PROP_IS_CORE: - self->is_core = g_value_get_boolean (value); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); - break; - } -} - -static void -clutter_input_device_x11_get_property (GObject *gobject, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - ClutterInputDeviceX11 *self = CLUTTER_INPUT_DEVICE_X11 (gobject); - - switch (prop_id) - { - case PROP_IS_CORE: - g_value_set_boolean (value, self->is_core); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); - break; - } -} - -static void -clutter_input_device_x11_class_init (ClutterInputDeviceX11Class *klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - GParamSpec *pspec; - - gobject_class->set_property = clutter_input_device_x11_set_property; - gobject_class->get_property = clutter_input_device_x11_get_property; - - pspec = g_param_spec_boolean ("is-core", - "Is Core", - "Whether the device is a core one", - FALSE, - CLUTTER_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY); - obj_props[PROP_IS_CORE] = pspec; - g_object_class_install_property (gobject_class, PROP_IS_CORE, pspec); -} - -static void -clutter_input_device_x11_init (ClutterInputDeviceX11 *self) -{ - self->is_core = FALSE; -} - -gint -_clutter_input_device_x11_construct (ClutterInputDevice *device, - ClutterBackendX11 *backend) -{ - int n_events = 0; - -#ifdef HAVE_XINPUT - ClutterInputDeviceX11 *device_x11; - XDevice *x_device = NULL; - gint device_id; - int i; - - device_x11 = CLUTTER_INPUT_DEVICE_X11 (device); - device_id = clutter_input_device_get_device_id (device); - - clutter_x11_trap_x_errors (); - - /* retrieve the X11 device */ - x_device = XOpenDevice (backend->xdpy, device_id); - - if (clutter_x11_untrap_x_errors () || x_device == NULL) - { - CLUTTER_NOTE (BACKEND, "Unable to open device %i", device_id); - return 0; - } - - device_x11->xdevice = x_device; - - CLUTTER_NOTE (BACKEND, - "Registering XINPUT device with XID: %li", - x_device->device_id); - - /* We must go through all the classes supported by this device and - * register the appropriate events we want. Each class only appears - * once. We need to store the types with the stage since they are - * created dynamically by the server. They are not device specific. - */ - for (i = 0; i < x_device->num_classes; i++) - { - XInputClassInfo *xclass_info = x_device->classes + i; - int *button_press, *button_release, *motion_notify; - int *key_press, *key_release; - - button_press = - &backend->event_types[CLUTTER_X11_XINPUT_BUTTON_PRESS_EVENT]; - button_release = - &backend->event_types[CLUTTER_X11_XINPUT_BUTTON_RELEASE_EVENT]; - motion_notify = - &backend->event_types[CLUTTER_X11_XINPUT_MOTION_NOTIFY_EVENT]; - - key_press = - &backend->event_types[CLUTTER_X11_XINPUT_KEY_PRESS_EVENT]; - key_release = - &backend->event_types[CLUTTER_X11_XINPUT_KEY_RELEASE_EVENT]; - - switch (xclass_info->input_class) - { - /* event though XInput 1.x is broken for keyboard-like devices - * it might still be useful to track them down; the core keyboard - * will handle the right events anyway - */ - case KeyClass: - DeviceKeyPress (x_device, - *key_press, - device_x11->xevent_list[n_events]); - n_events++; - - DeviceKeyRelease (x_device, - *key_release, - device_x11->xevent_list[n_events]); - n_events++; - break; - - case ButtonClass: - DeviceButtonPress (x_device, - *button_press, - device_x11->xevent_list[n_events]); - n_events++; - - DeviceButtonRelease (x_device, - *button_release, - device_x11->xevent_list[n_events]); - n_events++; - break; - - case ValuatorClass: - DeviceMotionNotify (x_device, - *motion_notify, - device_x11->xevent_list[n_events]); - n_events++; - break; - } - } - - device_x11->num_events = n_events; -#endif /* HAVE_XINPUT */ - - return n_events; -} - -void -_clutter_input_device_x11_select_events (ClutterInputDevice *device, - ClutterBackendX11 *backend_x11, - Window xwin) -{ -#if HAVE_XINPUT - ClutterInputDeviceX11 *device_x11; - - device_x11 = CLUTTER_INPUT_DEVICE_X11 (device); - - if (device_x11->xdevice == None || device_x11->num_events == 0) - return; - - XSelectExtensionEvent (backend_x11->xdpy, xwin, - device_x11->xevent_list, - device_x11->num_events); -#endif /* HAVE_XINPUT */ -} diff --git a/clutter/x11/clutter-input-device-x11.h b/clutter/x11/clutter-input-device-x11.h deleted file mode 100644 index b8d60ac46..000000000 --- a/clutter/x11/clutter-input-device-x11.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef __CLUTTER_INPUT_DEVICE_X11_H__ -#define __CLUTTER_INPUT_DEVICE_X11_H__ - -#include -#include "clutter-backend-x11.h" - -G_BEGIN_DECLS - -#define CLUTTER_TYPE_INPUT_DEVICE_X11 (clutter_input_device_x11_get_type ()) -#define CLUTTER_INPUT_DEVICE_X11(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_INPUT_DEVICE_X11, ClutterInputDeviceX11)) -#define CLUTTER_IS_INPUT_DEVICE_X11(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_INPUT_DEVICE_X11)) - -typedef struct _ClutterInputDeviceX11 ClutterInputDeviceX11; - -GType clutter_input_device_x11_get_type (void) G_GNUC_CONST; - -gint _clutter_input_device_x11_construct (ClutterInputDevice *device, - ClutterBackendX11 *backend); -void _clutter_input_device_x11_select_events (ClutterInputDevice *device, - ClutterBackendX11 *backend, - Window xwin); - -G_END_DECLS - -#endif /* __CLUTTER_INPUT_DEVICE_X11_H__ */ diff --git a/clutter/x11/clutter-input-device-xi2.c b/clutter/x11/clutter-input-device-xi2.c new file mode 100644 index 000000000..ac90b25f0 --- /dev/null +++ b/clutter/x11/clutter-input-device-xi2.c @@ -0,0 +1,161 @@ +#include "config.h" + +#include "clutter-input-device-xi2.h" + +#include "clutter-debug.h" +#include "clutter-device-manager-private.h" +#include "clutter-private.h" +#include "clutter-stage-private.h" + +#include "clutter-backend-x11.h" +#include "clutter-stage-x11.h" + +#include + +typedef struct _ClutterInputDeviceClass ClutterInputDeviceXI2Class; + +/* a specific XI2 input device */ +struct _ClutterInputDeviceXI2 +{ + ClutterInputDevice device; + + gint device_id; +}; + +#define N_BUTTONS 5 + +#define clutter_input_device_xi2_get_type _clutter_input_device_xi2_get_type + +G_DEFINE_TYPE (ClutterInputDeviceXI2, + clutter_input_device_xi2, + CLUTTER_TYPE_INPUT_DEVICE); + +static void +clutter_input_device_xi2_select_stage_events (ClutterInputDevice *device, + ClutterStage *stage, + gint event_mask) +{ + ClutterInputDeviceXI2 *device_xi2 = CLUTTER_INPUT_DEVICE_XI2 (device); + ClutterBackendX11 *backend_x11; + ClutterStageX11 *stage_x11; + XIEventMask xi_event_mask; + unsigned char *mask; + int len; + + backend_x11 = CLUTTER_BACKEND_X11 (device->backend); + stage_x11 = CLUTTER_STAGE_X11 (_clutter_stage_get_window (stage)); + + len = XIMaskLen (XI_LASTEVENT); + mask = g_new0 (unsigned char, len); + + if (event_mask & PointerMotionMask) + XISetMask (mask, XI_Motion); + + if (event_mask & ButtonPressMask) + XISetMask (mask, XI_ButtonPress); + + if (event_mask & ButtonReleaseMask) + XISetMask (mask, XI_ButtonRelease); + + if (event_mask & KeyPressMask) + XISetMask (mask, XI_KeyPress); + + if (event_mask & KeyReleaseMask) + XISetMask (mask, XI_KeyRelease); + + if (event_mask & EnterWindowMask) + XISetMask (mask, XI_Enter); + + if (event_mask & LeaveWindowMask) + XISetMask (mask, XI_Leave); + + xi_event_mask.deviceid = device_xi2->device_id; + xi_event_mask.mask = mask; + xi_event_mask.mask_len = len; + + CLUTTER_NOTE (BACKEND, "Selecting device id '%d' events", + device_xi2->device_id); + + XISelectEvents (backend_x11->xdpy, stage_x11->xwin, &xi_event_mask, 1); + + g_free (mask); +} + +static void +clutter_input_device_xi2_constructed (GObject *gobject) +{ + ClutterInputDeviceXI2 *device_xi2 = CLUTTER_INPUT_DEVICE_XI2 (gobject); + + g_object_get (gobject, "id", &device_xi2->device_id, NULL); + + if (G_OBJECT_CLASS (clutter_input_device_xi2_parent_class)->constructed) + G_OBJECT_CLASS (clutter_input_device_xi2_parent_class)->constructed (gobject); +} + +static void +clutter_input_device_xi2_class_init (ClutterInputDeviceXI2Class *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->constructed = clutter_input_device_xi2_constructed; + + g_signal_override_class_handler ("select-stage-events", + CLUTTER_TYPE_INPUT_DEVICE_XI2, + G_CALLBACK (clutter_input_device_xi2_select_stage_events)); +} + +static void +clutter_input_device_xi2_init (ClutterInputDeviceXI2 *self) +{ +} + +guint +_clutter_input_device_xi2_translate_state (XIModifierState *modifiers_state, + XIButtonState *buttons_state) +{ + guint retval = 0; + + if (modifiers_state) + retval = (guint) modifiers_state->effective; + + if (buttons_state) + { + int len, i; + + len = MIN (N_BUTTONS, buttons_state->mask_len * 8); + + for (i = 0; i < len; i++) + { + if (!XIMaskIsSet (buttons_state->mask, i)) + continue; + + switch (i) + { + case 1: + retval |= CLUTTER_BUTTON1_MASK; + break; + + case 2: + retval |= CLUTTER_BUTTON2_MASK; + break; + + case 3: + retval |= CLUTTER_BUTTON3_MASK; + break; + + case 4: + retval |= CLUTTER_BUTTON4_MASK; + break; + + case 5: + retval |= CLUTTER_BUTTON5_MASK; + break; + + default: + break; + } + } + } + + return retval; +} diff --git a/clutter/x11/clutter-input-device-xi2.h b/clutter/x11/clutter-input-device-xi2.h new file mode 100644 index 000000000..8daf86d9e --- /dev/null +++ b/clutter/x11/clutter-input-device-xi2.h @@ -0,0 +1,22 @@ +#ifndef __CLUTTER_INPUT_DEVICE_XI2_H__ +#define __CLUTTER_INPUT_DEVICE_XI2_H__ + +#include +#include + +G_BEGIN_DECLS + +#define CLUTTER_TYPE_INPUT_DEVICE_XI2 (_clutter_input_device_xi2_get_type ()) +#define CLUTTER_INPUT_DEVICE_XI2(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_INPUT_DEVICE_XI2, ClutterInputDeviceXI2)) +#define CLUTTER_IS_INPUT_DEVICE_XI2(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_INPUT_DEVICE_XI2)) + +typedef struct _ClutterInputDeviceXI2 ClutterInputDeviceXI2; + +GType _clutter_input_device_xi2_get_type (void) G_GNUC_CONST; + +guint _clutter_input_device_xi2_translate_state (XIModifierState *modifiers_state, + XIButtonState *buttons_state); + +G_END_DECLS + +#endif /* __CLUTTER_INPUT_DEVICE_XI2_H__ */ diff --git a/clutter/x11/clutter-stage-x11.c b/clutter/x11/clutter-stage-x11.c index a3666d6aa..56240cc99 100644 --- a/clutter/x11/clutter-stage-x11.c +++ b/clutter/x11/clutter-stage-x11.c @@ -23,6 +23,8 @@ #include "config.h" #endif +#include + #ifdef HAVE_UNISTD_H #include #endif @@ -33,10 +35,13 @@ #include "clutter-actor-private.h" #include "clutter-debug.h" -#include "clutter-main.h" -#include "clutter-feature.h" -#include "clutter-event.h" +#include "clutter-device-manager-private.h" #include "clutter-enum-types.h" +#include "clutter-event-translator.h" +#include "clutter-event.h" +#include "clutter-feature.h" +#include "clutter-main.h" +#include "clutter-paint-volume-private.h" #include "clutter-private.h" #include "clutter-stage-private.h" @@ -48,13 +53,16 @@ #define STAGE_X11_IS_MAPPED(s) ((((ClutterStageX11 *) (s))->wm_state & STAGE_X11_WITHDRAWN) == 0) -static void clutter_stage_window_iface_init (ClutterStageWindowIface *iface); +static void clutter_stage_window_iface_init (ClutterStageWindowIface *iface); +static void clutter_event_translator_iface_init (ClutterEventTranslatorIface *iface); G_DEFINE_TYPE_WITH_CODE (ClutterStageX11, clutter_stage_x11, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_STAGE_WINDOW, - clutter_stage_window_iface_init)); + clutter_stage_window_iface_init) + G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_EVENT_TRANSLATOR, + clutter_event_translator_iface_init)); #define _NET_WM_STATE_REMOVE 0 /* remove/unset property */ #define _NET_WM_STATE_ADD 1 /* add/set property */ @@ -393,11 +401,69 @@ static gboolean clutter_stage_x11_realize (ClutterStageWindow *stage_window) { ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window); + ClutterDeviceManager *device_manager; + ClutterBackendX11 *backend_x11; + int event_flags; set_wm_pid (stage_x11); set_wm_title (stage_x11); set_cursor_visible (stage_x11); + backend_x11 = CLUTTER_BACKEND_X11 (clutter_get_default_backend ()); + + /* the masks for the events we want to select on a stage window; + * KeyPressMask and KeyReleaseMask are necessary even with XI1 + * because key events are broken with that extension, and will + * be fixed by XI2 + */ + event_flags = StructureNotifyMask + | FocusChangeMask + | ExposureMask + | PropertyChangeMask + | EnterWindowMask + | LeaveWindowMask + | KeyPressMask + | KeyReleaseMask + | ButtonPressMask + | ButtonReleaseMask + | PointerMotionMask; + + /* we unconditionally select input events even with event retrieval + * disabled because we need to guarantee that the Clutter internal + * state is maintained when calling clutter_x11_handle_event() without + * requiring applications or embedding toolkits to select events + * themselves. if we did that, we'd have to document the events to be + * selected, and also update applications and embedding toolkits each + * time we added a new mask, or a new class of events. + * + * see: http://bugzilla.clutter-project.org/show_bug.cgi?id=998 + * for the rationale of why we did conditional selection. it is now + * clear that a compositor should clear out the input region, since + * it cannot assume a perfectly clean slate coming from us. + * + * see: http://bugzilla.clutter-project.org/show_bug.cgi?id=2228 + * for an example of things that break if we do conditional event + * selection. + */ + XSelectInput (backend_x11->xdpy, stage_x11->xwin, event_flags); + + /* input events also depent on the actual device, so we need to + * use the device manager to let every device select them, using + * the event mask we passed to XSelectInput as the template + */ + device_manager = clutter_device_manager_get_default (); + _clutter_device_manager_select_stage_events (device_manager, + stage_x11->wrapper, + event_flags); + + /* no user resize.. */ + clutter_stage_x11_fix_window_size (stage_x11, + stage_x11->xwin_width, + stage_x11->xwin_height); + clutter_stage_x11_set_wm_protocols (stage_x11); + + CLUTTER_NOTE (BACKEND, "Successfully realized stage"); + return TRUE; } @@ -677,6 +743,12 @@ clutter_stage_x11_finalize (GObject *gobject) static void clutter_stage_x11_dispose (GObject *gobject) { + ClutterEventTranslator *translator = CLUTTER_EVENT_TRANSLATOR (gobject); + ClutterBackendX11 *backend_x11; + + backend_x11 = CLUTTER_BACKEND_X11 (clutter_get_default_backend ()); + _clutter_backend_x11_remove_event_translator (backend_x11, translator); + G_OBJECT_CLASS (clutter_stage_x11_parent_class)->dispose (gobject); } @@ -724,6 +796,343 @@ clutter_stage_window_iface_init (ClutterStageWindowIface *iface) iface->realize = clutter_stage_x11_realize; } +static inline void +set_user_time (ClutterBackendX11 *backend_x11, + ClutterStageX11 *stage_x11, + long timestamp) +{ + if (timestamp != CLUTTER_CURRENT_TIME) + { + XChangeProperty (backend_x11->xdpy, + stage_x11->xwin, + backend_x11->atom_NET_WM_USER_TIME, + XA_CARDINAL, 32, + PropModeReplace, + (unsigned char *) ×tamp, 1); + } +} + +static gboolean +handle_wm_protocols_event (ClutterBackendX11 *backend_x11, + ClutterStageX11 *stage_x11, + XEvent *xevent) +{ + Atom atom = (Atom) xevent->xclient.data.l[0]; + + if (atom == backend_x11->atom_WM_DELETE_WINDOW && + xevent->xany.window == stage_x11->xwin) + { + /* the WM_DELETE_WINDOW is a request: we do not destroy + * the window right away, as it might contain vital data; + * we relay the event to the application and we let it + * handle the request + */ + CLUTTER_NOTE (EVENT, "Delete stage %s[%p], win:0x%x", + _clutter_actor_get_debug_name (CLUTTER_ACTOR (stage_x11->wrapper)), + stage_x11->wrapper, + (unsigned int) stage_x11->xwin); + + set_user_time (backend_x11, stage_x11, xevent->xclient.data.l[1]); + + return TRUE; + } + else if (atom == backend_x11->atom_NET_WM_PING && + xevent->xany.window == stage_x11->xwin) + { + XClientMessageEvent xclient = xevent->xclient; + + xclient.window = backend_x11->xwin_root; + XSendEvent (backend_x11->xdpy, xclient.window, + False, + SubstructureRedirectMask | SubstructureNotifyMask, + (XEvent *) &xclient); + return FALSE; + } + + /* do not send any of the WM_PROTOCOLS events to the queue */ + return FALSE; +} + +static gboolean +clipped_redraws_cool_off_cb (void *data) +{ + ClutterStageX11 *stage_x11 = data; + + stage_x11->clipped_redraws_cool_off = 0; + + return FALSE; +} + +static ClutterTranslateReturn +clutter_stage_x11_translate_event (ClutterEventTranslator *translator, + gpointer native, + ClutterEvent *event) +{ + ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (translator); + ClutterTranslateReturn res = CLUTTER_TRANSLATE_CONTINUE; + XEvent *xevent = native; + ClutterBackendX11 *backend_x11; + ClutterStage *stage; + Window stage_xwindow = stage_x11->xwin; + + stage = clutter_x11_get_stage_from_window (xevent->xany.window); + if (stage == NULL) + return CLUTTER_TRANSLATE_CONTINUE; + + backend_x11 = CLUTTER_BACKEND_X11 (clutter_get_default_backend ()); + + switch (xevent->type) + { + case ConfigureNotify: + if (!stage_x11->is_foreign_xwin) + { + CLUTTER_NOTE (BACKEND, "%s: ConfigureNotify[%x] (%d, %d)", + G_STRLOC, + (unsigned int) stage_x11->xwin, + xevent->xconfigure.width, + xevent->xconfigure.height); + + /* Queue a relayout - we want glViewport to be called + * with the correct values, and this is done in ClutterStage + * via _cogl_onscreen_clutter_backend_set_size (). + * + * We queue a relayout, because if this ConfigureNotify is + * in response to a size we set in the application, the + * set_size() call below is essentially a null-op. + * + * Make sure we do this only when the size has changed, + * otherwise we end up relayouting on window moves. + */ + if ((stage_x11->state & CLUTTER_STAGE_STATE_FULLSCREEN) || + (stage_x11->xwin_width != xevent->xconfigure.width) || + (stage_x11->xwin_height != xevent->xconfigure.height)) + { + clutter_actor_queue_relayout (CLUTTER_ACTOR (stage)); + } + + /* If we're fullscreened, we want these variables to + * represent the size of the window before it was set + * to fullscreen. + */ + if (!(stage_x11->state & CLUTTER_STAGE_STATE_FULLSCREEN)) + { + stage_x11->xwin_width = xevent->xconfigure.width; + stage_x11->xwin_height = xevent->xconfigure.height; + } + + clutter_actor_set_size (CLUTTER_ACTOR (stage), + xevent->xconfigure.width, + xevent->xconfigure.height); + + /* XXX: This is a workaround for a race condition when + * resizing windows while there are in-flight + * glXCopySubBuffer blits happening. + * + * The problem stems from the fact that rectangles for the + * blits are described relative to the bottom left of the + * window and because we can't guarantee control over the X + * window gravity used when resizing so the gravity is + * typically NorthWest not SouthWest. + * + * This means if you grow a window vertically the server + * will make sure to place the old contents of the window + * at the top-left/north-west of your new larger window, but + * that may happen asynchronous to GLX preparing to do a + * blit specified relative to the bottom-left/south-west of + * the window (based on the old smaller window geometry). + * + * When the GLX issued blit finally happens relative to the + * new bottom of your window, the destination will have + * shifted relative to the top-left where all the pixels you + * care about are so it will result in a nasty artefact + * making resizing look very ugly! + * + * We can't currently fix this completely, in-part because + * the window manager tends to trample any gravity we might + * set. This workaround instead simply disables blits for a + * while if we are notified of any resizes happening so if + * the user is resizing a window via the window manager then + * they may see an artefact for one frame but then we will + * fallback to redrawing the full stage until the cooling + * off period is over. + */ + if (stage_x11->clipped_redraws_cool_off) + g_source_remove (stage_x11->clipped_redraws_cool_off); + + stage_x11->clipped_redraws_cool_off = + g_timeout_add_seconds (1, clipped_redraws_cool_off_cb, stage_x11); + + CLUTTER_UNSET_PRIVATE_FLAGS (stage_x11->wrapper, CLUTTER_IN_RESIZE); + + /* the resize process is complete, so we can ask the stage + * to set up the GL viewport with the new size + */ + clutter_stage_ensure_viewport (stage); + } + break; + + case PropertyNotify: + if (xevent->xproperty.atom == backend_x11->atom_NET_WM_STATE && + xevent->xproperty.window == stage_xwindow && + !stage_x11->is_foreign_xwin) + { + Atom type; + gint format; + gulong n_items, bytes_after; + guchar *data = NULL; + gboolean fullscreen_set = FALSE; + + clutter_x11_trap_x_errors (); + XGetWindowProperty (backend_x11->xdpy, stage_xwindow, + backend_x11->atom_NET_WM_STATE, + 0, G_MAXLONG, + False, XA_ATOM, + &type, &format, &n_items, + &bytes_after, &data); + clutter_x11_untrap_x_errors (); + + if (type != None && data != NULL) + { + Atom *atoms = (Atom *) data; + gulong i; + gboolean is_fullscreen = FALSE; + + for (i = 0; i < n_items; i++) + { + if (atoms[i] == backend_x11->atom_NET_WM_STATE_FULLSCREEN) + fullscreen_set = TRUE; + } + + is_fullscreen = + (stage_x11->state & CLUTTER_STAGE_STATE_FULLSCREEN); + + if (fullscreen_set != is_fullscreen) + { + if (fullscreen_set) + stage_x11->state |= CLUTTER_STAGE_STATE_FULLSCREEN; + else + stage_x11->state &= ~CLUTTER_STAGE_STATE_FULLSCREEN; + + stage_x11->fullscreening = fullscreen_set; + + event->any.type = CLUTTER_STAGE_STATE; + event->any.source = CLUTTER_ACTOR (stage); + event->any.stage = stage; + event->stage_state.changed_mask = + CLUTTER_STAGE_STATE_FULLSCREEN; + event->stage_state.new_state = stage_x11->state; + + res = CLUTTER_TRANSLATE_QUEUE; + } + + XFree (data); + } + } + break; + + case FocusIn: + if (!(stage_x11->state & CLUTTER_STAGE_STATE_ACTIVATED)) + { + /* TODO: check the detail? */ + stage_x11->state |= CLUTTER_STAGE_STATE_ACTIVATED; + + event->type = CLUTTER_STAGE_STATE; + event->any.source = CLUTTER_ACTOR (stage); + event->any.stage = stage; + event->stage_state.changed_mask = CLUTTER_STAGE_STATE_ACTIVATED; + event->stage_state.new_state = stage_x11->state; + + res = CLUTTER_TRANSLATE_QUEUE; + } + break; + + case FocusOut: + if (stage_x11->state & CLUTTER_STAGE_STATE_ACTIVATED) + { + /* TODO: check the detail? */ + stage_x11->state &= ~CLUTTER_STAGE_STATE_ACTIVATED; + + event->any.type = CLUTTER_STAGE_STATE; + event->any.source = CLUTTER_ACTOR (stage); + event->any.stage = stage; + event->stage_state.changed_mask = CLUTTER_STAGE_STATE_ACTIVATED; + event->stage_state.new_state = stage_x11->state; + + res = CLUTTER_TRANSLATE_QUEUE; + } + break; + + case Expose: + { + XExposeEvent *expose = (XExposeEvent *) xevent; + ClutterPaintVolume clip; + ClutterVertex origin; + + CLUTTER_NOTE (EVENT, + "expose for stage: %s[%p], win:0x%x - " + "redrawing area (x: %d, y: %d, width: %d, height: %d)", + _clutter_actor_get_debug_name (CLUTTER_ACTOR (stage)), + stage, + (unsigned int) stage_xwindow, + expose->x, + expose->y, + expose->width, + expose->height); + + origin.x = expose->x; + origin.y = expose->y; + origin.z = 0; + + _clutter_paint_volume_init_static (CLUTTER_ACTOR (stage), &clip); + + clutter_paint_volume_set_origin (&clip, &origin); + clutter_paint_volume_set_width (&clip, expose->width); + clutter_paint_volume_set_height (&clip, expose->height); + + _clutter_actor_queue_redraw_with_clip (CLUTTER_ACTOR (stage), 0, &clip); + + clutter_paint_volume_free (&clip); + } + break; + + case DestroyNotify: + CLUTTER_NOTE (EVENT, + "Destroy notification received for stage %s[%p], win:0x%x", + _clutter_actor_get_debug_name (CLUTTER_ACTOR (stage)), + stage, + (unsigned int) stage_xwindow); + event->any.type = CLUTTER_DESTROY_NOTIFY; + event->any.stage = stage; + res = CLUTTER_TRANSLATE_QUEUE; + break; + + case ClientMessage: + CLUTTER_NOTE (EVENT, "Client message for stage %s[%p], win:0x%x", + _clutter_actor_get_debug_name (CLUTTER_ACTOR (stage)), + stage, + (unsigned int) stage_xwindow); + if (handle_wm_protocols_event (backend_x11, stage_x11, xevent)) + { + event->any.type = CLUTTER_DELETE; + event->any.stage = stage; + res = CLUTTER_TRANSLATE_QUEUE; + } + break; + + default: + res = CLUTTER_TRANSLATE_CONTINUE; + break; + } + + return res; +} + +static void +clutter_event_translator_iface_init (ClutterEventTranslatorIface *iface) +{ + iface->translate_event = clutter_stage_x11_translate_event; +} + /** * clutter_x11_get_stage_window: * @stage: a #ClutterStage @@ -959,3 +1368,136 @@ clutter_x11_set_stage_foreign (ClutterStage *stage, return TRUE; } + +void +_clutter_stage_x11_destroy_window_untrapped (ClutterStageX11 *stage_x11) +{ + if (!stage_x11->is_foreign_xwin && stage_x11->xwin != None) + { + ClutterBackendX11 *backend_x11; + + backend_x11 = CLUTTER_BACKEND_X11 (clutter_get_default_backend ()); + + XDestroyWindow (backend_x11->xdpy, stage_x11->xwin); + stage_x11->xwin = None; + } + else + stage_x11->xwin = None; +} + +void +_clutter_stage_x11_destroy_window (ClutterStageX11 *stage_x11) +{ + if (stage_x11->xwin == None) + return; + + clutter_x11_trap_x_errors (); + + _clutter_stage_x11_destroy_window_untrapped (stage_x11); + + clutter_x11_untrap_x_errors (); +} + +gboolean +_clutter_stage_x11_create_window (ClutterStageX11 *stage_x11) +{ + ClutterBackendX11 *backend_x11; + XSetWindowAttributes xattr; + XVisualInfo *xvisinfo; + unsigned long mask; + gfloat width, height; + + if (stage_x11->xwin != None) + return TRUE; + + CLUTTER_NOTE (MISC, "Creating stage X window"); + + backend_x11 = CLUTTER_BACKEND_X11 (clutter_get_default_backend ()); + + xvisinfo = clutter_backend_x11_get_visual_info (backend_x11); + if (xvisinfo == NULL) + { + g_critical ("Unable to find suitable GL visual."); + return FALSE; + } + + /* window attributes */ + xattr.background_pixel = WhitePixel (backend_x11->xdpy, + backend_x11->xscreen_num); + xattr.border_pixel = 0; + xattr.colormap = XCreateColormap (backend_x11->xdpy, + backend_x11->xwin_root, + xvisinfo->visual, + AllocNone); + mask = CWBorderPixel | CWColormap; + + /* Call get_size - this will either get the geometry size (which + * before we create the window is set to 640x480), or if a size + * is set, it will get that. This lets you set a size on the + * stage before it's realized. + * + * we also round to the nearest integer because stage sizes + * should always be in pixels + */ + clutter_actor_get_size (CLUTTER_ACTOR (stage_x11->wrapper), &width, &height); + stage_x11->xwin_width = floorf (width + 0.5); + stage_x11->xwin_height = floorf (height + 0.5); + + stage_x11->xwin = XCreateWindow (backend_x11->xdpy, + backend_x11->xwin_root, + 0, 0, + stage_x11->xwin_width, + stage_x11->xwin_height, + 0, + xvisinfo->depth, + InputOutput, + xvisinfo->visual, + mask, &xattr); + + CLUTTER_NOTE (BACKEND, "Stage [%p], window: 0x%x, size: %dx%d", + stage_x11, + (unsigned int) stage_x11->xwin, + stage_x11->xwin_width, + stage_x11->xwin_height); + + XFree (xvisinfo); + + return TRUE; +} + +void +_clutter_stage_x11_set_user_time (ClutterStageX11 *stage_x11, + guint32 user_time) +{ + ClutterBackendX11 *backend_x11; + + backend_x11 = CLUTTER_BACKEND_X11 (clutter_get_default_backend ()); + set_user_time (backend_x11, stage_x11, user_time); +} + +gboolean +_clutter_stage_x11_get_root_coords (ClutterStageX11 *stage_x11, + gint *root_x, + gint *root_y) +{ + ClutterBackendX11 *backend_x11; + gint return_val; + Window child; + gint tx, ty; + + backend_x11 = CLUTTER_BACKEND_X11 (clutter_get_default_backend ()); + + return_val = XTranslateCoordinates (backend_x11->xdpy, + stage_x11->xwin, + backend_x11->xwin_root, + 0, 0, &tx, &ty, + &child); + + if (root_x) + *root_x = tx; + + if (root_y) + *root_y = ty; + + return (return_val == 0); +} diff --git a/clutter/x11/clutter-stage-x11.h b/clutter/x11/clutter-stage-x11.h index b62c2d2b9..b0e1e3657 100644 --- a/clutter/x11/clutter-stage-x11.h +++ b/clutter/x11/clutter-stage-x11.h @@ -90,6 +90,15 @@ void clutter_stage_x11_unmap (ClutterStageX11 *stage_x11); GList *clutter_stage_x11_get_input_devices (ClutterStageX11 *stage_x11); +gboolean _clutter_stage_x11_create_window (ClutterStageX11 *stage_x11); +void _clutter_stage_x11_destroy_window_untrapped (ClutterStageX11 *stage_x11); +void _clutter_stage_x11_destroy_window (ClutterStageX11 *stage_x11); +void _clutter_stage_x11_set_user_time (ClutterStageX11 *stage_x11, + guint32 user_time); +gboolean _clutter_stage_x11_get_root_coords (ClutterStageX11 *stage_x11, + gint *root_x, + gint *root_y); + G_END_DECLS #endif /* __CLUTTER_STAGE_H__ */ diff --git a/configure.ac b/configure.ac index 0f1db8d45..e7ace50d0 100644 --- a/configure.ac +++ b/configure.ac @@ -861,29 +861,47 @@ AS_IF([test "x$SUPPORT_XLIB" = "x1"], [AC_MSG_ERROR([not found])] ) - # XINPUT (optional) - xinput=no + # XI (optional) AC_ARG_ENABLE([xinput], - [AS_HELP_STRING([--enable-xinput], [Use the XINPUT X extension])], - [ - AS_IF([test "x$enableval" = "xyes"], - [PKG_CHECK_MODULES(XINPUT, [xi], [xinput=yes], [xinput=no])] - ) - ], - [xinput=no]) + [AS_HELP_STRING([--enable-xinput], [Use the XI X extension])], + [], + [enable_xinput=no]) - AS_CASE([$xinput], + AS_IF([test "x$enable_xinput" = "xyes"], + [ + PKG_CHECK_EXISTS([xi], [have_xinput=yes], [have_xinput=no]) + ], + [ + have_xinput=no + ]) + + AS_CASE([$have_xinput], [yes], [ - AC_DEFINE(HAVE_XINPUT, 1, [Use the XINPUT X extension]) + AC_CHECK_HEADERS([X11/extensions/XInput2.h], + [ + have_xinput2=yes + AC_DEFINE([HAVE_XINPUT_2], + [1], + [Define to 1 if XI2 is available]) + ], + [ + have_xinput2=no + AC_DEFINE([HAVE_XINPUT], + [1], + [Define to 1 if XInput is available]) + ]) - X11_LIBS="$X11_LIBS -lXi" + X11_LIBS="$X11_LIBS $XINPUT_LIBS" X11_PC_FILES="$X11_PC_FILES xi" ], [no], - [], + [have_xinput2=no], + + [*], + [AC_MSG_ERROR([Invalid argument for --enable-xinput])] ) # XKB @@ -910,6 +928,7 @@ AS_IF([test "x$SUPPORT_XLIB" = "x1"], ] ) +AM_CONDITIONAL([BUILD_XI2], [test "x$have_xinput2" = "xyes"]) AM_CONDITIONAL(X11_TESTS, [test "x$x11_tests" = "xyes"]) dnl === Enable debug level ==================================================== @@ -1258,7 +1277,8 @@ fi if test "x$SUPPORT_XLIB" = "x1"; then echo " Enable XComposite: ${have_xcomposite}" -echo " Enable XInput 1.0: ${xinput}" +echo " Enable XInput: ${have_xinput}" +echo " Enable XI2: ${have_xinput2}" echo " Enable XKB: ${have_xkb}" echo " Enable X11 tests: ${x11_tests}" fi diff --git a/tests/interactive/test-devices.c b/tests/interactive/test-devices.c index e286a064c..8304d6d8c 100644 --- a/tests/interactive/test-devices.c +++ b/tests/interactive/test-devices.c @@ -63,6 +63,19 @@ stage_motion_event_cb (ClutterActor *actor, return TRUE; } + if (event->motion.axes != NULL) + { + guint n_axes = clutter_input_device_get_n_axes (event->motion.device); + guint i; + + for (i = 0; i < n_axes; i++) + { + g_print ("Axis[%02d].value: %.2f\n", + i, + event->motion.axes[i]); + } + } + return FALSE; } From 001f84875aa5a0f0871b0f86a73d4c1972c12430 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 17 Jan 2011 16:42:18 +0000 Subject: [PATCH 02/41] Enable XInput support by default Since we have a decent XI1 and XI2 implementation, now, we should turn the support for XInput on by default. The actual implementation to be used at run-time is still left to be decided by the user. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index e7ace50d0..34d4483c5 100644 --- a/configure.ac +++ b/configure.ac @@ -865,7 +865,7 @@ AS_IF([test "x$SUPPORT_XLIB" = "x1"], AC_ARG_ENABLE([xinput], [AS_HELP_STRING([--enable-xinput], [Use the XI X extension])], [], - [enable_xinput=no]) + [enable_xinput=yes]) AS_IF([test "x$enable_xinput" = "xyes"], [ From 2777c52f2759513c8e4b66e474ed2da587bd5de5 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 17 Jan 2011 16:56:07 +0000 Subject: [PATCH 03/41] input-device: Make ClutterInputDeviceClass private We keep the symbol in the public header, but the definition is now private. You could not sub-class InputDevice anyway, without the instance structure, and the lack of padding in the class made actually implementing devices in backends really hard. --- clutter/clutter-device-manager-private.h | 5 +++++ clutter/clutter-input-device.h | 14 -------------- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/clutter/clutter-device-manager-private.h b/clutter/clutter-device-manager-private.h index f3764d72d..56dfc5a37 100644 --- a/clutter/clutter-device-manager-private.h +++ b/clutter/clutter-device-manager-private.h @@ -106,6 +106,11 @@ struct _ClutterInputDevice guint has_cursor : 1; }; +struct _ClutterInputDeviceClass +{ + GObjectClass parent_class; +}; + /* device manager */ void _clutter_device_manager_add_device (ClutterDeviceManager *device_manager, ClutterInputDevice *device); diff --git a/clutter/clutter-input-device.h b/clutter/clutter-input-device.h index e04d47d16..eb95aa157 100644 --- a/clutter/clutter-input-device.h +++ b/clutter/clutter-input-device.h @@ -97,20 +97,6 @@ typedef enum { CLUTTER_INPUT_AXIS_WHEEL } ClutterInputAxis; -/** - * ClutterInputDeviceClass: - * - * The #ClutterInputDeviceClass structure contains only private - * data and should not be accessed directly - * - * Since: 1.2 - */ -struct _ClutterInputDeviceClass -{ - /*< private >*/ - GObjectClass parent_class; -}; - GType clutter_input_device_get_type (void) G_GNUC_CONST; ClutterInputDeviceType clutter_input_device_get_device_type (ClutterInputDevice *device); From 342cdd25756b37745bfd1725d78f6f670f77bee0 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 17 Jan 2011 17:01:58 +0000 Subject: [PATCH 04/41] input-device: Move select_stage_events() to a vfunc Don't use a signal, use a virtual function. --- clutter/clutter-device-manager-private.h | 4 +++ clutter/clutter-input-device.c | 28 ++++----------------- clutter/x11/clutter-input-device-core-x11.c | 5 ++-- clutter/x11/clutter-input-device-xi2.c | 5 ++-- 4 files changed, 13 insertions(+), 29 deletions(-) diff --git a/clutter/clutter-device-manager-private.h b/clutter/clutter-device-manager-private.h index 56dfc5a37..ec5a2fc7f 100644 --- a/clutter/clutter-device-manager-private.h +++ b/clutter/clutter-device-manager-private.h @@ -109,6 +109,10 @@ struct _ClutterInputDevice struct _ClutterInputDeviceClass { GObjectClass parent_class; + + void (* select_stage_events) (ClutterInputDevice *device, + ClutterStage *stage, + gint event_mask); }; /* device manager */ diff --git a/clutter/clutter-input-device.c b/clutter/clutter-input-device.c index 415efcf89..0d2b81f7d 100644 --- a/clutter/clutter-input-device.c +++ b/clutter/clutter-input-device.c @@ -65,17 +65,8 @@ enum PROP_LAST }; -enum -{ - SELECT_STAGE_EVENTS, - - LAST_SIGNAL -}; - static GParamSpec *obj_props[PROP_LAST] = { NULL, }; -static guint device_signals[LAST_SIGNAL] = { 0, }; - G_DEFINE_TYPE (ClutterInputDevice, clutter_input_device, G_TYPE_OBJECT); static void @@ -298,17 +289,6 @@ clutter_input_device_class_init (ClutterInputDeviceClass *klass) gobject_class->set_property = clutter_input_device_set_property; gobject_class->get_property = clutter_input_device_get_property; g_object_class_install_properties (gobject_class, PROP_LAST, obj_props); - - device_signals[SELECT_STAGE_EVENTS] = - g_signal_new (I_("select-stage-events"), - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_FIRST, - 0, - NULL, NULL, - _clutter_marshal_VOID__OBJECT_INT, - G_TYPE_NONE, 2, - CLUTTER_TYPE_STAGE, - G_TYPE_INT); } static void @@ -1100,7 +1080,9 @@ _clutter_input_device_select_stage_events (ClutterInputDevice *device, ClutterStage *stage, gint event_mask) { - g_signal_emit (device, device_signals[SELECT_STAGE_EVENTS], 0, - stage, - event_mask); + ClutterInputDeviceClass *device_class; + + device_class = CLUTTER_INPUT_DEVICE_GET_CLASS (device); + if (device_class->select_stage_events != NULL) + device_class->select_stage_events (device, stage, event_mask); } diff --git a/clutter/x11/clutter-input-device-core-x11.c b/clutter/x11/clutter-input-device-core-x11.c index 4c1bf2fa8..5d729e992 100644 --- a/clutter/x11/clutter-input-device-core-x11.c +++ b/clutter/x11/clutter-input-device-core-x11.c @@ -178,13 +178,12 @@ static void clutter_input_device_x11_class_init (ClutterInputDeviceX11Class *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + ClutterInputDeviceClass *device_class = CLUTTER_INPUT_DEVICE_CLASS (klass); gobject_class->constructed = clutter_input_device_x11_constructed; gobject_class->dispose = clutter_input_device_x11_dispose; - g_signal_override_class_handler ("select-stage-events", - CLUTTER_TYPE_INPUT_DEVICE_X11, - G_CALLBACK (clutter_input_device_x11_select_stage_events)); + device_class->select_stage_events = clutter_input_device_x11_select_stage_events; } static void diff --git a/clutter/x11/clutter-input-device-xi2.c b/clutter/x11/clutter-input-device-xi2.c index ac90b25f0..45d5321a9 100644 --- a/clutter/x11/clutter-input-device-xi2.c +++ b/clutter/x11/clutter-input-device-xi2.c @@ -96,12 +96,11 @@ static void clutter_input_device_xi2_class_init (ClutterInputDeviceXI2Class *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + ClutterInputDeviceClass *device_class = CLUTTER_INPUT_DEVICE_CLASS (klass); gobject_class->constructed = clutter_input_device_xi2_constructed; - g_signal_override_class_handler ("select-stage-events", - CLUTTER_TYPE_INPUT_DEVICE_XI2, - G_CALLBACK (clutter_input_device_xi2_select_stage_events)); + device_class->select_stage_events = clutter_input_device_xi2_select_stage_events; } static void From caf359d8c9a110797e9ad64a7cb3600b7b964275 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 18 Jan 2011 12:57:12 +0000 Subject: [PATCH 05/41] input-device: Add documentation --- clutter/clutter-input-device.c | 48 +++++++++++++++++++++++++++++++++- clutter/clutter-input-device.h | 2 +- 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/clutter/clutter-input-device.c b/clutter/clutter-input-device.c index 0d2b81f7d..8d9cbeb4a 100644 --- a/clutter/clutter-input-device.c +++ b/clutter/clutter-input-device.c @@ -3,7 +3,7 @@ * * An OpenGL based 'interactive canvas' library. * - * Copyright (C) 2009 Intel Corp. + * Copyright © 2009, 2010, 2011 Intel Corp. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -1028,6 +1028,18 @@ _clutter_input_device_remove_slave (ClutterInputDevice *master, master->slaves = g_list_remove (master->slaves, slave); } +/** + * clutter_input_device_get_slave_devices: + * @device: a #ClutterInputDevice + * + * Retrieves the slave devices attached to @device. + * + * Return value: (transfer container) (element-type Clutter.InputDevice): a + * list of #ClutterInputDevice, or %NULL. The contents of the list are + * owned by the device. Use g_list_free() when done + * + * Since: 1.6 + */ GList * clutter_input_device_get_slave_devices (ClutterInputDevice *device) { @@ -1036,6 +1048,15 @@ clutter_input_device_get_slave_devices (ClutterInputDevice *device) return g_list_copy (device->slaves); } +/*< internal > + * clutter_input_device_set_associated_device: + * @device: a #ClutterInputDevice + * @associated: (allow-none): a #ClutterInputDevice, or %NULL + * + * Sets the associated device for @device. + * + * This function keeps a reference on the associated device. + */ void _clutter_input_device_set_associated_device (ClutterInputDevice *device, ClutterInputDevice *associated) @@ -1067,6 +1088,21 @@ _clutter_input_device_set_associated_device (ClutterInputDevice *device, } } +/** + * clutter_input_device_get_associated_device: + * @device: a #ClutterInputDevice + * + * Retrieves a pointer to the #ClutterInputDevice that has been + * associated to @device. + * + * If the #ClutterInputDevice:device-mode property of @device is + * set to %CLUTTER_INPUT_MODE_MASTER, this function will return + * %NULL. + * + * Return value: (transfer none): a #ClutterInputDevice, or %NULL + * + * Since: 1.6 + */ ClutterInputDevice * clutter_input_device_get_associated_device (ClutterInputDevice *device) { @@ -1075,6 +1111,16 @@ clutter_input_device_get_associated_device (ClutterInputDevice *device) return device->associated; } +/*< internal > + * clutter_input_device_select_stage_events: + * @device: a #ClutterInputDevice + * @stage: the #ClutterStage to select events on + * @event_mask: platform-specific mask of events + * + * Selects input device events on @stage. + * + * The implementation of this function depends on the backend used. + */ void _clutter_input_device_select_stage_events (ClutterInputDevice *device, ClutterStage *stage, diff --git a/clutter/clutter-input-device.h b/clutter/clutter-input-device.h index eb95aa157..232554f13 100644 --- a/clutter/clutter-input-device.h +++ b/clutter/clutter-input-device.h @@ -3,7 +3,7 @@ * * An OpenGL based 'interactive canvas' library. * - * Copyright (C) 2009 Intel Corp. + * Copyright © 2009, 2010, 2011 Intel Corp. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public From 7c25b063aafc21527413106dca6db3fcb50bd5ad Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 18 Jan 2011 12:57:50 +0000 Subject: [PATCH 06/41] x11: Use XI2 API only if we support it XIQueryVersion() is XI2 API. --- clutter/x11/clutter-backend-x11.c | 43 +++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/clutter/x11/clutter-backend-x11.c b/clutter/x11/clutter-backend-x11.c index 428644b2d..5162b3d17 100644 --- a/clutter/x11/clutter-backend-x11.c +++ b/clutter/x11/clutter-backend-x11.c @@ -129,8 +129,8 @@ cogl_xlib_filter (XEvent *xevent, ClutterEvent *event, gpointer data) { - CoglXlibFilterReturn ret; ClutterX11FilterReturn retval; + CoglXlibFilterReturn ret; ret = _cogl_xlib_handle_event (xevent); switch (ret) @@ -237,6 +237,7 @@ clutter_backend_x11_create_device_manager (ClutterBackendX11 *backend_x11) &first_event, &first_error)) { +#ifdef HAVE_XINPUT_2 int major = 2; int minor = 0; @@ -251,6 +252,7 @@ clutter_backend_x11_create_device_manager (ClutterBackendX11 *backend_x11) NULL); } else +#endif /* HAVE_XINPUT_2 */ { CLUTTER_NOTE (BACKEND, "Creating Core+XI device manager"); backend_x11->device_manager = @@ -276,6 +278,18 @@ clutter_backend_x11_create_device_manager (ClutterBackendX11 *backend_x11) } } +static void +clutter_backend_x11_create_keymap (ClutterBackendX11 *backend_x11) +{ + if (backend_x11->keymap == NULL) + { + backend_x11->keymap = + g_object_new (CLUTTER_TYPE_KEYMAP_X11, + "backend", backend_x11, + NULL); + } +} + gboolean clutter_backend_x11_pre_parse (ClutterBackend *backend, GError **error) @@ -389,10 +403,7 @@ clutter_backend_x11_post_parse (ClutterBackend *backend, clutter_backend_x11_create_device_manager (backend_x11); /* register keymap */ - backend_x11->keymap = - g_object_new (CLUTTER_TYPE_KEYMAP_X11, - "backend", backend_x11, - NULL); + clutter_backend_x11_create_keymap (backend_x11); /* create XSETTINGS client */ backend_x11->xsettings = @@ -492,6 +503,8 @@ clutter_backend_x11_finalize (GObject *gobject) g_free (backend_x11->display_name); + clutter_x11_remove_filter (cogl_xlib_filter, NULL); + clutter_x11_remove_filter (xsettings_filter, backend_x11); _clutter_xsettings_client_destroy (backend_x11->xsettings); @@ -528,7 +541,7 @@ clutter_backend_x11_constructor (GType gtype, GObjectClass *parent_class; GObject *retval; - if (!backend_singleton) + if (backend_singleton == NULL) { parent_class = G_OBJECT_CLASS (clutter_backend_x11_parent_class); retval = parent_class->constructor (gtype, n_params, params); @@ -797,8 +810,8 @@ clutter_x11_set_display (Display *xdpy) { if (_clutter_context_is_initialized ()) { - g_critical ("Display connection already exists. You can only call " - "clutter_x11_set_display() before clutter_init()"); + g_warning ("%s() can only be used before calling clutter_init()", + G_STRFUNC); return; } @@ -824,6 +837,14 @@ clutter_x11_set_display (Display *xdpy) void clutter_x11_enable_xinput (void) { + if (_clutter_context_is_initialized ()) + { + g_warning ("%s() can only be used before calling clutter_init()", + G_STRFUNC); + return; + } + + clutter_enable_xinput = TRUE; } /** @@ -856,8 +877,8 @@ clutter_x11_disable_event_retrieval (void) { if (_clutter_context_is_initialized ()) { - g_warning ("clutter_x11_disable_event_retrieval() can only be " - "called before clutter_init()"); + g_warning ("%s() can only be used before calling clutter_init()", + G_STRFUNC); return; } @@ -1067,7 +1088,7 @@ clutter_x11_has_composite_extension (void) if (done_check) return have_composite; - if (!backend_singleton) + if (!_clutter_context_is_initialized ()) { g_critical ("X11 backend has not been initialised"); return FALSE; From de93f721c1314321bf78acec2f50d5a481210ec9 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 18 Jan 2011 12:59:49 +0000 Subject: [PATCH 07/41] Clean up the copyright and licensing blurbs --- clutter/glx/clutter-stage-glx.c | 9 ++++---- clutter/x11/clutter-device-manager-core-x11.c | 2 +- clutter/x11/clutter-device-manager-core-x11.h | 2 +- clutter/x11/clutter-device-manager-xi2.c | 2 +- clutter/x11/clutter-device-manager-xi2.h | 2 +- clutter/x11/clutter-input-device-core-x11.c | 23 +++++++++++++++++++ 6 files changed, 32 insertions(+), 8 deletions(-) diff --git a/clutter/glx/clutter-stage-glx.c b/clutter/glx/clutter-stage-glx.c index e0954b2e9..f3dc2cb3f 100644 --- a/clutter/glx/clutter-stage-glx.c +++ b/clutter/glx/clutter-stage-glx.c @@ -119,7 +119,8 @@ clutter_stage_glx_realize (ClutterStageWindow *stage_window) /* Try and create a GLXWindow to use with extensions dependent on * GLX versions >= 1.3 that don't accept regular X Windows as GLX - * drawables. */ + * drawables. + */ if (glXQueryVersion (backend_x11->xdpy, &major, &minor) && major == 1 && minor >= 3 && _clutter_backend_glx_get_fbconfig (backend_glx, &config)) @@ -138,9 +139,9 @@ clutter_stage_glx_realize (ClutterStageWindow *stage_window) ? stage_glx->glxwin : stage_x11->xwin; - /* similarly to above, we unconditionally select this event - * because we rely on it to advance the master clock, and - * drive redraw/relayout, animations and event handling. + /* we unconditionally select this event because we rely on it to + * advance the master clock, and drive redraw/relayout, animations + * and event handling. */ glXSelectEvent (backend_x11->xdpy, drawable, diff --git a/clutter/x11/clutter-device-manager-core-x11.c b/clutter/x11/clutter-device-manager-core-x11.c index 8d005c63f..a13d89480 100644 --- a/clutter/x11/clutter-device-manager-core-x11.c +++ b/clutter/x11/clutter-device-manager-core-x11.c @@ -3,7 +3,7 @@ * * An OpenGL based 'interactive canvas' library. * - * Copyright (C) 2009 Intel Corp. + * Copyright © 2009, 2010, 2011 Intel Corp. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/clutter/x11/clutter-device-manager-core-x11.h b/clutter/x11/clutter-device-manager-core-x11.h index 8d659679f..945d470fc 100644 --- a/clutter/x11/clutter-device-manager-core-x11.h +++ b/clutter/x11/clutter-device-manager-core-x11.h @@ -3,7 +3,7 @@ * * An OpenGL based 'interactive canvas' library. * - * Copyright (C) 2009 Intel Corp. + * Copyright © 2009, 2010, 2011 Intel Corp. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/clutter/x11/clutter-device-manager-xi2.c b/clutter/x11/clutter-device-manager-xi2.c index 7ffe3c391..9063cd880 100644 --- a/clutter/x11/clutter-device-manager-xi2.c +++ b/clutter/x11/clutter-device-manager-xi2.c @@ -3,7 +3,7 @@ * * An OpenGL based 'interactive canvas' library. * - * Copyright (C) 2009 Intel Corp. + * Copyright © 2011 Intel Corp. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/clutter/x11/clutter-device-manager-xi2.h b/clutter/x11/clutter-device-manager-xi2.h index f516fccf0..ec54cbdb4 100644 --- a/clutter/x11/clutter-device-manager-xi2.h +++ b/clutter/x11/clutter-device-manager-xi2.h @@ -3,7 +3,7 @@ * * An OpenGL based 'interactive canvas' library. * - * Copyright (C) 2009 Intel Corp. + * Copyright © 2011 Intel Corp. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/clutter/x11/clutter-input-device-core-x11.c b/clutter/x11/clutter-input-device-core-x11.c index 5d729e992..573300c0c 100644 --- a/clutter/x11/clutter-input-device-core-x11.c +++ b/clutter/x11/clutter-input-device-core-x11.c @@ -1,3 +1,26 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Copyright © 2010, 2011 Intel Corp. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Author: Emmanuele Bassi + */ + #include "config.h" #include "clutter-input-device-core-x11.h" From 0075c45ca49569988fb6b8f1b4a60cd4e77be287 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 18 Jan 2011 13:01:27 +0000 Subject: [PATCH 08/41] device/xi2: Blow the cached devices list Every time we add or remove a device we should clear the cached list that we return in get_devices(), so that it gets repopulated. --- clutter/x11/clutter-device-manager-xi2.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/clutter/x11/clutter-device-manager-xi2.c b/clutter/x11/clutter-device-manager-xi2.c index 9063cd880..0b033f449 100644 --- a/clutter/x11/clutter-device-manager-xi2.c +++ b/clutter/x11/clutter-device-manager-xi2.c @@ -272,6 +272,10 @@ add_device (ClutterDeviceManagerXI2 *manager_xi2, _clutter_input_device_add_slave (master, device); } + /* blow the cache */ + g_slist_free (manager_xi2->all_devices); + manager_xi2->all_devices = NULL; + g_signal_emit_by_name (manager_xi2, "device-added", device); } @@ -294,6 +298,10 @@ remove_device (ClutterDeviceManagerXI2 *manager_xi2, manager_xi2->slave_devices = g_list_remove (manager_xi2->slave_devices, device); + /* blow the cache */ + g_slist_free (manager_xi2->all_devices); + manager_xi2->all_devices = NULL; + g_signal_emit_by_name (manager_xi2, "device-removed", device); g_object_run_dispose (G_OBJECT (device)); From 9c48486d7ac73aa55b69a70cf1ef7009a0af2a09 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 18 Jan 2011 13:02:15 +0000 Subject: [PATCH 09/41] device/xi2: Remove the ::remove implementation Removing a device is only internal API, and we already have a function for that: we don't need to implement the DeviceManager virtual as well. --- clutter/x11/clutter-device-manager-xi2.c | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/clutter/x11/clutter-device-manager-xi2.c b/clutter/x11/clutter-device-manager-xi2.c index 0b033f449..c21aa4218 100644 --- a/clutter/x11/clutter-device-manager-xi2.c +++ b/clutter/x11/clutter-device-manager-xi2.c @@ -851,28 +851,14 @@ static void clutter_device_manager_xi2_add_device (ClutterDeviceManager *manager, ClutterInputDevice *device) { + /* XXX implement */ } static void clutter_device_manager_xi2_remove_device (ClutterDeviceManager *manager, ClutterInputDevice *device) { - ClutterDeviceManagerXI2 *manager_xi2 = CLUTTER_DEVICE_MANAGER_XI2 (manager); - gint device_id; - - device_id = clutter_input_device_get_device_id (device); - - manager_xi2->master_devices = - g_list_remove (manager_xi2->master_devices, device); - manager_xi2->slave_devices = - g_list_remove (manager_xi2->slave_devices, device); - - g_signal_emit_by_name (manager_xi2, "device-removed", device); - - g_object_run_dispose (G_OBJECT (device)); - - g_hash_table_remove (manager_xi2->devices_by_id, - GINT_TO_POINTER (device_id)); + /* XXX implement */ } static const GSList * From 2cb0077f7d7260109f0974d260cb4ed63ade6e9e Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 18 Jan 2011 13:03:07 +0000 Subject: [PATCH 10/41] device/xi2: Implement get_core_device() We ask XI2 to get the client pointer for CLUTTER_POINTER_DEVICE, and we use the attached keyboard device for CLUTTER_KEYBOARD_DEVICE. For everything else, we return NULL. --- clutter/x11/clutter-device-manager-xi2.c | 25 ++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/clutter/x11/clutter-device-manager-xi2.c b/clutter/x11/clutter-device-manager-xi2.c index c21aa4218..dbdf67024 100644 --- a/clutter/x11/clutter-device-manager-xi2.c +++ b/clutter/x11/clutter-device-manager-xi2.c @@ -896,6 +896,31 @@ static ClutterInputDevice * clutter_device_manager_xi2_get_core_device (ClutterDeviceManager *manager, ClutterInputDeviceType device_type) { + ClutterDeviceManagerXI2 *manager_xi2 = CLUTTER_DEVICE_MANAGER_XI2 (manager); + ClutterBackendX11 *backend_x11; + ClutterInputDevice *device; + int device_id; + + backend_x11 = + CLUTTER_BACKEND_X11 (_clutter_device_manager_get_backend (manager)); + + XIGetClientPointer (backend_x11->xdpy, None, &device_id); + + device = g_hash_table_lookup (manager_xi2->devices_by_id, + GINT_TO_POINTER (device_id)); + + switch (device_type) + { + case CLUTTER_POINTER_DEVICE: + return device; + + case CLUTTER_KEYBOARD_DEVICE: + return clutter_input_device_get_associated_device (device); + + default: + break; + } + return NULL; } From ee15d0d2d01a02e426cbb8c340738e4f8411fc4f Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 18 Jan 2011 13:05:17 +0000 Subject: [PATCH 11/41] Add copyright and licensing blurbs --- clutter/x11/clutter-input-device-core-x11.h | 23 +++++++++++++++++++++ clutter/x11/clutter-input-device-xi2.c | 23 +++++++++++++++++++++ clutter/x11/clutter-input-device-xi2.h | 23 +++++++++++++++++++++ 3 files changed, 69 insertions(+) diff --git a/clutter/x11/clutter-input-device-core-x11.h b/clutter/x11/clutter-input-device-core-x11.h index fc8610677..14354dfbb 100644 --- a/clutter/x11/clutter-input-device-core-x11.h +++ b/clutter/x11/clutter-input-device-core-x11.h @@ -1,3 +1,26 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Copyright © 2010, 2011 Intel Corp. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Author: Emmanuele Bassi + */ + #ifndef __CLUTTER_INPUT_DEVICE_X11_H__ #define __CLUTTER_INPUT_DEVICE_X11_H__ diff --git a/clutter/x11/clutter-input-device-xi2.c b/clutter/x11/clutter-input-device-xi2.c index 45d5321a9..a17d2bd46 100644 --- a/clutter/x11/clutter-input-device-xi2.c +++ b/clutter/x11/clutter-input-device-xi2.c @@ -1,3 +1,26 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Copyright © 2011 Intel Corp. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Author: Emmanuele Bassi + */ + #include "config.h" #include "clutter-input-device-xi2.h" diff --git a/clutter/x11/clutter-input-device-xi2.h b/clutter/x11/clutter-input-device-xi2.h index 8daf86d9e..b16e172b9 100644 --- a/clutter/x11/clutter-input-device-xi2.h +++ b/clutter/x11/clutter-input-device-xi2.h @@ -1,3 +1,26 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Copyright © 2011 Intel Corp. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Author: Emmanuele Bassi + */ + #ifndef __CLUTTER_INPUT_DEVICE_XI2_H__ #define __CLUTTER_INPUT_DEVICE_XI2_H__ From c9c6236d3786ed3f69f170b02523e08a91b7f644 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 18 Jan 2011 13:05:46 +0000 Subject: [PATCH 12/41] stage/x11: Do not check a boolean for equality Booleans should only be used like direct conditions, never as comparisons with TRUE or FALSE. --- clutter/x11/clutter-stage-x11.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/clutter/x11/clutter-stage-x11.c b/clutter/x11/clutter-stage-x11.c index 56240cc99..5142e4085 100644 --- a/clutter/x11/clutter-stage-x11.c +++ b/clutter/x11/clutter-stage-x11.c @@ -19,9 +19,7 @@ * */ -#ifdef HAVE_CONFIG_H #include "config.h" -#endif #include @@ -577,7 +575,7 @@ clutter_stage_x11_set_cursor_visible (ClutterStageWindow *stage_window, { ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window); - stage_x11->is_cursor_visible = (cursor_visible == TRUE); + stage_x11->is_cursor_visible = !!cursor_visible; set_cursor_visible (stage_x11); } From c044e44dc798a4afede519abe430ff6a20bd745d Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 18 Jan 2011 13:07:36 +0000 Subject: [PATCH 13/41] device/x11: Use similar core for axis translation as XI2 --- clutter/x11/clutter-input-device-core-x11.c | 39 ++++++++++----------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/clutter/x11/clutter-input-device-core-x11.c b/clutter/x11/clutter-input-device-core-x11.c index 573300c0c..fa8be52a5 100644 --- a/clutter/x11/clutter-input-device-core-x11.c +++ b/clutter/x11/clutter-input-device-core-x11.c @@ -234,24 +234,26 @@ update_axes (ClutterInputDeviceX11 *device_x11, device_x11->axis_data[first_axis + i] = axes_data[i]; } -static void +static gdouble * translate_axes (ClutterInputDeviceX11 *device_x11, ClutterStageX11 *stage_x11, - gdouble *event_axes, gfloat *event_x, gfloat *event_y) { ClutterInputDevice *device = CLUTTER_INPUT_DEVICE (device_x11); gint root_x, root_y; gint n_axes, i; - gfloat x, y; + gdouble x, y; + gdouble *retval; if (!_clutter_stage_x11_get_root_coords (stage_x11, &root_x, &root_y)) - return; + return NULL; x = y = 0.0f; n_axes = clutter_input_device_get_n_axes (device); + retval = g_new0 (gdouble, n_axes); + for (i = 0; i < n_axes; i++) { ClutterInputAxis axis; @@ -266,17 +268,17 @@ translate_axes (ClutterInputDeviceX11 *device_x11, root_x, root_y, i, device_x11->axis_data[i], - &event_axes[i]); + &retval[i]); if (axis == CLUTTER_INPUT_AXIS_X) - x = event_axes[i]; + x = retval[i]; else if (axis == CLUTTER_INPUT_AXIS_Y) - y = event_axes[i]; + y = retval[i]; break; default: _clutter_input_device_translate_axis (device, i, device_x11->axis_data[i], - &event_axes[i]); + &retval[i]); break; } } @@ -286,6 +288,8 @@ translate_axes (ClutterInputDeviceX11 *device_x11, if (event_y) *event_y = y; + + return retval; } /* @@ -327,17 +331,14 @@ _clutter_input_device_x11_translate_xi_event (ClutterInputDeviceX11 *device_x11, event->button.modifier_state = translate_state (xdbe->state, xdbe->device_state); - event->button.axes = - g_new0 (gdouble, clutter_input_device_get_n_axes (device)); - update_axes (device_x11, xdbe->axes_count, xdbe->first_axis, xdbe->axis_data); - translate_axes (device_x11, stage_x11, - event->button.axes, - &event->button.x, - &event->button.y); + + event->button.axes = translate_axes (device_x11, stage_x11, + &event->button.x, + &event->button.y); _clutter_stage_x11_set_user_time (stage_x11, event->button.time); @@ -400,12 +401,10 @@ _clutter_input_device_x11_translate_xi_event (ClutterInputDeviceX11 *device_x11, xdme->axes_count, xdme->first_axis, xdme->axis_data); - translate_axes (device_x11, stage_x11, - event->motion.axes, - &event->motion.x, - &event->motion.y); - _clutter_stage_x11_set_user_time (stage_x11, event->motion.time); + event->motion.axes = translate_axes (device_x11, stage_x11, + &event->motion.x, + &event->motion.y); return TRUE; } From c8cc03f8a7b13db01a71c4a597c05a7b94ba664c Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 18 Jan 2011 13:08:09 +0000 Subject: [PATCH 14/41] event/x11: Epic clean up Event translation is now done where it belongs: we don't need a massive switch in a file with direct access to private structure members. So long, event_translate(); and thanks for all the fish. --- clutter/x11/clutter-event-x11.c | 869 -------------------------------- 1 file changed, 869 deletions(-) diff --git a/clutter/x11/clutter-event-x11.c b/clutter/x11/clutter-event-x11.c index b5bd31306..14653d6df 100644 --- a/clutter/x11/clutter-event-x11.c +++ b/clutter/x11/clutter-event-x11.c @@ -24,45 +24,21 @@ * Emmanuele Bassi */ -#ifdef HAVE_CONFIG_H #include "config.h" -#endif -#include "clutter-stage-x11.h" #include "clutter-backend-x11.h" -#include "clutter-keymap-x11.h" #include "clutter-x11.h" -#include "clutter-actor-private.h" #include "clutter-backend-private.h" #include "clutter-debug.h" -#include "clutter-device-manager-private.h" #include "clutter-event.h" #include "clutter-main.h" -#include "clutter-paint-volume-private.h" #include "clutter-private.h" -#include "clutter-stage-private.h" - -#include "cogl/cogl-internal.h" #include #include -#ifdef HAVE_XFIXES -#include -#endif - -#include - -#ifdef HAVE_XINPUT -#include -#endif - -#ifdef HAVE_XKB -#include -#endif - #if 0 /* XEMBED protocol support for toolkit embedding */ #define XEMBED_MAPPED (1 << 0) @@ -251,847 +227,6 @@ _clutter_backend_x11_events_uninit (ClutterBackend *backend) } } -#if 0 -static void -update_last_event_time (ClutterBackendX11 *backend_x11, - XEvent *xevent) -{ - Time current_time = CurrentTime; - Time last_time = backend_x11->last_event_time; - - switch (xevent->type) - { - case KeyPress: - case KeyRelease: - current_time = xevent->xkey.time; - break; - - case ButtonPress: - case ButtonRelease: - current_time = xevent->xbutton.time; - break; - - case MotionNotify: - current_time = xevent->xmotion.time; - break; - - case EnterNotify: - case LeaveNotify: - current_time = xevent->xcrossing.time; - break; - - case PropertyNotify: - current_time = xevent->xproperty.time; - break; - - default: - break; - } - - /* only change the current event time if it's after the previous event - * time, or if it is at least 30 seconds earlier - in case the system - * clock was changed - */ - if ((current_time != CurrentTime) && - (current_time > last_time || (last_time - current_time > (30 * 1000)))) - backend_x11->last_event_time = current_time; -} - -static void -set_user_time (ClutterBackendX11 *backend_x11, - Window *xwindow, - long timestamp) -{ - if (timestamp != CLUTTER_CURRENT_TIME) - { - XChangeProperty (backend_x11->xdpy, *xwindow, - backend_x11->atom_NET_WM_USER_TIME, - XA_CARDINAL, 32, PropModeReplace, - (unsigned char *) ×tamp, 1); - } -} - -#ifdef HAVE_XINPUT -static void -convert_xdevicekey_to_xkey (XDeviceKeyEvent *xkev, - XEvent *xevent) -{ - xevent->xany.type = xevent->xkey.type = xkev->type; - xevent->xkey.serial = xkev->serial; - xevent->xkey.display = xkev->display; - xevent->xkey.window = xkev->window; - xevent->xkey.root = xkev->root; - xevent->xkey.subwindow = xkev->subwindow; - xevent->xkey.time = xkev->time; - xevent->xkey.x = xkev->x; - xevent->xkey.y = xkev->y; - xevent->xkey.x_root = xkev->x_root; - xevent->xkey.y_root = xkev->y_root; - xevent->xkey.state = xkev->state; - xevent->xkey.keycode = xkev->keycode; - xevent->xkey.same_screen = xkev->same_screen; -} -#endif /* HAVE_XINPUT */ - -static inline void -translate_key_event (ClutterBackendX11 *backend_x11, - ClutterEvent *event, - XEvent *xevent) -{ - ClutterEventX11 *event_x11; - char buffer[256 + 1]; - int n; - - /* KeyEvents have platform specific data associated to them */ - event_x11 = _clutter_event_x11_new (); - _clutter_event_set_platform_data (event, event_x11); - - event->key.time = xevent->xkey.time; - event->key.modifier_state = (ClutterModifierType) xevent->xkey.state; - event->key.hardware_keycode = xevent->xkey.keycode; - - /* keyval is the key ignoring all modifiers ('1' vs. '!') */ - event->key.keyval = - _clutter_keymap_x11_translate_key_state (backend_x11->keymap, - event->key.hardware_keycode, - event->key.modifier_state, - NULL); - - event_x11->key_group = - _clutter_keymap_x11_get_key_group (backend_x11->keymap, - event->key.modifier_state); - event_x11->key_is_modifier = - _clutter_keymap_x11_get_is_modifier (backend_x11->keymap, - event->key.hardware_keycode); - event_x11->num_lock_set = - _clutter_keymap_x11_get_num_lock_state (backend_x11->keymap); - event_x11->caps_lock_set = - _clutter_keymap_x11_get_caps_lock_state (backend_x11->keymap); - - /* unicode_value is the printable representation */ - n = XLookupString (&xevent->xkey, buffer, sizeof (buffer) - 1, NULL, NULL); - - if (n != NoSymbol) - { - event->key.unicode_value = g_utf8_get_char_validated (buffer, n); - if ((event->key.unicode_value != -1) && - (event->key.unicode_value != -2)) - goto out; - } - else - event->key.unicode_value = (gunichar)'\0'; - -out: - return; -} - -static gboolean -handle_wm_protocols_event (ClutterBackendX11 *backend_x11, - Window window, - XEvent *xevent) -{ - Atom atom = (Atom) xevent->xclient.data.l[0]; - - if (atom == backend_x11->atom_WM_DELETE_WINDOW && - xevent->xany.window == window) - { - /* the WM_DELETE_WINDOW is a request: we do not destroy - * the window right away, as it might contain vital data; - * we relay the event to the application and we let it - * handle the request - */ - CLUTTER_NOTE (EVENT, "delete window:\txid: %ld", - xevent->xclient.window); - - set_user_time (backend_x11, - &window, - xevent->xclient.data.l[1]); - - return TRUE; - } - else if (atom == backend_x11->atom_NET_WM_PING && - xevent->xany.window == window) - { - XClientMessageEvent xclient = xevent->xclient; - - xclient.window = backend_x11->xwin_root; - XSendEvent (backend_x11->xdpy, xclient.window, - False, - SubstructureRedirectMask | SubstructureNotifyMask, - (XEvent *) &xclient); - return FALSE; - } - - /* do not send any of the WM_PROTOCOLS events to the queue */ - return FALSE; -} - -static gboolean -clipped_redraws_cool_off_cb (void *data) -{ - ClutterStageX11 *stage_x11 = data; - - stage_x11->clipped_redraws_cool_off = 0; - - return FALSE; -} - -static gboolean -event_translate (ClutterBackend *backend, - ClutterEvent *event, - XEvent *xevent) -{ - ClutterBackendX11 *backend_x11; - ClutterStageX11 *stage_x11; - ClutterStage *stage; - ClutterStageWindow *impl; - ClutterDeviceManager *manager; - gboolean res, not_yet_handled = FALSE; - Window xwindow, stage_xwindow; - ClutterInputDevice *device; - - backend_x11 = CLUTTER_BACKEND_X11 (backend); - - xwindow = xevent->xany.window; - - if (backend_x11->event_filters) - { - GSList *node; - - node = backend_x11->event_filters; - - while (node) - { - ClutterX11EventFilter *filter = node->data; - - switch (filter->func (xevent, event, filter->data)) - { - case CLUTTER_X11_FILTER_CONTINUE: - break; - case CLUTTER_X11_FILTER_TRANSLATE: - return TRUE; - case CLUTTER_X11_FILTER_REMOVE: - return FALSE; - default: - break; - } - - node = node->next; - } - } - - /* Pass the event through Cogl */ - if (_cogl_xlib_handle_event (xevent) == COGL_XLIB_FILTER_REMOVE) - return FALSE; - - /* Do further processing only on events for the stage window (the x11 - * filters might be getting events for other windows, so do not mess - * them about. - */ - stage = clutter_x11_get_stage_from_window (xwindow); - if (stage == NULL) - return FALSE; - - impl = _clutter_stage_get_window (stage); - stage_x11 = CLUTTER_STAGE_X11 (impl); - stage_xwindow = xwindow; /* clutter_x11_get_stage_window (stage); */ - - event->any.stage = stage; - - res = TRUE; - - update_last_event_time (backend_x11, xevent); - - manager = clutter_device_manager_get_default (); - - switch (xevent->type) - { - case ConfigureNotify: - if (!stage_x11->is_foreign_xwin) - { - CLUTTER_NOTE (BACKEND, "%s: ConfigureNotify[%x] (%d, %d)", - G_STRLOC, - (unsigned int) stage_x11->xwin, - xevent->xconfigure.width, - xevent->xconfigure.height); - - /* Queue a relayout - we want glViewport to be called - * with the correct values, and this is done in ClutterStage - * via _cogl_onscreen_clutter_backend_set_size (). - * - * We queue a relayout, because if this ConfigureNotify is - * in response to a size we set in the application, the - * set_size above is essentially a null-op. - * - * Make sure we do this only when the size has changed, - * otherwise we end up relayouting on window moves. - */ - if ((stage_x11->state & CLUTTER_STAGE_STATE_FULLSCREEN) || - (stage_x11->xwin_width != xevent->xconfigure.width) || - (stage_x11->xwin_height != xevent->xconfigure.height)) - { - clutter_actor_queue_relayout (CLUTTER_ACTOR (stage)); - } - - /* If we're fullscreened, we want these variables to - * represent the size of the window before it was set - * to fullscreen. - */ - if (!(stage_x11->state & CLUTTER_STAGE_STATE_FULLSCREEN)) - { - stage_x11->xwin_width = xevent->xconfigure.width; - stage_x11->xwin_height = xevent->xconfigure.height; - } - - clutter_actor_set_size (CLUTTER_ACTOR (stage), - xevent->xconfigure.width, - xevent->xconfigure.height); - - /* XXX: This is a workaround for a race condition when - * resizing windows while there are in-flight - * glXCopySubBuffer blits happening. - * - * The problem stems from the fact that rectangles for the - * blits are described relative to the bottom left of the - * window and because we can't guarantee control over the X - * window gravity used when resizing so the gravity is - * typically NorthWest not SouthWest. - * - * This means if you grow a window vertically the server - * will make sure to place the old contents of the window - * at the top-left/north-west of your new larger window, but - * that may happen asynchronous to GLX preparing to do a - * blit specified relative to the bottom-left/south-west of - * the window (based on the old smaller window geometry). - * - * When the GLX issued blit finally happens relative to the - * new bottom of your window, the destination will have - * shifted relative to the top-left where all the pixels you - * care about are so it will result in a nasty artefact - * making resizing look very ugly! - * - * We can't currently fix this completely, in-part because - * the window manager tends to trample any gravity we might - * set. This workaround instead simply disables blits for a - * while if we are notified of any resizes happening so if - * the user is resizing a window via the window manager then - * they may see an artefact for one frame but then we will - * fallback to redrawing the full stage until the cooling - * off period is over. - */ - if (stage_x11->clipped_redraws_cool_off) - g_source_remove (stage_x11->clipped_redraws_cool_off); - stage_x11->clipped_redraws_cool_off = - g_timeout_add_seconds (1, clipped_redraws_cool_off_cb, stage_x11); - - CLUTTER_UNSET_PRIVATE_FLAGS (stage_x11->wrapper, - CLUTTER_IN_RESIZE); - - /* the resize process is complete, so we can ask the stage - * to set up the GL viewport with the new size - */ - clutter_stage_ensure_viewport (stage); - } - res = FALSE; - break; - - case PropertyNotify: - if (xevent->xproperty.atom == backend_x11->atom_NET_WM_STATE && - xevent->xproperty.window == stage_xwindow && - !stage_x11->is_foreign_xwin) - { - Atom type; - gint format; - gulong n_items, bytes_after; - guchar *data = NULL; - gboolean fullscreen_set = FALSE; - - clutter_x11_trap_x_errors (); - XGetWindowProperty (backend_x11->xdpy, stage_xwindow, - backend_x11->atom_NET_WM_STATE, - 0, G_MAXLONG, - False, XA_ATOM, - &type, &format, &n_items, - &bytes_after, &data); - clutter_x11_untrap_x_errors (); - - if (type != None && data != NULL) - { - Atom *atoms = (Atom *) data; - gulong i; - gboolean is_fullscreen = FALSE; - - for (i = 0; i < n_items; i++) - { - if (atoms[i] == backend_x11->atom_NET_WM_STATE_FULLSCREEN) - fullscreen_set = TRUE; - } - - is_fullscreen = - (stage_x11->state & CLUTTER_STAGE_STATE_FULLSCREEN); - - if (fullscreen_set != is_fullscreen) - { - if (fullscreen_set) - stage_x11->state |= CLUTTER_STAGE_STATE_FULLSCREEN; - else - stage_x11->state &= ~CLUTTER_STAGE_STATE_FULLSCREEN; - - stage_x11->fullscreening = fullscreen_set; - - event->type = CLUTTER_STAGE_STATE; - event->stage_state.changed_mask = - CLUTTER_STAGE_STATE_FULLSCREEN; - event->stage_state.new_state = stage_x11->state; - } - else - res = FALSE; - - XFree (data); - } - else - res = FALSE; - } - else - res = FALSE; - break; - - case MapNotify: - res = FALSE; - break; - - case UnmapNotify: - res = FALSE; - break; - - case FocusIn: - if (!(stage_x11->state & CLUTTER_STAGE_STATE_ACTIVATED)) - { - /* TODO: check xevent->xfocus.detail ? */ - stage_x11->state |= CLUTTER_STAGE_STATE_ACTIVATED; - - event->type = CLUTTER_STAGE_STATE; - event->stage_state.changed_mask = CLUTTER_STAGE_STATE_ACTIVATED; - event->stage_state.new_state = stage_x11->state; - } - else - res = FALSE; - break; - - case FocusOut: - if (stage_x11->state & CLUTTER_STAGE_STATE_ACTIVATED) - { - stage_x11->state &= ~CLUTTER_STAGE_STATE_ACTIVATED; - - event->type = CLUTTER_STAGE_STATE; - event->stage_state.changed_mask = CLUTTER_STAGE_STATE_ACTIVATED; - event->stage_state.new_state = stage_x11->state; - } - else - res = FALSE; - break; - - case Expose: - { - XExposeEvent *expose = (XExposeEvent *)xevent; - ClutterPaintVolume clip; - ClutterVertex origin; - - CLUTTER_NOTE (MULTISTAGE, "expose for stage: %p, redrawing", stage); - CLUTTER_NOTE (MULTISTAGE, - "expose for stage: %p, " - "redrawing (x=%d, y=%d, width=%d, height=%d)", - stage, - expose->x, - expose->y, - expose->width, - expose->height); - - origin.x = expose->x; - origin.y = expose->y; - origin.z = 0; - - _clutter_paint_volume_init_static (CLUTTER_ACTOR (stage), &clip); - - clutter_paint_volume_set_origin (&clip, &origin); - clutter_paint_volume_set_width (&clip, expose->width); - clutter_paint_volume_set_height (&clip, expose->height); - - _clutter_actor_queue_redraw_with_clip (CLUTTER_ACTOR (stage), 0, &clip); - - clutter_paint_volume_free (&clip); - - res = FALSE; - } - break; - case DestroyNotify: - CLUTTER_NOTE (EVENT, "destroy notify:\txid: %ld", - xevent->xdestroywindow.window); - if (xevent->xdestroywindow.window == stage_xwindow && - !stage_x11->is_foreign_xwin) - event->type = event->any.type = CLUTTER_DESTROY_NOTIFY; - else - res = FALSE; - break; - - case ClientMessage: - CLUTTER_NOTE (EVENT, "client message"); - - event->type = event->any.type = CLUTTER_CLIENT_MESSAGE; - - if (xevent->xclient.message_type == backend_x11->atom_XEMBED) - res = handle_xembed_event (backend_x11, xevent); - else if (xevent->xclient.message_type == backend_x11->atom_WM_PROTOCOLS) - { - res = handle_wm_protocols_event (backend_x11, stage_xwindow, xevent); - event->type = event->any.type = CLUTTER_DELETE; - } - break; - - case KeyPress: - event->key.type = event->type = CLUTTER_KEY_PRESS; - event->key.device = - clutter_device_manager_get_core_device (manager, - CLUTTER_KEYBOARD_DEVICE); - - translate_key_event (backend_x11, event, xevent); - - set_user_time (backend_x11, &xwindow, xevent->xkey.time); - break; - - case KeyRelease: - /* old-style X11 terminals require that even modern X11 send - * KeyPress/KeyRelease pairs when auto-repeating. for this - * reason modern(-ish) API like XKB has a way to detect - * auto-repeat and do a single KeyRelease at the end of a - * KeyPress sequence. - * - * this check emulates XKB's detectable auto-repeat; we peek - * the next event and check if it's a KeyPress for the same key - * and timestamp - and then ignore it if it matches the - * KeyRelease - * - * if we have XKB, and autorepeat is enabled, then this becomes - * a no-op - */ - if (!backend_x11->have_xkb_autorepeat && XPending (xevent->xkey.display)) - { - XEvent next_event; - - XPeekEvent (xevent->xkey.display, &next_event); - - if (next_event.type == KeyPress && - next_event.xkey.keycode == xevent->xkey.keycode && - next_event.xkey.time == xevent->xkey.time) - { - res = FALSE; - break; - } - } - - event->key.type = event->type = CLUTTER_KEY_RELEASE; - event->key.device = - clutter_device_manager_get_core_device (manager, - CLUTTER_KEYBOARD_DEVICE); - - translate_key_event (backend_x11, event, xevent); - break; - - default: - /* ignore every other event */ - not_yet_handled = TRUE; - break; - } - - /* Input device event handling.. */ - if (not_yet_handled) - { - device = clutter_device_manager_get_core_device (manager, - CLUTTER_POINTER_DEVICE); - - /* Regular X event */ - switch (xevent->type) - { - case ButtonPress: - switch (xevent->xbutton.button) - { - case 4: /* up */ - case 5: /* down */ - case 6: /* left */ - case 7: /* right */ - event->scroll.type = event->type = CLUTTER_SCROLL; - - if (xevent->xbutton.button == 4) - event->scroll.direction = CLUTTER_SCROLL_UP; - else if (xevent->xbutton.button == 5) - event->scroll.direction = CLUTTER_SCROLL_DOWN; - else if (xevent->xbutton.button == 6) - event->scroll.direction = CLUTTER_SCROLL_LEFT; - else - event->scroll.direction = CLUTTER_SCROLL_RIGHT; - - event->scroll.time = xevent->xbutton.time; - event->scroll.x = xevent->xbutton.x; - event->scroll.y = xevent->xbutton.y; - event->scroll.modifier_state = xevent->xbutton.state; - event->scroll.device = device; - break; - - default: - event->button.type = event->type = CLUTTER_BUTTON_PRESS; - event->button.time = xevent->xbutton.time; - event->button.x = xevent->xbutton.x; - event->button.y = xevent->xbutton.y; - event->button.modifier_state = xevent->xbutton.state; - event->button.button = xevent->xbutton.button; - event->button.device = device; - break; - } - - set_user_time (backend_x11, &xwindow, event->button.time); - - res = TRUE; - break; - - case ButtonRelease: - /* scroll events don't have a corresponding release */ - if (xevent->xbutton.button == 4 || - xevent->xbutton.button == 5 || - xevent->xbutton.button == 6 || - xevent->xbutton.button == 7) - { - res = FALSE; - goto out; - } - - event->button.type = event->type = CLUTTER_BUTTON_RELEASE; - event->button.time = xevent->xbutton.time; - event->button.x = xevent->xbutton.x; - event->button.y = xevent->xbutton.y; - event->button.modifier_state = xevent->xbutton.state; - event->button.button = xevent->xbutton.button; - event->button.device = device; - - res = TRUE; - break; - - case MotionNotify: - event->motion.type = event->type = CLUTTER_MOTION; - event->motion.time = xevent->xmotion.time; - event->motion.x = xevent->xmotion.x; - event->motion.y = xevent->xmotion.y; - event->motion.modifier_state = xevent->xmotion.state; - event->motion.device = device; - - res = TRUE; - break; - - case EnterNotify: - /* we know that we are entering the stage here */ - _clutter_input_device_set_stage (device, stage); - CLUTTER_NOTE (EVENT, "Entering the stage"); - - /* Convert enter notifies to motion events because X - doesn't emit the corresponding motion notify */ - event->motion.type = event->type = CLUTTER_MOTION; - event->motion.time = xevent->xcrossing.time; - event->motion.x = xevent->xcrossing.x; - event->motion.y = xevent->xcrossing.y; - event->motion.modifier_state = xevent->xcrossing.state; - event->motion.source = CLUTTER_ACTOR (stage); - event->motion.device = device; - - res = TRUE; - break; - - case LeaveNotify: - if (device->stage == NULL) - { - CLUTTER_NOTE (EVENT, - "Discarding LeaveNotify for ButtonRelease " - "event off-stage"); - res = FALSE; - goto out; - } - - /* we know that we are leaving the stage here */ - _clutter_input_device_set_stage (device, NULL); - CLUTTER_NOTE (EVENT, "Leaving the stage (time:%u)", - event->crossing.time); - - event->crossing.type = event->type = CLUTTER_LEAVE; - event->crossing.time = xevent->xcrossing.time; - event->crossing.x = xevent->xcrossing.x; - event->crossing.y = xevent->xcrossing.y; - event->crossing.source = CLUTTER_ACTOR (stage); - event->crossing.device = device; - res = TRUE; - break; - - default: - res = FALSE; - break; - } - } - - /* XInput fun...*/ - if (!res && clutter_x11_has_xinput ()) - { -#ifdef HAVE_XINPUT - int *ev_types = backend_x11->event_types; - int button_press, button_release; - int key_press, key_release; - int motion_notify; - - button_press = ev_types[CLUTTER_X11_XINPUT_BUTTON_PRESS_EVENT]; - button_release = ev_types[CLUTTER_X11_XINPUT_BUTTON_RELEASE_EVENT]; - motion_notify = ev_types[CLUTTER_X11_XINPUT_MOTION_NOTIFY_EVENT]; - key_press = ev_types[CLUTTER_X11_XINPUT_KEY_PRESS_EVENT]; - key_release = ev_types[CLUTTER_X11_XINPUT_KEY_RELEASE_EVENT]; - - CLUTTER_NOTE (EVENT, "XInput event type: %d", xevent->type); - - if (xevent->type == button_press) - { - XDeviceButtonEvent *xbev = (XDeviceButtonEvent *) xevent; - - device = _clutter_x11_get_device_for_xid (xbev->deviceid); - _clutter_input_device_set_stage (device, stage); - - CLUTTER_NOTE (EVENT, - "XI ButtonPress for %li ('%s') at %d, %d", - xbev->deviceid, - device->device_name, - xbev->x, - xbev->y); - - switch (xbev->button) - { - case 4: - case 5: - case 6: - case 7: - event->scroll.type = event->type = CLUTTER_SCROLL; - - if (xbev->button == 4) - event->scroll.direction = CLUTTER_SCROLL_UP; - else if (xbev->button == 5) - event->scroll.direction = CLUTTER_SCROLL_DOWN; - else if (xbev->button == 6) - event->scroll.direction = CLUTTER_SCROLL_LEFT; - else - event->scroll.direction = CLUTTER_SCROLL_RIGHT; - - event->scroll.time = xbev->time; - event->scroll.x = xbev->x; - event->scroll.y = xbev->y; - event->scroll.modifier_state = xbev->state; - event->scroll.device = device; - break; - - default: - event->button.type = event->type = CLUTTER_BUTTON_PRESS; - event->button.time = xbev->time; - event->button.x = xbev->x; - event->button.y = xbev->y; - event->button.modifier_state = xbev->state; - event->button.button = xbev->button; - event->button.device = device; - break; - } - - set_user_time (backend_x11, &xwindow, xbev->time); - - res = TRUE; - } - else if (xevent->type == button_release) - { - XDeviceButtonEvent *xbev = (XDeviceButtonEvent *)xevent; - - device = _clutter_x11_get_device_for_xid (xbev->deviceid); - _clutter_input_device_set_stage (device, stage); - - CLUTTER_NOTE (EVENT, "XI ButtonRelease for %li ('%s') at %d, %d", - xbev->deviceid, - device->device_name, - xbev->x, - xbev->y); - - /* scroll events don't have a corresponding release */ - if (xbev->button == 4 || - xbev->button == 5 || - xbev->button == 6 || - xbev->button == 7) - { - res = FALSE; - goto out; - } - - event->button.type = event->type = CLUTTER_BUTTON_RELEASE; - event->button.time = xbev->time; - event->button.x = xbev->x; - event->button.y = xbev->y; - event->button.modifier_state = xbev->state; - event->button.button = xbev->button; - event->button.device = device; - - res = TRUE; - } - else if (xevent->type == motion_notify) - { - XDeviceMotionEvent *xmev = (XDeviceMotionEvent *)xevent; - - device = _clutter_x11_get_device_for_xid (xmev->deviceid); - _clutter_input_device_set_stage (device, stage); - - CLUTTER_NOTE (EVENT, "XI Motion for %li ('%s') at %d, %d", - xmev->deviceid, - device->device_name, - xmev->x, - xmev->y); - - event->motion.type = event->type = CLUTTER_MOTION; - event->motion.time = xmev->time; - event->motion.x = xmev->x; - event->motion.y = xmev->y; - event->motion.modifier_state = xmev->state; - event->motion.device = device; - - res = TRUE; - } - else if (xevent->type == key_press || xevent->type == key_release) - { - /* the XInput 1.x handling of key presses/releases is broken: - * it makes key repeat, key presses and releases outside the - * window not generate events even when the window has focus - */ - XDeviceKeyEvent *xkev = (XDeviceKeyEvent *) xevent; - XEvent xevent_converted; - - convert_xdevicekey_to_xkey (xkev, &xevent_converted); - - event->key.type = event->type = (xevent->type == key_press) - ? CLUTTER_KEY_PRESS - : CLUTTER_KEY_RELEASE; - - translate_key_event (backend_x11, event, &xevent_converted); - - if (xevent->type == key_press) - set_user_time (backend_x11, &xwindow, xkev->time); - } - else -#endif /* HAVE_XINPUT */ - { - CLUTTER_NOTE (EVENT, "Uknown Event"); - res = FALSE; - } - } - -out: - return res; -} -#endif - static void events_queue (ClutterBackend *backend) { @@ -1137,8 +272,6 @@ events_queue (ClutterBackend *backend) ClutterX11FilterReturn clutter_x11_handle_event (XEvent *xevent) { - ClutterBackendX11Class *backend_x11_class; - ClutterMainContext *clutter_context; ClutterX11FilterReturn result; ClutterBackend *backend; ClutterEvent *event; @@ -1157,9 +290,7 @@ clutter_x11_handle_event (XEvent *xevent) clutter_threads_enter (); - clutter_context = _clutter_context_get_default (); backend = clutter_get_default_backend (); - backend_x11_class = CLUTTER_BACKEND_X11_GET_CLASS (backend); event = clutter_event_new (CLUTTER_NOTHING); From fa3c5a28a7b82d817ae7b83a058158a101e1e4b0 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 18 Jan 2011 13:15:20 +0000 Subject: [PATCH 15/41] backend/x11: Allow querying xinput support Undeprecate the XInput-related X11 API: since we don't enable XI support by default we still need to ask for it, and see if we have it after the backend initialization sequence. --- clutter/x11/clutter-backend-x11.c | 14 ++++++++------ clutter/x11/clutter-backend-x11.h | 5 +---- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/clutter/x11/clutter-backend-x11.c b/clutter/x11/clutter-backend-x11.c index 5162b3d17..cf222714d 100644 --- a/clutter/x11/clutter-backend-x11.c +++ b/clutter/x11/clutter-backend-x11.c @@ -244,7 +244,7 @@ clutter_backend_x11_create_device_manager (ClutterBackendX11 *backend_x11) if (XIQueryVersion (backend_x11->xdpy, &major, &minor) != BadRequest) { CLUTTER_NOTE (BACKEND, "Creating XI2 device manager"); - + backend_x11->has_xinput = TRUE; backend_x11->device_manager = g_object_new (CLUTTER_TYPE_DEVICE_MANAGER_XI2, "backend", backend_x11, @@ -255,11 +255,13 @@ clutter_backend_x11_create_device_manager (ClutterBackendX11 *backend_x11) #endif /* HAVE_XINPUT_2 */ { CLUTTER_NOTE (BACKEND, "Creating Core+XI device manager"); + backend_x11->has_xinput = TRUE; backend_x11->device_manager = g_object_new (CLUTTER_TYPE_DEVICE_MANAGER_X11, "backend", backend_x11, "event-base", first_event, NULL); + } } } @@ -267,6 +269,7 @@ clutter_backend_x11_create_device_manager (ClutterBackendX11 *backend_x11) #endif /* HAVE_XINPUT || HAVE_XINPUT_2 */ { CLUTTER_NOTE (BACKEND, "Creating Core device manager"); + backend_x11->has_xinput = FALSE; backend_x11->device_manager = g_object_new (CLUTTER_TYPE_DEVICE_MANAGER_X11, "backend", backend_x11, @@ -831,8 +834,6 @@ clutter_x11_set_display (Display *xdpy) * want to use clutter_x11_has_xinput() to see if support was enabled. * * Since: 0.8 - * - * Deprecated: 1.6: This function does not do anything. */ void clutter_x11_enable_xinput (void) @@ -1056,14 +1057,15 @@ clutter_x11_get_input_devices (void) * and XInput support is available at run time. * * Since: 0.8 - * - * Deprecated: 1.6 */ gboolean clutter_x11_has_xinput (void) { #if defined(HAVE_XINPUT) || defined(HAVE_XINPUT_2) - return TRUE; + if (backend_singleton != NULL) + return backend_singleton->has_xinput; + + return FALSE; #else return FALSE; #endif diff --git a/clutter/x11/clutter-backend-x11.h b/clutter/x11/clutter-backend-x11.h index 5d710d713..737e13915 100644 --- a/clutter/x11/clutter-backend-x11.h +++ b/clutter/x11/clutter-backend-x11.h @@ -97,13 +97,10 @@ struct _ClutterBackendX11 Atom atom_NET_WM_NAME; Atom atom_UTF8_STRING; - int xi_event_base; - int event_types[CLUTTER_X11_XINPUT_LAST_EVENT]; - gboolean have_xinput; - Time last_event_time; ClutterDeviceManager *device_manager; + gboolean has_xinput; XSettingsClient *xsettings; Window xsettings_xwin; From a3102a777ead15f7ba4b671bd25fd642762fec57 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 18 Jan 2011 14:09:04 +0000 Subject: [PATCH 16/41] docs: Fill out documentation for new symbols --- clutter/clutter-event.c | 82 ++++++++++++++++++++++ clutter/clutter-event.h | 1 + clutter/clutter-input-device.c | 37 ++++++++++ clutter/clutter-input-device.h | 29 ++++++++ clutter/clutter-private.h | 4 ++ clutter/x11/clutter-device-manager-xi2.c | 39 +++++++--- doc/reference/clutter/Makefile.am | 1 + doc/reference/clutter/clutter-sections.txt | 12 +++- 8 files changed, 194 insertions(+), 11 deletions(-) diff --git a/clutter/clutter-event.c b/clutter/clutter-event.c index df823dc1d..40629d6ed 100644 --- a/clutter/clutter-event.c +++ b/clutter/clutter-event.c @@ -47,6 +47,8 @@ typedef struct _ClutterEventPrivate { ClutterEvent base; + ClutterInputDevice *source_device; + gpointer platform_data; } ClutterEventPrivate; @@ -907,3 +909,83 @@ clutter_get_current_event (void) return context->current_event; } + +/** + * clutter_event_get_source_device: + * @event: a #ClutterEvent + * + * Retrieves the hardware device that originated the event. + * + * If you need the virtual device, use clutter_event_get_device(). + * + * If no hardware device originated this event, this function will + * return the same device as clutter_event_get_device(). + * + * Return value: (transfer none): a pointer to a #ClutterInputDevice + * or %NULL + * + * Since: 1.6 + */ +ClutterInputDevice * +clutter_event_get_source_device (const ClutterEvent *event) +{ + ClutterEventPrivate *real_event; + + if (!is_event_allocated (event)) + return NULL; + + real_event = (ClutterEventPrivate *) event; + + if (real_event->source_device != NULL) + return real_event->source_device; + + return clutter_event_get_device (event); +} + +void +_clutter_event_set_device (ClutterEvent *event, + ClutterInputDevice *device) +{ + switch (event->type) + { + case CLUTTER_NOTHING: + case CLUTTER_STAGE_STATE: + case CLUTTER_DESTROY_NOTIFY: + case CLUTTER_CLIENT_MESSAGE: + case CLUTTER_DELETE: + case CLUTTER_ENTER: + case CLUTTER_LEAVE: + break; + + case CLUTTER_BUTTON_PRESS: + case CLUTTER_BUTTON_RELEASE: + event->button.device = device; + break; + + case CLUTTER_MOTION: + event->motion.device = device; + break; + + case CLUTTER_SCROLL: + event->scroll.device = device; + break; + + case CLUTTER_KEY_PRESS: + case CLUTTER_KEY_RELEASE: + event->key.device = device; + break; + } +} + +void +_clutter_event_set_source_device (ClutterEvent *event, + ClutterInputDevice *device) +{ + ClutterEventPrivate *real_event; + + if (!is_event_allocated (event)) + return; + + real_event = (ClutterEventPrivate *) event; + real_event->source_device = device; +} diff --git a/clutter/clutter-event.h b/clutter/clutter-event.h index 38bcd97b6..e7d20eb5e 100644 --- a/clutter/clutter-event.h +++ b/clutter/clutter-event.h @@ -403,6 +403,7 @@ ClutterModifierType clutter_event_get_state (const ClutterEvent gint clutter_event_get_device_id (const ClutterEvent *event); ClutterInputDeviceType clutter_event_get_device_type (const ClutterEvent *event); ClutterInputDevice * clutter_event_get_device (const ClutterEvent *event); +ClutterInputDevice * clutter_event_get_source_device (const ClutterEvent *event); ClutterActor * clutter_event_get_source (const ClutterEvent *event); ClutterStage * clutter_event_get_stage (const ClutterEvent *event); diff --git a/clutter/clutter-input-device.c b/clutter/clutter-input-device.c index 8d9cbeb4a..013ce437b 100644 --- a/clutter/clutter-input-device.c +++ b/clutter/clutter-input-device.c @@ -924,6 +924,16 @@ _clutter_input_device_get_axis (ClutterInputDevice *device, return info->axis; } +/** + * clutter_input_device_get_n_axes: + * @device: a #ClutterInputDevice + * + * Retrieves the number of axes available on @device. + * + * Return value: the number of axes on the device + * + * Since: 1.6 + */ guint clutter_input_device_get_n_axes (ClutterInputDevice *device) { @@ -964,6 +974,20 @@ clutter_input_device_get_n_keys (ClutterInputDevice *device) return 0; } +/** + * clutter_input_device_set_key: + * @device: a #ClutterInputDevice + * @index_: the index of the key + * @keyval: the keyval + * @modifiers: a bitmask of modifiers + * + * Sets the keyval and modifiers at the given @index_ for @device. + * + * Clutter will use the keyval and modifiers set when filling out + * an event coming from the same input device. + * + * Since: 1.6 + */ void clutter_input_device_set_key (ClutterInputDevice *device, guint index_, @@ -982,6 +1006,19 @@ clutter_input_device_set_key (ClutterInputDevice *device, key_info->modifiers = modifiers; } +/** + * clutter_input_device_get_key: + * @device: a #ClutterInputDevice + * @index_: the index of the key + * @keyval: (out): return location for the keyval at @index_ + * @modifiers: (out): return location for the modifiers at @index_ + * + * Retrieves the key set using clutter_input_device_set_key() + * + * Return value: %TRUE if a key was set at the given index + * + * Since: 1.6 + */ gboolean clutter_input_device_get_key (ClutterInputDevice *device, guint index_, diff --git a/clutter/clutter-input-device.h b/clutter/clutter-input-device.h index 232554f13..86621b51f 100644 --- a/clutter/clutter-input-device.h +++ b/clutter/clutter-input-device.h @@ -57,6 +57,9 @@ typedef struct _ClutterInputDeviceClass ClutterInputDeviceClass; * @CLUTTER_TABLET_DEVICE: A tablet device * @CLUTTER_TOUCHPAD_DEVICE: A touchpad device * @CLUTTER_TOUCHSCREEN_DEVICE: A touch screen device + * @CLUTTER_PEN_DEVICE: A pen device + * @CLUTTER_ERASER_DEVICE: An eraser device + * @CLUTTER_CURSOR_DEVICE: A cursor device * @CLUTTER_N_DEVICE_TYPES: The number of device types * * The types of input devices available. @@ -81,12 +84,38 @@ typedef enum { CLUTTER_N_DEVICE_TYPES } ClutterInputDeviceType; +/** + * ClutterInputMode: + * @CLUTTER_INPUT_MODE_MASTER: A master, virtual device + * @CLUTTER_INPUT_MODE_SLAVE: A slave, physical device, attached to + * a master device + * @CLUTTER_INPUT_MODE_FLOATING: A slave, physical device, not attached + * to a master device + * + * The mode for input devices available. + * + * Since: 1.6 + */ typedef enum { CLUTTER_INPUT_MODE_MASTER, CLUTTER_INPUT_MODE_SLAVE, CLUTTER_INPUT_MODE_FLOATING } ClutterInputMode; +/** + * ClutterInputAxis: + * @CLUTTER_INPUT_AXIS_IGNORE: Unused axis + * @CLUTTER_INPUT_AXIS_X: The position on the X axis + * @CLUTTER_INPUT_AXIS_Y: The position of the Y axis + * @CLUTTER_INPUT_AXIS_PRESSURE: The pressure information + * @CLUTTER_INPUT_AXIS_XTILT: The tilt on the X axis + * @CLUTTER_INPUT_AXIS_YTILT: The tile on the Y axis + * @CLUTTER_INPUT_AXIS_WHEEL: A wheel + * + * The type of axes Clutter recognizes on a #ClutterInputDevice + * + * Since: 1.6 + */ typedef enum { CLUTTER_INPUT_AXIS_IGNORE, CLUTTER_INPUT_AXIS_X, diff --git a/clutter/clutter-private.h b/clutter/clutter-private.h index 79b6dcba9..931020e06 100644 --- a/clutter/clutter-private.h +++ b/clutter/clutter-private.h @@ -230,6 +230,10 @@ gpointer _clutter_event_get_platform_data (const ClutterEvent *event); void _clutter_event_push (const ClutterEvent *event, gboolean do_copy); +void _clutter_event_set_device (ClutterEvent *event, + ClutterInputDevice *device); +void _clutter_event_set_source_device (ClutterEvent *event, + ClutterInputDevice *device); void _clutter_util_fully_transform_vertices (const CoglMatrix *modelview, const CoglMatrix *projection, diff --git a/clutter/x11/clutter-device-manager-xi2.c b/clutter/x11/clutter-device-manager-xi2.c index dbdf67024..7641e37b6 100644 --- a/clutter/x11/clutter-device-manager-xi2.c +++ b/clutter/x11/clutter-device-manager-xi2.c @@ -632,9 +632,13 @@ clutter_device_manager_xi2_translate_event (ClutterEventTranslator *translator, event_x11->caps_lock_set = _clutter_keymap_x11_get_caps_lock_state (backend_x11->keymap); + device = g_hash_table_lookup (manager_xi2->devices_by_id, + GINT_TO_POINTER (xev->sourceid)); + _clutter_event_set_source_device (event, device); + device = g_hash_table_lookup (manager_xi2->devices_by_id, GINT_TO_POINTER (xev->deviceid)); - event->key.device = device; + _clutter_event_set_device (event, device); /* XXX keep this in sync with the evdev device manager */ n = print_keysym (event->key.keyval, buffer, sizeof (buffer)); @@ -693,9 +697,6 @@ clutter_device_manager_xi2_translate_event (ClutterEventTranslator *translator, event->scroll.time = xev->time; event->scroll.x = xev->event_x; event->scroll.y = xev->event_y; - event->scroll.device = - g_hash_table_lookup (manager_xi2->devices_by_id, - GINT_TO_POINTER (xev->deviceid)); event->scroll.modifier_state = _clutter_input_device_xi2_translate_state (&xev->mods, &xev->buttons); @@ -704,6 +705,14 @@ clutter_device_manager_xi2_translate_event (ClutterEventTranslator *translator, event->scroll.y, stage_x11, &xev->valuators); + + device = g_hash_table_lookup (manager_xi2->devices_by_id, + GINT_TO_POINTER (xev->sourceid)); + _clutter_event_set_source_device (event, device); + + device = g_hash_table_lookup (manager_xi2->devices_by_id, + GINT_TO_POINTER (xev->deviceid)); + _clutter_event_set_device (event, device); break; default: @@ -717,9 +726,6 @@ clutter_device_manager_xi2_translate_event (ClutterEventTranslator *translator, event->button.x = xev->event_x; event->button.y = xev->event_y; event->button.button = xev->detail; - event->button.device = - g_hash_table_lookup (manager_xi2->devices_by_id, - GINT_TO_POINTER (xev->deviceid)); event->button.modifier_state = _clutter_input_device_xi2_translate_state (&xev->mods, &xev->buttons); @@ -728,6 +734,14 @@ clutter_device_manager_xi2_translate_event (ClutterEventTranslator *translator, event->button.y, stage_x11, &xev->valuators); + + device = g_hash_table_lookup (manager_xi2->devices_by_id, + GINT_TO_POINTER (xev->sourceid)); + _clutter_event_set_source_device (event, device); + + device = g_hash_table_lookup (manager_xi2->devices_by_id, + GINT_TO_POINTER (xev->deviceid)); + _clutter_event_set_device (event, device); break; } @@ -760,13 +774,18 @@ clutter_device_manager_xi2_translate_event (ClutterEventTranslator *translator, event->motion.time = xev->time; event->motion.x = xev->event_x; event->motion.y = xev->event_y; - event->motion.device = - g_hash_table_lookup (manager_xi2->devices_by_id, - GINT_TO_POINTER (xev->deviceid)); event->motion.modifier_state = _clutter_input_device_xi2_translate_state (&xev->mods, &xev->buttons); + device = g_hash_table_lookup (manager_xi2->devices_by_id, + GINT_TO_POINTER (xev->sourceid)); + _clutter_event_set_source_device (event, device); + + device = g_hash_table_lookup (manager_xi2->devices_by_id, + GINT_TO_POINTER (xev->deviceid)); + _clutter_event_set_device (event, device); + CLUTTER_NOTE (EVENT, "motion: win:0x%x device:%s (x:%.2f, y:%.2f)", (unsigned int) stage_x11->xwin, event->motion.device->device_name, diff --git a/doc/reference/clutter/Makefile.am b/doc/reference/clutter/Makefile.am index fe52cac02..adc47850d 100644 --- a/doc/reference/clutter/Makefile.am +++ b/doc/reference/clutter/Makefile.am @@ -75,6 +75,7 @@ IGNORE_HFILES=\ clutter-deprecated.h \ clutter-device-manager-private.h \ clutter-enum-types.h \ + clutter-event-translator.h \ clutter-id-pool.h \ clutter-keysyms.h \ clutter-keysyms-compat.h \ diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index b147c52b5..3dbadb375 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -1056,6 +1056,7 @@ clutter_event_get_scroll_direction clutter_event_get_device clutter_event_get_device_id clutter_event_get_device_type +clutter_event_get_source_device clutter_get_current_event_time @@ -1072,14 +1073,22 @@ clutter_event_get_type clutter-input-device ClutterInputDevice ClutterInputDeviceType +ClutterInputAxis +ClutterInputMode ClutterInputDevice -ClutterInputDeviceClass clutter_input_device_get_device_id clutter_input_device_get_device_type clutter_input_device_get_device_name clutter_input_device_get_device_coords clutter_input_device_get_pointer_actor clutter_input_device_get_pointer_stage +clutter_input_device_set_key +clutter_input_device_get_key +clutter_input_device_get_n_axes +clutter_input_device_get_associated_device +clutter_input_device_get_slave_devices + + clutter_input_device_update_from_event @@ -1091,6 +1100,7 @@ CLUTTER_IS_INPUT_DEVICE_CLASS CLUTTER_INPUT_DEVICE_GET_CLASS +ClutterInputDeviceClass clutter_input_device_get_type From 0a869e0718331737a1126da861222f7b0162fcce Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 18 Jan 2011 14:16:11 +0000 Subject: [PATCH 17/41] egl/x11: Defer X11-specific code to the X11 backend The ClutterStageX11 implementation does most of the heavy lifting, so subclasses like ClutterStageGLX and ClutterStageEGL do not need to handle things like creating the stage Window and selecting events; just chaining up and using the internal API will suffice. --- clutter/egl/clutter-stage-egl.c | 108 +++----------------------------- 1 file changed, 8 insertions(+), 100 deletions(-) diff --git a/clutter/egl/clutter-stage-egl.c b/clutter/egl/clutter-stage-egl.c index 6bf8dd73c..7bb38de26 100644 --- a/clutter/egl/clutter-stage-egl.c +++ b/clutter/egl/clutter-stage-egl.c @@ -42,24 +42,18 @@ clutter_stage_egl_unrealize (ClutterStageWindow *stage_window) ClutterStageEGL *stage_egl = CLUTTER_STAGE_EGL (stage_window); ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window); - CLUTTER_NOTE (BACKEND, "Unrealizing stage"); + CLUTTER_NOTE (BACKEND, "Unrealizing EGL stage [%p]", stage_egl); clutter_x11_trap_x_errors (); - if (!stage_x11->is_foreign_xwin && stage_x11->xwin != None) - { - XDestroyWindow (backend_x11->xdpy, stage_x11->xwin); - stage_x11->xwin = None; - } - else - stage_x11->xwin = None; - if (stage_egl->egl_surface != EGL_NO_SURFACE) { eglDestroySurface (clutter_egl_get_egl_display (), stage_egl->egl_surface); stage_egl->egl_surface = EGL_NO_SURFACE; } + _clutter_stage_x11_destroy_window_untrapped (stage_x11); + XSync (backend_x11->xdpy, False); clutter_x11_untrap_x_errors (); @@ -72,73 +66,19 @@ clutter_stage_egl_realize (ClutterStageWindow *stage_window) ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window); ClutterBackend *backend; ClutterBackendEGL *backend_egl; - ClutterBackendX11 *backend_x11; EGLDisplay edpy; - CLUTTER_NOTE (BACKEND, "Realizing main stage"); + CLUTTER_NOTE (BACKEND, "Realizing stage '%s' [%p]", + G_OBJECT_TYPE_NAME (stage_egl), + stage_egl); backend = clutter_get_default_backend (); backend_egl = CLUTTER_BACKEND_EGL (backend); - backend_x11 = CLUTTER_BACKEND_X11 (backend); edpy = clutter_egl_get_egl_display (); - if (stage_x11->xwin == None) - { - XSetWindowAttributes xattr; - unsigned long mask; - XVisualInfo *xvisinfo; - gfloat width, height; - - CLUTTER_NOTE (MISC, "Creating stage X window"); - - xvisinfo = clutter_backend_x11_get_visual_info (backend_x11); - if (xvisinfo == NULL) - { - g_critical ("Unable to find suitable GL visual."); - return FALSE; - } - - /* window attributes */ - xattr.background_pixel = WhitePixel (backend_x11->xdpy, - backend_x11->xscreen_num); - xattr.border_pixel = 0; - xattr.colormap = XCreateColormap (backend_x11->xdpy, - backend_x11->xwin_root, - xvisinfo->visual, - AllocNone); - mask = CWBorderPixel | CWColormap; - - /* Call get_size - this will either get the geometry size (which - * before we create the window is set to 640x480), or if a size - * is set, it will get that. This lets you set a size on the - * stage before it's realized. - */ - clutter_actor_get_size (CLUTTER_ACTOR (stage_x11->wrapper), - &width, - &height); - stage_x11->xwin_width = (gint)width; - stage_x11->xwin_height = (gint)height; - - stage_x11->xwin = XCreateWindow (backend_x11->xdpy, - backend_x11->xwin_root, - 0, 0, - stage_x11->xwin_width, - stage_x11->xwin_height, - 0, - xvisinfo->depth, - InputOutput, - xvisinfo->visual, - mask, &xattr); - - CLUTTER_NOTE (BACKEND, "Stage [%p], window: 0x%x, size: %dx%d", - stage_window, - (unsigned int) stage_x11->xwin, - stage_x11->xwin_width, - stage_x11->xwin_height); - - XFree (xvisinfo); - } + if (!_clutter_stage_x11_create_window (stage_x11)) + return FALSE; if (stage_egl->egl_surface == EGL_NO_SURFACE) { @@ -152,38 +92,6 @@ clutter_stage_egl_realize (ClutterStageWindow *stage_window) if (stage_egl->egl_surface == EGL_NO_SURFACE) g_warning ("Unable to create an EGL surface"); - if (clutter_x11_has_event_retrieval ()) - { - if (clutter_x11_has_xinput ()) - { - XSelectInput (backend_x11->xdpy, stage_x11->xwin, - StructureNotifyMask | - FocusChangeMask | - ExposureMask | - EnterWindowMask | LeaveWindowMask | - PropertyChangeMask); -#ifdef USE_XINPUT - _clutter_x11_select_events (stage_x11->xwin); -#endif - } - else - XSelectInput (backend_x11->xdpy, stage_x11->xwin, - StructureNotifyMask | - FocusChangeMask | - ExposureMask | - PointerMotionMask | - KeyPressMask | KeyReleaseMask | - ButtonPressMask | ButtonReleaseMask | - EnterWindowMask | LeaveWindowMask | - PropertyChangeMask); - } - - /* no user resize... */ - clutter_stage_x11_fix_window_size (stage_x11, - stage_x11->xwin_width, - stage_x11->xwin_height); - clutter_stage_x11_set_wm_protocols (stage_x11); - return clutter_stage_egl_parent_iface->realize (stage_window); } From b662a070f2c5ebe2af479fd0290745ad923b3c42 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 18 Jan 2011 15:42:02 +0000 Subject: [PATCH 18/41] docs: Update dependencies in README --- README | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README b/README index 3bedaebe4..80d569f0a 100644 --- a/README +++ b/README @@ -27,7 +27,7 @@ On X11, Clutter depends on the following extensions • XDamage • XExt • XFixes - • XInput 1.x (if --enable-xinput is passed to configure) + • XInput 1.x or 2.x • XKB When running the OpenGL flavor, Clutter requires at least version 1.3 From 431200f40dad93bcaeff76ad34f3fdd431d5a8f2 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 18 Jan 2011 16:13:26 +0000 Subject: [PATCH 19/41] device: Add keys and axes accessors Allow retrieving the number of keys and axes, since we provide the API to iterate over them both. --- clutter/clutter-device-manager-private.h | 2 -- clutter/clutter-input-device.c | 30 ++++++++++++++++----- clutter/clutter-input-device.h | 3 +++ clutter/x11/clutter-device-manager-xi2.c | 2 +- clutter/x11/clutter-input-device-core-x11.c | 3 +-- doc/reference/clutter/clutter-sections.txt | 12 ++++++--- 6 files changed, 38 insertions(+), 14 deletions(-) diff --git a/clutter/clutter-device-manager-private.h b/clutter/clutter-device-manager-private.h index ec5a2fc7f..67ec1a314 100644 --- a/clutter/clutter-device-manager-private.h +++ b/clutter/clutter-device-manager-private.h @@ -148,8 +148,6 @@ guint _clutter_input_device_add_axis (ClutterInputDev gdouble min_value, gdouble max_value, gdouble resolution); -ClutterInputAxis _clutter_input_device_get_axis (ClutterInputDevice *device, - guint index_); void _clutter_input_device_reset_axes (ClutterInputDevice *device); void _clutter_input_device_set_associated_device (ClutterInputDevice *device, diff --git a/clutter/clutter-input-device.c b/clutter/clutter-input-device.c index 013ce437b..824895642 100644 --- a/clutter/clutter-input-device.c +++ b/clutter/clutter-input-device.c @@ -907,9 +907,20 @@ _clutter_input_device_translate_axis (ClutterInputDevice *device, return TRUE; } +/** + * clutter_input_device_get_axis: + * @device: a #ClutterInputDevice + * @index_: the index of the axis + * + * Retrieves the type of axis on @device at the given index. + * + * Return value: the axis type + * + * Since: 1.6 + */ ClutterInputAxis -_clutter_input_device_get_axis (ClutterInputDevice *device, - guint index_) +clutter_input_device_get_axis (ClutterInputDevice *device, + guint index_) { ClutterAxisInfo *info; @@ -963,15 +974,22 @@ _clutter_input_device_set_keys (ClutterInputDevice *device, device->max_keycode = max_keycode; } +/** + * clutter_input_device_get_n_keys: + * @device: a #ClutterInputDevice + * + * Retrieves the number of keys registered for @device. + * + * Return value: the number of registered keys + * + * Since: 1.6 + */ guint clutter_input_device_get_n_keys (ClutterInputDevice *device) { g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), 0); - if (device->keys != NULL) - return device->keys->len; - - return 0; + return device->n_keys; } /** diff --git a/clutter/clutter-input-device.h b/clutter/clutter-input-device.h index 86621b51f..fb305263b 100644 --- a/clutter/clutter-input-device.h +++ b/clutter/clutter-input-device.h @@ -138,7 +138,10 @@ ClutterStage * clutter_input_device_get_pointer_stage (ClutterInputDev G_CONST_RETURN gchar * clutter_input_device_get_device_name (ClutterInputDevice *device); guint clutter_input_device_get_n_axes (ClutterInputDevice *device); +ClutterInputAxis clutter_input_device_get_axis (ClutterInputDevice *device, + guint index_); +guint clutter_input_device_get_n_keys (ClutterInputDevice *device); void clutter_input_device_set_key (ClutterInputDevice *device, guint index_, guint keyval, diff --git a/clutter/x11/clutter-device-manager-xi2.c b/clutter/x11/clutter-device-manager-xi2.c index 7641e37b6..afcfa8da6 100644 --- a/clutter/x11/clutter-device-manager-xi2.c +++ b/clutter/x11/clutter-device-manager-xi2.c @@ -500,7 +500,7 @@ translate_axes (ClutterInputDevice *device, if (!XIMaskIsSet (valuators->mask, i)) continue; - axis = _clutter_input_device_get_axis (device, i); + axis = clutter_input_device_get_axis (device, i); val = *values++; switch (axis) diff --git a/clutter/x11/clutter-input-device-core-x11.c b/clutter/x11/clutter-input-device-core-x11.c index fa8be52a5..d23cada39 100644 --- a/clutter/x11/clutter-input-device-core-x11.c +++ b/clutter/x11/clutter-input-device-core-x11.c @@ -258,8 +258,7 @@ translate_axes (ClutterInputDeviceX11 *device_x11, { ClutterInputAxis axis; - axis = _clutter_input_device_get_axis (device, i); - + axis = clutter_input_device_get_axis (device, i); switch (axis) { case CLUTTER_INPUT_AXIS_X: diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index 3dbadb375..a8da544a7 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -1082,12 +1082,18 @@ clutter_input_device_get_device_name clutter_input_device_get_device_coords clutter_input_device_get_pointer_actor clutter_input_device_get_pointer_stage -clutter_input_device_set_key -clutter_input_device_get_key -clutter_input_device_get_n_axes clutter_input_device_get_associated_device clutter_input_device_get_slave_devices + +clutter_input_device_get_n_keys +clutter_input_device_set_key +clutter_input_device_get_key + + +clutter_input_device_get_n_axes +clutter_input_device_get_axis + clutter_input_device_update_from_event From d805237c313f5e2ce85b93fd167569c25b702163 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 18 Jan 2011 16:54:12 +0000 Subject: [PATCH 20/41] event: Add accessor for the axes field CLUTTER_BUTTON_* and CLUTTER_MOTION event types have axes data attached to them, so we want to expose a common ClutterEvent method for extracting that data. --- clutter/clutter-event.c | 57 +++++++++++++++++++++++++++++++++++++++++ clutter/clutter-event.h | 2 ++ 2 files changed, 59 insertions(+) diff --git a/clutter/clutter-event.c b/clutter/clutter-event.c index 40629d6ed..9f4588dd7 100644 --- a/clutter/clutter-event.c +++ b/clutter/clutter-event.c @@ -989,3 +989,60 @@ _clutter_event_set_source_device (ClutterEvent *event, real_event = (ClutterEventPrivate *) event; real_event->source_device = device; } + +/** + * clutter_event_get_axes: + * @event: a #ClutterEvent + * @n_axes: (out): return location for the number of axes returned + * + * Retrieves the array of axes values attached to the event. + * + * Return value: (transfer none): an array of axis values + * + * Since: 1.6 + */ +gdouble * +clutter_event_get_axes (const ClutterEvent *event, + guint *n_axes) +{ + gdouble *retval = NULL; + guint len = 0; + + switch (event->type) + { + case CLUTTER_NOTHING: + case CLUTTER_STAGE_STATE: + case CLUTTER_DESTROY_NOTIFY: + case CLUTTER_CLIENT_MESSAGE: + case CLUTTER_DELETE: + case CLUTTER_ENTER: + case CLUTTER_LEAVE: + case CLUTTER_SCROLL: + case CLUTTER_KEY_PRESS: + case CLUTTER_KEY_RELEASE: + break; + + case CLUTTER_BUTTON_PRESS: + case CLUTTER_BUTTON_RELEASE: + retval = event->button.axes; + break; + + case CLUTTER_MOTION: + retval = event->motion.axes; + break; + } + + if (retval != NULL) + { + ClutterInputDevice *device; + + device = clutter_event_get_device (event); + if (device != NULL) + len = clutter_input_device_get_n_axes (device); + } + + if (n_axes) + *n_axes = len; + + return retval; +} diff --git a/clutter/clutter-event.h b/clutter/clutter-event.h index e7d20eb5e..f24bffd46 100644 --- a/clutter/clutter-event.h +++ b/clutter/clutter-event.h @@ -410,6 +410,8 @@ ClutterStage * clutter_event_get_stage (const ClutterEvent void clutter_event_get_coords (const ClutterEvent *event, gfloat *x, gfloat *y); +gdouble * clutter_event_get_axes (const ClutterEvent *event, + guint *n_axes); guint clutter_event_get_key_symbol (const ClutterEvent *event); guint16 clutter_event_get_key_code (const ClutterEvent *event); From 333ca35ceca739388be36cf48514fcb68f3b7aef Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 18 Jan 2011 16:55:14 +0000 Subject: [PATCH 21/41] device: Add get_axis_value() We need a convenience function for extracting the value of a specific axis type out of the array of axis values attached to events. --- clutter/clutter-input-device.c | 47 ++++++++++++++++++++++++++++++++++ clutter/clutter-input-device.h | 4 +++ 2 files changed, 51 insertions(+) diff --git a/clutter/clutter-input-device.c b/clutter/clutter-input-device.c index 824895642..d9e0714c1 100644 --- a/clutter/clutter-input-device.c +++ b/clutter/clutter-input-device.c @@ -924,6 +924,9 @@ clutter_input_device_get_axis (ClutterInputDevice *device, { ClutterAxisInfo *info; + g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), + CLUTTER_INPUT_AXIS_IGNORE); + if (device->axes == NULL) return CLUTTER_INPUT_AXIS_IGNORE; @@ -935,6 +938,50 @@ clutter_input_device_get_axis (ClutterInputDevice *device, return info->axis; } +/** + * clutter_input_device_get_axis_value: + * @device: a #ClutterInputDevice + * @axes: (array): an array of axes values, typically + * coming from clutter_event_get_axes() + * @axis: the axis to extract + * @value: (out): return location for the axis value + * + * Extracts the value of the given @axis of a #ClutterInputDevice from + * an array of axis values. + * + * Return value: %TRUE if the value was set, and %FALSE otherwise + * + * Since: 1.6 + */ +gboolean +clutter_input_device_get_axis_value (ClutterInputDevice *device, + gdouble *axes, + ClutterInputAxis axis, + gdouble *value) +{ + gint i; + + g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), FALSE); + g_return_val_if_fail (device->axes != NULL, FALSE); + + for (i = 0; i < device->axes->len; i++) + { + ClutterAxisInfo *info; + + info = &g_array_index (device->axes, ClutterAxisInfo, i); + + if (info->axis == axis) + { + if (value) + *value = axes[i]; + + return TRUE; + } + } + + return FALSE; +} + /** * clutter_input_device_get_n_axes: * @device: a #ClutterInputDevice diff --git a/clutter/clutter-input-device.h b/clutter/clutter-input-device.h index fb305263b..7e1fbb6d8 100644 --- a/clutter/clutter-input-device.h +++ b/clutter/clutter-input-device.h @@ -140,6 +140,10 @@ G_CONST_RETURN gchar * clutter_input_device_get_device_name (ClutterInputDev guint clutter_input_device_get_n_axes (ClutterInputDevice *device); ClutterInputAxis clutter_input_device_get_axis (ClutterInputDevice *device, guint index_); +gboolean clutter_input_device_get_axis_value (ClutterInputDevice *device, + gdouble *axes, + ClutterInputAxis axis, + gdouble *value); guint clutter_input_device_get_n_keys (ClutterInputDevice *device); void clutter_input_device_set_key (ClutterInputDevice *device, From ad06f1b20fcac9167f456ba21674d8f7a2c0cd3b Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 18 Jan 2011 16:57:14 +0000 Subject: [PATCH 22/41] docs: Update API reference --- doc/reference/clutter/clutter-sections.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index a8da544a7..f96653a35 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -1029,6 +1029,7 @@ clutter_event_get_time clutter_event_get_source clutter_event_get_stage clutter_event_get_flags +clutter_event_get_axes clutter_event_get @@ -1093,6 +1094,7 @@ clutter_input_device_get_key clutter_input_device_get_n_axes clutter_input_device_get_axis +clutter_input_device_get_axis_value clutter_input_device_update_from_event From dd5f6ca7e1b5042beca6363109424565a7d8223b Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 18 Jan 2011 17:33:22 +0000 Subject: [PATCH 23/41] device: Add more accessors for properties Allow using real accessor methods for getting the ClutterInputDevice properties set on construction. --- clutter/clutter-input-device.c | 38 ++++++++++++++++++++++ clutter/clutter-input-device.h | 2 ++ doc/reference/clutter/clutter-sections.txt | 2 ++ 3 files changed, 42 insertions(+) diff --git a/clutter/clutter-input-device.c b/clutter/clutter-input-device.c index d9e0714c1..406102f79 100644 --- a/clutter/clutter-input-device.c +++ b/clutter/clutter-input-device.c @@ -732,6 +732,44 @@ clutter_input_device_get_device_name (ClutterInputDevice *device) return device->device_name; } +/** + * clutter_input_device_get_has_cursor: + * @device: a #ClutterInputDevice + * + * Retrieves whether @device has a pointer that follows the + * device motion. + * + * Return value: %TRUE if the device has a cursor + * + * Since: 1.6 + */ +gboolean +clutter_input_device_get_has_cursor (ClutterInputDevice *device) +{ + g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), FALSE); + + return device->has_cursor; +} + +/** + * clutter_input_device_get_device_mode: + * @device: a #ClutterInputDevice + * + * Retrieves the #ClutterInputMode of @device. + * + * Return value: the device mode + * + * Since: 1.6 + */ +ClutterInputMode +clutter_input_device_get_device_mode (ClutterInputDevice *device) +{ + g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), + CLUTTER_INPUT_MODE_FLOATING); + + return device->device_mode; +} + /** * clutter_input_device_update_from_event: * @device: a #ClutterInputDevice diff --git a/clutter/clutter-input-device.h b/clutter/clutter-input-device.h index 7e1fbb6d8..5bd20c42f 100644 --- a/clutter/clutter-input-device.h +++ b/clutter/clutter-input-device.h @@ -136,6 +136,8 @@ void clutter_input_device_get_device_coords (ClutterInputDev ClutterActor * clutter_input_device_get_pointer_actor (ClutterInputDevice *device); ClutterStage * clutter_input_device_get_pointer_stage (ClutterInputDevice *device); G_CONST_RETURN gchar * clutter_input_device_get_device_name (ClutterInputDevice *device); +ClutterInputMode clutter_input_device_get_device_mode (ClutterInputDevice *device); +gboolean clutter_input_device_get_has_cursor (ClutterInputDevice *device); guint clutter_input_device_get_n_axes (ClutterInputDevice *device); ClutterInputAxis clutter_input_device_get_axis (ClutterInputDevice *device, diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index f96653a35..bb2c5577b 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -1080,6 +1080,8 @@ ClutterInputDevice clutter_input_device_get_device_id clutter_input_device_get_device_type clutter_input_device_get_device_name +clutter_input_device_get_device_mode +clutter_input_device_get_has_cursor clutter_input_device_get_device_coords clutter_input_device_get_pointer_actor clutter_input_device_get_pointer_stage From 405e611279321ebe293e02b4507899355f49cc64 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 18 Jan 2011 22:30:21 +0000 Subject: [PATCH 24/41] device/xi2: Translate the axis data after setting devices We need the devices (source and virtual) to be set before translating the axis data from XI2 to the Clutter event. --- clutter/x11/clutter-device-manager-xi2.c | 30 +++++++++++++++--------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/clutter/x11/clutter-device-manager-xi2.c b/clutter/x11/clutter-device-manager-xi2.c index afcfa8da6..5aa723cf9 100644 --- a/clutter/x11/clutter-device-manager-xi2.c +++ b/clutter/x11/clutter-device-manager-xi2.c @@ -700,12 +700,6 @@ clutter_device_manager_xi2_translate_event (ClutterEventTranslator *translator, event->scroll.modifier_state = _clutter_input_device_xi2_translate_state (&xev->mods, &xev->buttons); - event->scroll.axes = translate_axes (event->scroll.device, - event->scroll.x, - event->scroll.y, - stage_x11, - &xev->valuators); - device = g_hash_table_lookup (manager_xi2->devices_by_id, GINT_TO_POINTER (xev->sourceid)); _clutter_event_set_source_device (event, device); @@ -713,6 +707,13 @@ clutter_device_manager_xi2_translate_event (ClutterEventTranslator *translator, device = g_hash_table_lookup (manager_xi2->devices_by_id, GINT_TO_POINTER (xev->deviceid)); _clutter_event_set_device (event, device); + + event->scroll.axes = translate_axes (event->scroll.device, + event->scroll.x, + event->scroll.y, + stage_x11, + &xev->valuators); + break; default: @@ -729,11 +730,6 @@ clutter_device_manager_xi2_translate_event (ClutterEventTranslator *translator, event->button.modifier_state = _clutter_input_device_xi2_translate_state (&xev->mods, &xev->buttons); - event->button.axes = translate_axes (event->button.device, - event->button.x, - event->button.y, - stage_x11, - &xev->valuators); device = g_hash_table_lookup (manager_xi2->devices_by_id, GINT_TO_POINTER (xev->sourceid)); @@ -742,6 +738,12 @@ clutter_device_manager_xi2_translate_event (ClutterEventTranslator *translator, device = g_hash_table_lookup (manager_xi2->devices_by_id, GINT_TO_POINTER (xev->deviceid)); _clutter_event_set_device (event, device); + + event->button.axes = translate_axes (event->button.device, + event->button.x, + event->button.y, + stage_x11, + &xev->valuators); break; } @@ -786,6 +788,12 @@ clutter_device_manager_xi2_translate_event (ClutterEventTranslator *translator, GINT_TO_POINTER (xev->deviceid)); _clutter_event_set_device (event, device); + event->motion.axes = translate_axes (event->motion.device, + event->motion.x, + event->motion.y, + stage_x11, + &xev->valuators); + CLUTTER_NOTE (EVENT, "motion: win:0x%x device:%s (x:%.2f, y:%.2f)", (unsigned int) stage_x11->xwin, event->motion.device->device_name, From d078fe09301db384b7a465b86ad83dab860576b4 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 18 Jan 2011 22:31:14 +0000 Subject: [PATCH 25/41] event: Fix up clutter_event_copy() We need to copy the scroll axis data, and we need to copy the private Event data from the source event to the target event structure. --- clutter/clutter-event.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/clutter/clutter-event.c b/clutter/clutter-event.c index 9f4588dd7..a5908a473 100644 --- a/clutter/clutter-event.c +++ b/clutter/clutter-event.c @@ -676,12 +676,22 @@ ClutterEvent * clutter_event_copy (const ClutterEvent *event) { ClutterEvent *new_event; + ClutterEventPrivate *new_real_event; g_return_val_if_fail (event != NULL, NULL); new_event = clutter_event_new (CLUTTER_NOTHING); + new_real_event = (ClutterEventPrivate *) new_event; + *new_event = *event; + if (is_event_allocated (event)) + { + ClutterEventPrivate *real_event = (ClutterEventPrivate *) event; + + new_real_event->source_device = real_event->source_device; + } + switch (event->type) { case CLUTTER_BUTTON_PRESS: @@ -696,6 +706,17 @@ clutter_event_copy (const ClutterEvent *event) } break; + case CLUTTER_SCROLL: + if (event->scroll.device != NULL && event->scroll.axes != NULL) + { + gint n_axes; + + n_axes = clutter_input_device_get_n_axes (event->scroll.device); + new_event->scroll.axes = g_memdup (event->scroll.axes, + sizeof (gdouble) * n_axes); + } + break; + case CLUTTER_MOTION: if (event->motion.device != NULL && event->motion.axes != NULL) { @@ -743,6 +764,10 @@ clutter_event_free (ClutterEvent *event) g_free (event->motion.axes); break; + case CLUTTER_SCROLL: + g_free (event->scroll.axes); + break; + default: break; } From f54044f7e45fe37538187ca06f7d378ae8d64800 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 19 Jan 2011 13:52:33 +0000 Subject: [PATCH 26/41] test-devices: Clean up and show axes data --- tests/interactive/test-devices.c | 106 +++++++++++++++++++++++++++---- 1 file changed, 92 insertions(+), 14 deletions(-) diff --git a/tests/interactive/test-devices.c b/tests/interactive/test-devices.c index 8304d6d8c..b311f8986 100644 --- a/tests/interactive/test-devices.c +++ b/tests/interactive/test-devices.c @@ -26,6 +26,47 @@ device_type_name (ClutterInputDevice *device) case CLUTTER_EXTENSION_DEVICE: return "Extension"; + case CLUTTER_PEN_DEVICE: + return "Pen"; + + case CLUTTER_ERASER_DEVICE: + return "Eraser"; + + case CLUTTER_CURSOR_DEVICE: + return "Cursor"; + + default: + return "Unknown"; + } + + g_warn_if_reached (); + + return NULL; +} + +static const gchar * +axis_type_name (ClutterInputAxis axis) +{ + switch (axis) + { + case CLUTTER_INPUT_AXIS_X: + return "Absolute X"; + + case CLUTTER_INPUT_AXIS_Y: + return "Absolute Y"; + + case CLUTTER_INPUT_AXIS_PRESSURE: + return "Pressure"; + + case CLUTTER_INPUT_AXIS_XTILT: + return "X Tilt"; + + case CLUTTER_INPUT_AXIS_YTILT: + return "Y Tilt"; + + case CLUTTER_INPUT_AXIS_WHEEL: + return "Wheel"; + default: return "Unknown"; } @@ -36,22 +77,27 @@ device_type_name (ClutterInputDevice *device) } static gboolean -stage_motion_event_cb (ClutterActor *actor, - ClutterEvent *event, +stage_button_event_cb (ClutterActor *actor, + ClutterEvent *event, gpointer userdata) { TestDevicesApp *app = (TestDevicesApp *)userdata; ClutterInputDevice *device; + ClutterInputDevice *source_device; ClutterActor *hand = NULL; device = clutter_event_get_device (event); + source_device = clutter_event_get_source_device (event); hand = g_hash_table_lookup (app->devices, device); - g_print ("Device: '%s' (id:%d, type:%s)\n", + g_print ("Device: '%s' (id:%d, type: %s, source: '%s')\n", clutter_input_device_get_device_name (device), clutter_input_device_get_device_id (device), - device_type_name (device)); + device_type_name (device), + source_device != device + ? clutter_input_device_get_device_name (source_device) + : ""); if (hand != NULL) { @@ -65,26 +111,52 @@ stage_motion_event_cb (ClutterActor *actor, if (event->motion.axes != NULL) { - guint n_axes = clutter_input_device_get_n_axes (event->motion.device); - guint i; + gdouble *axes; + guint n_axes, i; + axes = clutter_event_get_axes (event, &n_axes); for (i = 0; i < n_axes; i++) { - g_print ("Axis[%02d].value: %.2f\n", + g_print ("Axis[%02d][%s].value: %.2f\n", i, - event->motion.axes[i]); + axis_type_name (clutter_input_device_get_axis (device, i)), + axes[i]); } } return FALSE; } +static gboolean +stage_motion_event_cb (ClutterActor *actor, + ClutterEvent *event, + gpointer userdata) +{ + TestDevicesApp *app = (TestDevicesApp *)userdata; + ClutterInputDevice *device; + ClutterActor *hand = NULL; + + device = clutter_event_get_device (event); + + hand = g_hash_table_lookup (app->devices, device); + if (hand != NULL) + { + gfloat event_x, event_y; + + clutter_event_get_coords (event, &event_x, &event_y); + clutter_actor_set_position (hand, event_x, event_y); + + return TRUE; + } + + return FALSE; +} + G_MODULE_EXPORT int test_devices_main (int argc, char **argv) { ClutterActor *stage; TestDevicesApp *app; - ClutterColor stage_color = { 0x61, 0x64, 0x8c, 0xff }; ClutterDeviceManager *manager; const GSList *stage_devices, *l; @@ -96,12 +168,18 @@ test_devices_main (int argc, char **argv) app = g_new0 (TestDevicesApp, 1); app->devices = g_hash_table_new (g_direct_hash, g_direct_equal) ; - stage = clutter_stage_get_default (); - clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color); - //clutter_stage_fullscreen (CLUTTER_STAGE (stage)); - + stage = clutter_stage_new (); + clutter_stage_set_color (CLUTTER_STAGE (stage), CLUTTER_COLOR_LightSkyBlue); + clutter_stage_set_title (CLUTTER_STAGE (stage), "Devices"); + clutter_stage_hide_cursor (CLUTTER_STAGE (stage)); + g_signal_connect (stage, + "destroy", G_CALLBACK (clutter_main_quit), + NULL); g_signal_connect (stage, - "motion-event", G_CALLBACK(stage_motion_event_cb), + "motion-event", G_CALLBACK (stage_motion_event_cb), + app); + g_signal_connect (stage, + "button-press-event", G_CALLBACK (stage_button_event_cb), app); clutter_actor_show_all (stage); From 50e52f550a14e86a689673ce898970337cc46e8b Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 19 Jan 2011 13:53:20 +0000 Subject: [PATCH 27/41] device: Use a double for translate_axis() argument While XI1 has axis data in events exposed as integers, XI2 uses double precision floating point values. --- clutter/clutter-device-manager-private.h | 2 +- clutter/clutter-input-device.c | 17 ++++++++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/clutter/clutter-device-manager-private.h b/clutter/clutter-device-manager-private.h index 67ec1a314..a316f2c68 100644 --- a/clutter/clutter-device-manager-private.h +++ b/clutter/clutter-device-manager-private.h @@ -163,7 +163,7 @@ void _clutter_input_device_select_stage_events (ClutterInputDev gboolean _clutter_input_device_translate_axis (ClutterInputDevice *device, guint index_, - gint value, + gdouble value, gdouble *axis_value); G_END_DECLS diff --git a/clutter/clutter-input-device.c b/clutter/clutter-input-device.c index 406102f79..c71bf060e 100644 --- a/clutter/clutter-input-device.c +++ b/clutter/clutter-input-device.c @@ -915,10 +915,25 @@ _clutter_input_device_add_axis (ClutterInputDevice *device, return pos; } +/*< private > + * clutter_input_translate_axis: + * @device: a #ClutterInputDevice + * @index_: the index of the axis + * @gint: the absolute value of the axis + * @axis_value: (out): the translated value of the axis + * + * Performs a conversion from the absolute value of the axis + * to a relative value. + * + * The axis at @index_ must not be %CLUTTER_INPUT_AXIS_X or + * %CLUTTER_INPUT_AXIS_Y. + * + * Return value: %TRUE if the conversion was successful + */ gboolean _clutter_input_device_translate_axis (ClutterInputDevice *device, guint index_, - gint value, + gdouble value, gdouble *axis_value) { ClutterAxisInfo *info; From d3ed3d3a7b3950160a3351d54e766628fd916ad8 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 19 Jan 2011 13:54:27 +0000 Subject: [PATCH 28/41] device: Add more doc annotations Even for internal API. --- clutter/clutter-input-device.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/clutter/clutter-input-device.c b/clutter/clutter-input-device.c index c71bf060e..261d24c7c 100644 --- a/clutter/clutter-input-device.c +++ b/clutter/clutter-input-device.c @@ -1002,6 +1002,18 @@ clutter_input_device_get_axis (ClutterInputDevice *device, * Extracts the value of the given @axis of a #ClutterInputDevice from * an array of axis values. * + * An example of typical usage for this function is: + * + * |[ + * ClutterInputDevice *device = clutter_event_get_device (event); + * gdouble *axes = clutter_event_get_axes (event, NULL); + * gdouble pressure_value = 0; + * + * clutter_input_device_get_axis_value (device, axes, + * CLUTTER_INPUT_AXIS_PRESSURE, + * &pressure_value); + * ]| + * * Return value: %TRUE if the value was set, and %FALSE otherwise * * Since: 1.6 @@ -1167,6 +1179,16 @@ clutter_input_device_get_key (ClutterInputDevice *device, return TRUE; } +/*< private > + * clutter_input_device_add_slave: + * @master: a #ClutterInputDevice + * @slave: a #ClutterInputDevice + * + * Adds @slave to the list of slave devices of @master + * + * This function does not increase the reference count of either @master + * or @slave. + */ void _clutter_input_device_add_slave (ClutterInputDevice *master, ClutterInputDevice *slave) @@ -1175,6 +1197,16 @@ _clutter_input_device_add_slave (ClutterInputDevice *master, master->slaves = g_list_prepend (master->slaves, slave); } +/*< private > + * clutter_input_device_remove_slave: + * @master: a #ClutterInputDevice + * @slave: a #ClutterInputDevice + * + * Removes @slave from the list of slave devices of @master. + * + * This function does not decrease the reference count of either @master + * or @slave. + */ void _clutter_input_device_remove_slave (ClutterInputDevice *master, ClutterInputDevice *slave) From 137f7d42a8db40a85ae95b10d9ed0fa895f48b0a Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 19 Jan 2011 15:17:34 +0000 Subject: [PATCH 29/41] backend/eglx: Add newly created stages to the translators The events directed to the stage should be translated by the stage. --- clutter/egl/clutter-backend-egl.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/clutter/egl/clutter-backend-egl.c b/clutter/egl/clutter-backend-egl.c index 5f120fc9a..c70e331ef 100644 --- a/clutter/egl/clutter-backend-egl.c +++ b/clutter/egl/clutter-backend-egl.c @@ -750,6 +750,7 @@ clutter_backend_egl_create_stage (ClutterBackend *backend, { #ifdef COGL_HAS_XLIB_SUPPORT ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (backend); + ClutterEventTranslator *translator; ClutterStageWindow *stage; ClutterStageX11 *stage_x11; @@ -762,6 +763,9 @@ clutter_backend_egl_create_stage (ClutterBackend *backend, stage_x11 = CLUTTER_STAGE_X11 (stage); stage_x11->wrapper = wrapper; + translator = CLUTTER_EVENT_TRANSLATOR (stage_x11); + _clutter_backend_x11_add_event_translator (backend_x11, translator); + CLUTTER_NOTE (MISC, "EGLX stage created (display:%p, screen:%d, root:%u)", backend_x11->xdpy, backend_x11->xscreen_num, From 73cf6bd52c282acc1cdb67724d2b55d3f822f7ef Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 19 Jan 2011 16:23:45 +0000 Subject: [PATCH 30/41] device: Allow enabling/disabling non-master devices Slave and floating devices should always be disabled, and not deliver events to the scene. It is up to the user to enable non-master devices and handle events coming from them. ClutterInputDevice gets a new :enabled property, defaulting to FALSE; when a device manager creates a new device it has to set it to TRUE if the :device-mode property is set to CLUTTER_INPUT_MODE_MASTER. The main event queue entry point, _clutter_event_push(), will automatically discard events coming from disabled devices. --- clutter/clutter-device-manager-private.h | 3 +- clutter/clutter-event.c | 9 +++ clutter/clutter-input-device.c | 66 +++++++++++++++++++ clutter/clutter-input-device.h | 3 + clutter/x11/clutter-device-manager-core-x11.c | 3 + clutter/x11/clutter-device-manager-xi2.c | 10 ++- tests/interactive/test-devices.c | 10 ++- 7 files changed, 100 insertions(+), 4 deletions(-) diff --git a/clutter/clutter-device-manager-private.h b/clutter/clutter-device-manager-private.h index a316f2c68..210995170 100644 --- a/clutter/clutter-device-manager-private.h +++ b/clutter/clutter-device-manager-private.h @@ -103,7 +103,8 @@ struct _ClutterInputDevice gint min_keycode; gint max_keycode; - guint has_cursor : 1; + guint has_cursor : 1; + guint is_enabled : 1; }; struct _ClutterInputDeviceClass diff --git a/clutter/clutter-event.c b/clutter/clutter-event.c index a5908a473..47264a773 100644 --- a/clutter/clutter-event.c +++ b/clutter/clutter-event.c @@ -832,10 +832,19 @@ _clutter_event_push (const ClutterEvent *event, gboolean do_copy) { ClutterMainContext *context = _clutter_context_get_default (); + ClutterInputDevice *device; /* FIXME: check queue is valid */ g_assert (context != NULL); + /* disabled devices don't propagate events */ + device = clutter_event_get_device (event); + if (device != NULL) + { + if (!clutter_input_device_get_enabled (device)) + return; + } + if (do_copy) { ClutterEvent *copy; diff --git a/clutter/clutter-input-device.c b/clutter/clutter-input-device.c index 261d24c7c..12ae2e2de 100644 --- a/clutter/clutter-input-device.c +++ b/clutter/clutter-input-device.c @@ -59,6 +59,7 @@ enum PROP_DEVICE_MODE, PROP_HAS_CURSOR, + PROP_ENABLED, PROP_N_AXES, @@ -139,6 +140,10 @@ clutter_input_device_set_property (GObject *gobject, self->has_cursor = g_value_get_boolean (value); break; + case PROP_ENABLED: + self->is_enabled = g_value_get_boolean (value); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; @@ -190,6 +195,10 @@ clutter_input_device_get_property (GObject *gobject, g_value_set_uint (value, 0); break; + case PROP_ENABLED: + g_value_set_boolean (value, self->is_enabled); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; @@ -270,6 +279,13 @@ clutter_input_device_class_init (ClutterInputDeviceClass *klass) FALSE, CLUTTER_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + obj_props[PROP_ENABLED] = + g_param_spec_boolean ("enabled", + P_("Enabled"), + P_("Whether the device is enabled"), + FALSE, + CLUTTER_PARAM_READWRITE); + obj_props[PROP_N_AXES] = g_param_spec_uint ("n-axes", P_("Number of Axes"), @@ -587,6 +603,56 @@ clutter_input_device_get_device_id (ClutterInputDevice *device) return device->id; } +/** + * clutter_input_device_set_enabled: + * @device: a #ClutterInputDevice + * @enabled: %TRUE to enable the @device + * + * Enables or disables a #ClutterInputDevice. + * + * Only devices with a #ClutterInputDevice:device-mode property set + * to %CLUTTER_INPUT_MODE_SLAVE or %CLUTTER_INPUT_MODE_FLOATING can + * be disabled. + * + * Since: 1.6 + */ +void +clutter_input_device_set_enabled (ClutterInputDevice *device, + gboolean enabled) +{ + g_return_if_fail (CLUTTER_IS_INPUT_DEVICE (device)); + + enabled = !!enabled; + + if (!enabled && device->device_mode == CLUTTER_INPUT_MODE_MASTER) + return; + + if (device->is_enabled == enabled) + return; + + device->is_enabled = enabled; + + g_object_notify_by_pspec (G_OBJECT (device), obj_props[PROP_ENABLED]); +} + +/** + * clutter_input_device_get_enabled: + * @device: a #ClutterInputDevice + * + * Retrieves whether @device is enabled. + * + * Return value: %TRUE if the device is enabled + * + * Since: 1.6 + */ +gboolean +clutter_input_device_get_enabled (ClutterInputDevice *device) +{ + g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), FALSE); + + return device->is_enabled; +} + /** * clutter_input_device_get_device_coords: * @device: a #ClutterInputDevice of type %CLUTTER_POINTER_DEVICE diff --git a/clutter/clutter-input-device.h b/clutter/clutter-input-device.h index 5bd20c42f..6772a3914 100644 --- a/clutter/clutter-input-device.h +++ b/clutter/clutter-input-device.h @@ -138,6 +138,9 @@ ClutterStage * clutter_input_device_get_pointer_stage (ClutterInputDev G_CONST_RETURN gchar * clutter_input_device_get_device_name (ClutterInputDevice *device); ClutterInputMode clutter_input_device_get_device_mode (ClutterInputDevice *device); gboolean clutter_input_device_get_has_cursor (ClutterInputDevice *device); +void clutter_input_device_set_enabled (ClutterInputDevice *device, + gboolean enabled); +gboolean clutter_input_device_get_enabled (ClutterInputDevice *device); guint clutter_input_device_get_n_axes (ClutterInputDevice *device); ClutterInputAxis clutter_input_device_get_axis (ClutterInputDevice *device, diff --git a/clutter/x11/clutter-device-manager-core-x11.c b/clutter/x11/clutter-device-manager-core-x11.c index a13d89480..af3397366 100644 --- a/clutter/x11/clutter-device-manager-core-x11.c +++ b/clutter/x11/clutter-device-manager-core-x11.c @@ -188,6 +188,7 @@ create_device (ClutterDeviceManagerX11 *manager_x11, "device-type", source, "device-mode", CLUTTER_INPUT_MODE_FLOATING, "backend", backend_x11, + "enabled", FALSE, NULL); translate_class_info (retval, info); @@ -581,6 +582,7 @@ default_device: "device-manager", manager_x11, "device-mode", CLUTTER_INPUT_MODE_MASTER, "backend", backend_x11, + "enabled", TRUE, NULL); CLUTTER_NOTE (BACKEND, "Added core pointer device"); @@ -592,6 +594,7 @@ default_device: "device-manager", manager_x11, "device-mode", CLUTTER_INPUT_MODE_MASTER, "backend", backend_x11, + "enabled", TRUE, NULL); CLUTTER_NOTE (BACKEND, "Added core keyboard device"); diff --git a/clutter/x11/clutter-device-manager-xi2.c b/clutter/x11/clutter-device-manager-xi2.c index 5aa723cf9..e9fbbdb68 100644 --- a/clutter/x11/clutter-device-manager-xi2.c +++ b/clutter/x11/clutter-device-manager-xi2.c @@ -159,6 +159,7 @@ create_device (ClutterDeviceManagerXI2 *manager_xi2, ClutterInputDeviceType source; ClutterInputDevice *retval; ClutterInputMode mode; + gboolean is_enabled; if (info->use == XIMasterKeyboard || info->use == XISlaveKeyboard) source = CLUTTER_KEYBOARD_DEVICE; @@ -185,16 +186,19 @@ create_device (ClutterDeviceManagerXI2 *manager_xi2, case XIMasterKeyboard: case XIMasterPointer: mode = CLUTTER_INPUT_MODE_MASTER; + is_enabled = TRUE; break; case XISlaveKeyboard: case XISlavePointer: mode = CLUTTER_INPUT_MODE_SLAVE; + is_enabled = FALSE; break; case XIFloatingSlave: default: mode = CLUTTER_INPUT_MODE_FLOATING; + is_enabled = FALSE; break; } @@ -206,6 +210,7 @@ create_device (ClutterDeviceManagerXI2 *manager_xi2, "device-type", source, "device-mode", mode, "backend", backend_x11, + "enabled", is_enabled, NULL); translate_device_classes (backend_x11->xdpy, retval, @@ -655,11 +660,14 @@ clutter_device_manager_xi2_translate_event (ClutterEventTranslator *translator, event->key.unicode_value = (gunichar) '\0'; } - CLUTTER_NOTE (EVENT, "%s: win:0x%x, key: %12s (%d)", + CLUTTER_NOTE (EVENT, + "%s: win:0x%x device:%d source:%d, key: %12s (%d)", event->any.type == CLUTTER_KEY_PRESS ? "key press " : "key release", (unsigned int) stage_x11->xwin, + xev->deviceid, + xev->sourceid, event->key.keyval ? buffer : "(none)", event->key.keyval); diff --git a/tests/interactive/test-devices.c b/tests/interactive/test-devices.c index b311f8986..f38e3285e 100644 --- a/tests/interactive/test-devices.c +++ b/tests/interactive/test-devices.c @@ -196,15 +196,21 @@ test_devices_main (int argc, char **argv) ClutterInputDeviceType device_type; ClutterActor *hand = NULL; - g_print ("got a %s device '%s' with id %d...\n", + g_print ("got a %s device '%s' with id %d\n", device_type_name (device), clutter_input_device_get_device_name (device), clutter_input_device_get_device_id (device)); device_type = clutter_input_device_get_device_type (device); if (device_type == CLUTTER_POINTER_DEVICE || - device_type == CLUTTER_EXTENSION_DEVICE) + device_type == CLUTTER_PEN_DEVICE || + device_type == CLUTTER_POINTER_DEVICE) { + g_print ("*** enabling device '%s' ***\n", + clutter_input_device_get_device_name (device)); + + clutter_input_device_set_enabled (device, TRUE); + hand = clutter_texture_new_from_file (TESTS_DATADIR G_DIR_SEPARATOR_S "redhand.png", From 400ecdfc81d19ab27cbc7a4f416b6fc109556ebf Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 19 Jan 2011 16:34:37 +0000 Subject: [PATCH 31/41] win32: Update DeviceManager device creation --- clutter/win32/clutter-device-manager-win32.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/clutter/win32/clutter-device-manager-win32.c b/clutter/win32/clutter-device-manager-win32.c index 1e9308a62..3527c9cf3 100644 --- a/clutter/win32/clutter-device-manager-win32.c +++ b/clutter/win32/clutter-device-manager-win32.c @@ -48,12 +48,16 @@ static void clutter_device_manager_win32_constructed (GObject *gobject) { ClutterDeviceManager *manager = CLUTTER_DEVICE_MANAGER (gobject); + ClutterDeviceManagerWin32 *manager_win32; ClutterInputDevice *device; device = g_object_new (CLUTTER_TYPE_INPUT_DEVICE, "id", 0, "name", "Core Pointer", "device-type", CLUTTER_POINTER_DEVICE, + "device-mode", CLUTTER_INPUT_MODE_MASTER, + "has-cursor", TRUE, + "enabled", TRUE, NULL); CLUTTER_NOTE (BACKEND, "Added core pointer device"); _clutter_device_manager_add_device (manager, device); @@ -62,9 +66,18 @@ clutter_device_manager_win32_constructed (GObject *gobject) "id", 1, "name", "Core Keyboard", "device-type", CLUTTER_KEYBOARD_DEVICE, + "device-mode", CLUTTER_INPUT_MODE_MASTER, + "enabled", TRUE, NULL); CLUTTER_NOTE (BACKEND, "Added core keyboard device"); _clutter_device_manager_add_device (manager, device); + + manager_win32 = CLUTTER_DEVICE_MANAGER_WIN32 (manager); + + _clutter_input_device_set_associated_device (manager_win32->core_pointer, + manager_win32->core_keyboard); + _clutter_input_device_set_associated_device (manager_win32->core_keyboard, + manager_win32->core_pointer); } static void From 0f56abf569d524bd42d4a3cdc6a5321a3de2b7f9 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 19 Jan 2011 16:34:49 +0000 Subject: [PATCH 32/41] */event: Never manipulate the event queue directly Always use _clutter_event_push() instead. --- clutter/egl/clutter-event-tslib.c | 2 +- clutter/evdev/clutter-device-manager-evdev.c | 5 +--- .../wayland/clutter-input-device-wayland.c | 26 +++++-------------- clutter/win32/clutter-event-win32.c | 6 +---- 4 files changed, 10 insertions(+), 29 deletions(-) diff --git a/clutter/egl/clutter-event-tslib.c b/clutter/egl/clutter-event-tslib.c index ff1a06d05..f53318c68 100644 --- a/clutter/egl/clutter-event-tslib.c +++ b/clutter/egl/clutter-event-tslib.c @@ -274,7 +274,7 @@ clutter_event_dispatch (GSource *source, clicked = FALSE; } - g_queue_push_head (clutter_context->events_queue, event); + _clutter_event_push (event, FALSE); } /* Pop an event off the queue if any */ diff --git a/clutter/evdev/clutter-device-manager-evdev.c b/clutter/evdev/clutter-device-manager-evdev.c index 955718fb6..b87aa4fd6 100644 --- a/clutter/evdev/clutter-device-manager-evdev.c +++ b/clutter/evdev/clutter-device-manager-evdev.c @@ -139,13 +139,10 @@ clutter_event_check (GSource *source) static void queue_event (ClutterEvent *event) { - ClutterMainContext *context; - if (event == NULL) return; - context = _clutter_context_get_default (); - g_queue_push_head (context->events_queue, event); + _clutter_event_push (event, FALSE); } static void diff --git a/clutter/wayland/clutter-input-device-wayland.c b/clutter/wayland/clutter-input-device-wayland.c index 688aba902..6e52bbaf9 100644 --- a/clutter/wayland/clutter-input-device-wayland.c +++ b/clutter/wayland/clutter-input-device-wayland.c @@ -74,7 +74,6 @@ clutter_backend_wayland_handle_motion (void *data, { ClutterInputDeviceWayland *device = data; ClutterStageWayland *stage_wayland = device->pointer_focus; - ClutterMainContext *clutter_context; ClutterEvent *event; event = clutter_event_new (CLUTTER_MOTION); @@ -90,8 +89,7 @@ clutter_backend_wayland_handle_motion (void *data, device->x = x; device->y = y; - clutter_context = _clutter_context_get_default (); - g_queue_push_head (clutter_context->events_queue, event); + _clutter_event_push (event, FALSE); } static void @@ -102,7 +100,6 @@ clutter_backend_wayland_handle_button (void *data, { ClutterInputDeviceWayland *device = data; ClutterStageWayland *stage_wayland = device->pointer_focus; - ClutterMainContext *clutter_context; ClutterEvent *event; ClutterEventType type; @@ -132,8 +129,7 @@ clutter_backend_wayland_handle_button (void *data, break; } - clutter_context = _clutter_context_get_default (); - g_queue_push_head (clutter_context->events_queue, event); + _clutter_event_push (event, FALSE); } static void @@ -144,7 +140,6 @@ clutter_backend_wayland_handle_key (void *data, { ClutterInputDeviceWayland *device = data; ClutterStageWayland *stage_wayland = device->keyboard_focus; - ClutterMainContext *clutter_context; ClutterEvent *event; event = _clutter_key_event_new_from_evdev ((ClutterInputDevice *) device, @@ -153,8 +148,7 @@ clutter_backend_wayland_handle_key (void *data, _time, key, state, &device->modifier_state); - clutter_context = _clutter_context_get_default (); - g_queue_push_head (clutter_context->events_queue, event); + _clutter_event_push (event, FALSE); } static void @@ -166,7 +160,6 @@ clutter_backend_wayland_handle_pointer_focus (void *data, { ClutterInputDeviceWayland *device = data; ClutterStageWayland *stage_wayland; - ClutterMainContext *clutter_context; ClutterEvent *event; if (device->pointer_focus) @@ -181,8 +174,7 @@ clutter_backend_wayland_handle_pointer_focus (void *data, event->crossing.source = CLUTTER_ACTOR (stage_wayland->wrapper); event->crossing.device = CLUTTER_INPUT_DEVICE (device); - clutter_context = _clutter_context_get_default (); - g_queue_push_head (clutter_context->events_queue, event); + _clutter_event_push (event, FALSE); device->pointer_focus = NULL; _clutter_input_device_set_stage (CLUTTER_INPUT_DEVICE (device), NULL); @@ -204,8 +196,7 @@ clutter_backend_wayland_handle_pointer_focus (void *data, event->motion.source = CLUTTER_ACTOR (stage_wayland->wrapper); event->motion.device = CLUTTER_INPUT_DEVICE (device); - clutter_context = _clutter_context_get_default (); - g_queue_push_head (clutter_context->events_queue, event); + _clutter_event_push (event, FALSE); device->surface_x = sx; device->surface_y = sy; @@ -226,7 +217,6 @@ clutter_backend_wayland_handle_keyboard_focus (void *data, { ClutterInputDeviceWayland *device = data; ClutterStageWayland *stage_wayland; - ClutterMainContext *clutter_context; ClutterEvent *event; uint32_t *k, *end; @@ -242,8 +232,7 @@ clutter_backend_wayland_handle_keyboard_focus (void *data, event->stage_state.changed_mask = CLUTTER_STAGE_STATE_ACTIVATED; event->stage_state.new_state = 0; - clutter_context = _clutter_context_get_default (); - g_queue_push_head (clutter_context->events_queue, event); + _clutter_event_push (event, FALSE); } if (surface) @@ -261,8 +250,7 @@ clutter_backend_wayland_handle_keyboard_focus (void *data, for (k = keys->data; k < end; k++) device->modifier_state |= device->xkb->map->modmap[*k]; - clutter_context = _clutter_context_get_default (); - g_queue_push_head (clutter_context->events_queue, event); + _clutter_event_push (event, FALSE); } } diff --git a/clutter/win32/clutter-event-win32.c b/clutter/win32/clutter-event-win32.c index 128c62e6e..8429fdd11 100644 --- a/clutter/win32/clutter-event-win32.c +++ b/clutter/win32/clutter-event-win32.c @@ -207,14 +207,10 @@ get_modifier_state (WPARAM wparam) static void take_and_queue_event (ClutterEvent *event) { - ClutterMainContext *clutter_context; - - clutter_context = _clutter_context_get_default (); - /* The event is added directly to the queue instead of using clutter_event_put so that it can avoid a copy. This takes ownership of the event */ - g_queue_push_head (clutter_context->events_queue, event); + _clutter_event_push (event, FALSE); } static inline void From 89467abae591fb07afed507323f086264d73e68d Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 19 Jan 2011 17:03:27 +0000 Subject: [PATCH 33/41] docs: More documentation fixes for InputDevice --- clutter/clutter-input-device.c | 107 +++++++++++++++++---- doc/reference/clutter/clutter-sections.txt | 10 +- 2 files changed, 95 insertions(+), 22 deletions(-) diff --git a/clutter/clutter-input-device.c b/clutter/clutter-input-device.c index 12ae2e2de..1f5360f1c 100644 --- a/clutter/clutter-input-device.c +++ b/clutter/clutter-input-device.c @@ -141,7 +141,7 @@ clutter_input_device_set_property (GObject *gobject, break; case PROP_ENABLED: - self->is_enabled = g_value_get_boolean (value); + clutter_input_device_set_enabled (self, g_value_get_boolean (value)); break; default: @@ -189,10 +189,7 @@ clutter_input_device_get_property (GObject *gobject, break; case PROP_N_AXES: - if (self->axes != NULL) - g_value_set_uint (value, self->axes->len); - else - g_value_set_uint (value, 0); + g_value_set_uint (value, clutter_input_device_get_n_axes (self)); break; case PROP_ENABLED: @@ -257,6 +254,13 @@ clutter_input_device_class_init (ClutterInputDeviceClass *klass) CLUTTER_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + /** + * ClutterInputDevice:device-manager: + * + * The #ClutterDeviceManager instance which owns the device + * + * Since: 1.6 + */ obj_props[PROP_DEVICE_MANAGER] = g_param_spec_object ("device-manager", P_("Device Manager"), @@ -264,6 +268,13 @@ clutter_input_device_class_init (ClutterInputDeviceClass *klass) CLUTTER_TYPE_DEVICE_MANAGER, CLUTTER_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + /** + * ClutterInputDevice:mode: + * + * The mode of the device. + * + * Since: 1.6 + */ obj_props[PROP_DEVICE_MODE] = g_param_spec_enum ("device-mode", P_("Device Mode"), @@ -272,6 +283,13 @@ clutter_input_device_class_init (ClutterInputDeviceClass *klass) CLUTTER_INPUT_MODE_FLOATING, CLUTTER_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + /** + * ClutterInputDevice:has-cursor: + * + * Whether the device has an on screen cursor following its movement. + * + * Since: 1.6 + */ obj_props[PROP_HAS_CURSOR] = g_param_spec_boolean ("has-cursor", P_("Has Cursor"), @@ -279,6 +297,18 @@ clutter_input_device_class_init (ClutterInputDeviceClass *klass) FALSE, CLUTTER_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + /** + * ClutterInputDevice:enabled: + * + * Whether the device is enabled. + * + * A device with the #ClutterInputDevice:device-mode property set + * to %CLUTTER_INPUT_MODE_MASTER cannot be disabled. + * + * A device must be enabled in order to receive events from it. + * + * Since: 1.6 + */ obj_props[PROP_ENABLED] = g_param_spec_boolean ("enabled", P_("Enabled"), @@ -286,6 +316,13 @@ clutter_input_device_class_init (ClutterInputDeviceClass *klass) FALSE, CLUTTER_PARAM_READWRITE); + /** + * ClutterInputDevice:n-axes: + * + * The number of axes of the device. + * + * Since: 1.6 + */ obj_props[PROP_N_AXES] = g_param_spec_uint ("n-axes", P_("Number of Axes"), @@ -294,6 +331,13 @@ clutter_input_device_class_init (ClutterInputDeviceClass *klass) 0, CLUTTER_PARAM_READABLE); + /** + * ClutterInputDevice:backend: + * + * The #ClutterBackend that created the device. + * + * Since: 1.6 + */ obj_props[PROP_BACKEND] = g_param_spec_object ("backend", P_("Backend"), @@ -325,8 +369,8 @@ clutter_input_device_init (ClutterInputDevice *self) self->max_keycode = G_MAXUINT; } -/* - * _clutter_input_device_set_coords: +/*< private > + * clutter_input_device_set_coords: * @device: a #ClutterInputDevice * @x: X coordinate of the device * @y: Y coordinate of the device @@ -347,8 +391,8 @@ _clutter_input_device_set_coords (ClutterInputDevice *device, device->current_y = y; } -/* - * _clutter_input_device_set_state: +/*< private > + * clutter_input_device_set_state: * @device: a #ClutterInputDevice * @state: a bitmask of modifiers * @@ -363,8 +407,8 @@ _clutter_input_device_set_state (ClutterInputDevice *device, device->current_state = state; } -/* - * _clutter_input_device_set_time: +/*< private > + * clutter_input_device_set_time: * @device: a #ClutterInputDevice * @time_: the time * @@ -380,10 +424,7 @@ _clutter_input_device_set_time (ClutterInputDevice *device, device->current_time = time_; } -/* - * cursor_weak_unref: - * - * #ClutterInputDevice keeps a weak reference on the actor +/* #ClutterInputDevice keeps a weak reference on the actor * under its pointer; this function unsets the reference on * the actor to avoid keeping around stale pointers */ @@ -396,8 +437,8 @@ cursor_weak_unref (gpointer user_data, device->cursor_actor = NULL; } -/* - * _clutter_input_device_set_stage: +/*< private > + * clutter_input_device_set_stage: * @device: a #ClutterInputDevice * @stage: a #ClutterStage or %NULL * @@ -446,8 +487,8 @@ _clutter_input_device_set_stage (ClutterInputDevice *device, device->cursor_actor = NULL; } -/* - * _clutter_input_device_set_actor: +/*< private > + * clutter_input_device_set_actor: * @device: a #ClutterInputDevice * @actor: a #ClutterActor * @@ -925,6 +966,12 @@ clutter_input_device_update_from_event (ClutterInputDevice *device, _clutter_input_device_set_stage (device, event_stage); } +/*< private > + * clutter_input_device_reset_axes: + * @device: a #ClutterInputDevice + * + * Resets the axes on @device + */ void _clutter_input_device_reset_axes (ClutterInputDevice *device) { @@ -936,6 +983,16 @@ _clutter_input_device_reset_axes (ClutterInputDevice *device) } } +/*< private > + * clutter_input_device_add_axis: + * @device: a #ClutterInputDevice + * @axis: the axis type + * @minimum: the minimum axis value + * @maximum: the maximum axis value + * @resolution: the axis resolution + * + * Adds an axis of type @axis on @device. + */ guint _clutter_input_device_add_axis (ClutterInputDevice *device, ClutterInputAxis axis, @@ -1134,6 +1191,18 @@ clutter_input_device_get_n_axes (ClutterInputDevice *device) return 0; } +/*< private > + * clutter_input_device_set_keys: + * @device: a #ClutterInputDevice + * @n_keys: the number of keys of the device + * @min_keycode: the minimum key code + * @max_keycode: the maximum key code + * + * Initializes the keys of @device. + * + * Call clutter_input_device_set_key() on each key to set the keyval + * and modifiers. + */ void _clutter_input_device_set_keys (ClutterInputDevice *device, guint n_keys, diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index bb2c5577b..5d9875ffc 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -1082,9 +1082,8 @@ clutter_input_device_get_device_type clutter_input_device_get_device_name clutter_input_device_get_device_mode clutter_input_device_get_has_cursor -clutter_input_device_get_device_coords -clutter_input_device_get_pointer_actor -clutter_input_device_get_pointer_stage +clutter_input_device_set_enabled +clutter_input_device_get_enabled clutter_input_device_get_associated_device clutter_input_device_get_slave_devices @@ -1098,6 +1097,11 @@ clutter_input_device_get_n_axes clutter_input_device_get_axis clutter_input_device_get_axis_value + +clutter_input_device_get_device_coords +clutter_input_device_get_pointer_actor +clutter_input_device_get_pointer_stage + clutter_input_device_update_from_event From a277b4091a3fa09a39cf001809c3048eb27f3952 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 20 Jan 2011 15:39:28 +0000 Subject: [PATCH 34/41] x11: Hide all private symbols The x11 backend exposes a lot of symbols that are meant to only be used when implementing a subclassed backend, like the glx and eglx ones. The uninstalled headers are also filled with cruft declarations of functions long since removed. Let's try to clean up this mess. --- clutter/egl/clutter-backend-egl.c | 13 +++++++++---- clutter/glx/clutter-backend-glx.c | 19 ++++++++++++++----- clutter/x11/clutter-backend-x11.c | 28 ++++++++++------------------ clutter/x11/clutter-backend-x11.h | 31 +++---------------------------- clutter/x11/clutter-keymap-x11.c | 4 ++-- clutter/x11/clutter-keymap-x11.h | 4 ++-- clutter/x11/clutter-stage-x11.c | 12 +++++++----- clutter/x11/clutter-stage-x11.h | 13 ++----------- 8 files changed, 49 insertions(+), 75 deletions(-) diff --git a/clutter/egl/clutter-backend-egl.c b/clutter/egl/clutter-backend-egl.c index c70e331ef..deb87761a 100644 --- a/clutter/egl/clutter-backend-egl.c +++ b/clutter/egl/clutter-backend-egl.c @@ -290,7 +290,7 @@ retry: * a dummy, offscreen override-redirect window to which we can always * fall back if no stage is available */ - xvisinfo = clutter_backend_x11_get_visual_info (backend_x11); + xvisinfo = _clutter_backend_x11_get_visual_info (backend_x11); if (xvisinfo == NULL) { g_critical ("Unable to find suitable GL visual."); @@ -679,7 +679,7 @@ check_vblank_env (const char *name) static ClutterFeatureFlags clutter_backend_egl_get_features (ClutterBackend *backend) { - ClutterBackendEGL *backend_egl = CLUTTER_BACKEND_EGL (backend); + ClutterBackendEGL *backend_egl = CLUTTER_BACKEND_EGL (backend); const gchar *egl_extensions = NULL; const gchar *gl_extensions = NULL; ClutterFeatureFlags flags; @@ -687,8 +687,13 @@ clutter_backend_egl_get_features (ClutterBackend *backend) g_assert (backend_egl->egl_context != NULL); #ifdef COGL_HAS_XLIB_SUPPORT - flags = clutter_backend_x11_get_features (backend); - flags |= CLUTTER_FEATURE_STAGE_MULTIPLE; + { + ClutterBackendClass *parent_class; + + parent_class = CLUTTER_BACKEND_CLASS (_clutter_backend_egl_parent_class); + flags = parent_class->get_features (backend); + flags |= CLUTTER_FEATURE_STAGE_MULTIPLE; + } #else flags = CLUTTER_FEATURE_STAGE_STATIC; #endif diff --git a/clutter/glx/clutter-backend-glx.c b/clutter/glx/clutter-backend-glx.c index 6e5258d91..2b6dcbd30 100644 --- a/clutter/glx/clutter-backend-glx.c +++ b/clutter/glx/clutter-backend-glx.c @@ -69,6 +69,8 @@ static gboolean clutter_backend_glx_pre_parse (ClutterBackend *backend, GError **error) { + ClutterBackendClass *parent_class = + CLUTTER_BACKEND_CLASS (_clutter_backend_glx_parent_class); const gchar *env_string; env_string = g_getenv ("CLUTTER_VBLANK"); @@ -78,7 +80,7 @@ clutter_backend_glx_pre_parse (ClutterBackend *backend, env_string = NULL; } - return clutter_backend_x11_pre_parse (backend, error); + return parent_class->pre_parse (backend, error); } static gboolean @@ -87,11 +89,11 @@ clutter_backend_glx_post_parse (ClutterBackend *backend, { ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (backend); ClutterBackendGLX *backend_glx = CLUTTER_BACKEND_GLX (backend); - ClutterBackendClass *backend_class = + ClutterBackendClass *parent_class = CLUTTER_BACKEND_CLASS (_clutter_backend_glx_parent_class); int glx_major, glx_minor; - if (!backend_class->post_parse (backend, error)) + if (!parent_class->post_parse (backend, error)) return FALSE; if (!glXQueryExtension (backend_x11->xdpy, @@ -134,8 +136,12 @@ static void clutter_backend_glx_add_options (ClutterBackend *backend, GOptionGroup *group) { + ClutterBackendClass *parent_class = + CLUTTER_BACKEND_CLASS (_clutter_backend_glx_parent_class); + g_option_group_add_entries (group, entries); - clutter_backend_x11_add_options (backend, group); + + parent_class->add_options (backend, group); } static void @@ -215,12 +221,15 @@ static ClutterFeatureFlags clutter_backend_glx_get_features (ClutterBackend *backend) { ClutterBackendGLX *backend_glx = CLUTTER_BACKEND_GLX (backend); + ClutterBackendClass *parent_class; const gchar *glx_extensions = NULL; const gchar *gl_extensions = NULL; ClutterFeatureFlags flags; gboolean use_dri = FALSE; - flags = clutter_backend_x11_get_features (backend); + parent_class = CLUTTER_BACKEND_CLASS (_clutter_backend_glx_parent_class); + + flags = parent_class->get_features (backend); flags |= CLUTTER_FEATURE_STAGE_MULTIPLE; /* this will make sure that the GL context exists */ diff --git a/clutter/x11/clutter-backend-x11.c b/clutter/x11/clutter-backend-x11.c index cf222714d..2215c3356 100644 --- a/clutter/x11/clutter-backend-x11.c +++ b/clutter/x11/clutter-backend-x11.c @@ -68,6 +68,8 @@ #include "clutter-main.h" #include "clutter-private.h" +#define clutter_backend_x11_get_type _clutter_backend_x11_get_type + G_DEFINE_TYPE (ClutterBackendX11, clutter_backend_x11, CLUTTER_TYPE_BACKEND); /* atoms; remember to add the code that assigns the atom value to @@ -294,8 +296,8 @@ clutter_backend_x11_create_keymap (ClutterBackendX11 *backend_x11) } gboolean -clutter_backend_x11_pre_parse (ClutterBackend *backend, - GError **error) +_clutter_backend_x11_pre_parse (ClutterBackend *backend, + GError **error) { const gchar *env_string; @@ -327,8 +329,8 @@ clutter_backend_x11_pre_parse (ClutterBackend *backend, } gboolean -clutter_backend_x11_post_parse (ClutterBackend *backend, - GError **error) +_clutter_backend_x11_post_parse (ClutterBackend *backend, + GError **error) { ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (backend); @@ -716,8 +718,8 @@ clutter_backend_x11_class_init (ClutterBackendX11Class *klass) gobject_class->dispose = clutter_backend_x11_dispose; gobject_class->finalize = clutter_backend_x11_finalize; - backend_class->pre_parse = clutter_backend_x11_pre_parse; - backend_class->post_parse = clutter_backend_x11_post_parse; + backend_class->pre_parse = _clutter_backend_x11_pre_parse; + backend_class->post_parse = _clutter_backend_x11_post_parse; backend_class->init_events = clutter_backend_x11_init_events; backend_class->add_options = clutter_backend_x11_add_options; backend_class->get_features = clutter_backend_x11_get_features; @@ -1015,16 +1017,6 @@ clutter_x11_remove_filter (ClutterX11FilterFunc func, } } -ClutterInputDevice * -_clutter_x11_get_device_for_xid (XID id) -{ - ClutterDeviceManager *manager; - - manager = clutter_device_manager_get_default (); - - return clutter_device_manager_get_device (manager, (gint) id); -} - /** * clutter_x11_get_input_devices: * @@ -1167,7 +1159,7 @@ clutter_x11_get_use_argb_visual (void) } XVisualInfo * -clutter_backend_x11_get_visual_info (ClutterBackendX11 *backend_x11) +_clutter_backend_x11_get_visual_info (ClutterBackendX11 *backend_x11) { ClutterBackendX11Class *klass; @@ -1199,7 +1191,7 @@ clutter_x11_get_visual_info (void) backend_x11 = CLUTTER_BACKEND_X11 (clutter_get_default_backend ()); - return clutter_backend_x11_get_visual_info (backend_x11); + return _clutter_backend_x11_get_visual_info (backend_x11); } void diff --git a/clutter/x11/clutter-backend-x11.h b/clutter/x11/clutter-backend-x11.h index 737e13915..81ad573d5 100644 --- a/clutter/x11/clutter-backend-x11.h +++ b/clutter/x11/clutter-backend-x11.h @@ -37,7 +37,7 @@ G_BEGIN_DECLS -#define CLUTTER_TYPE_BACKEND_X11 (clutter_backend_x11_get_type ()) +#define CLUTTER_TYPE_BACKEND_X11 (_clutter_backend_x11_get_type ()) #define CLUTTER_BACKEND_X11(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_BACKEND_X11, ClutterBackendX11)) #define CLUTTER_IS_BACKEND_X11(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_BACKEND_X11)) #define CLUTTER_BACKEND_X11_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_BACKEND_X11, ClutterBackendX11Class)) @@ -128,36 +128,11 @@ struct _ClutterBackendX11Class void _clutter_backend_x11_events_init (ClutterBackend *backend); void _clutter_backend_x11_events_uninit (ClutterBackend *backend); -GType clutter_backend_x11_get_type (void) G_GNUC_CONST; +GType _clutter_backend_x11_get_type (void) G_GNUC_CONST; /* Private to glx/eglx backends */ -gboolean -clutter_backend_x11_pre_parse (ClutterBackend *backend, - GError **error); - -gboolean -clutter_backend_x11_post_parse (ClutterBackend *backend, - GError **error); - -gboolean -clutter_backend_x11_init_stage (ClutterBackend *backend, - GError **error); - -ClutterActor * -clutter_backend_x11_get_stage (ClutterBackend *backend); - -void -clutter_backend_x11_add_options (ClutterBackend *backend, - GOptionGroup *group); - -ClutterFeatureFlags -clutter_backend_x11_get_features (ClutterBackend *backend); - XVisualInfo * -clutter_backend_x11_get_visual_info (ClutterBackendX11 *backend_x11); - -ClutterInputDevice * -_clutter_x11_get_device_for_xid (XID id); +_clutter_backend_x11_get_visual_info (ClutterBackendX11 *backend_x11); void _clutter_x11_select_events (Window xwin); diff --git a/clutter/x11/clutter-keymap-x11.c b/clutter/x11/clutter-keymap-x11.c index 33b690ea9..7336190d4 100644 --- a/clutter/x11/clutter-keymap-x11.c +++ b/clutter/x11/clutter-keymap-x11.c @@ -21,9 +21,7 @@ * Author: Emmanuele Bassi */ -#ifdef HAVE_CONFIG_H #include "config.h" -#endif #include "clutter-keymap-x11.h" #include "clutter-backend-x11.h" @@ -80,6 +78,8 @@ enum static GParamSpec *obj_props[PROP_LAST]; +#define clutter_keymap_x11_get_type _clutter_keymap_x11_get_type + G_DEFINE_TYPE (ClutterKeymapX11, clutter_keymap_x11, G_TYPE_OBJECT); #ifdef HAVE_XKB diff --git a/clutter/x11/clutter-keymap-x11.h b/clutter/x11/clutter-keymap-x11.h index 34abf3997..7f6b4224d 100644 --- a/clutter/x11/clutter-keymap-x11.h +++ b/clutter/x11/clutter-keymap-x11.h @@ -29,13 +29,13 @@ G_BEGIN_DECLS -#define CLUTTER_TYPE_KEYMAP_X11 (clutter_keymap_x11_get_type ()) +#define CLUTTER_TYPE_KEYMAP_X11 (_clutter_keymap_x11_get_type ()) #define CLUTTER_KEYMAP_X11(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_KEYMAP_X11, ClutterKeymapX11)) #define CLUTTER_IS_KEYMAP_X11(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_KEYMAP_X11)) typedef struct _ClutterKeymapX11 ClutterKeymapX11; -GType clutter_keymap_x11_get_type (void) G_GNUC_CONST; +GType _clutter_keymap_x11_get_type (void) G_GNUC_CONST; gint _clutter_keymap_x11_get_key_group (ClutterKeymapX11 *keymap, ClutterModifierType state); diff --git a/clutter/x11/clutter-stage-x11.c b/clutter/x11/clutter-stage-x11.c index 5142e4085..2ba9e983a 100644 --- a/clutter/x11/clutter-stage-x11.c +++ b/clutter/x11/clutter-stage-x11.c @@ -54,6 +54,8 @@ static void clutter_stage_window_iface_init (ClutterStageWindowIface *iface); static void clutter_event_translator_iface_init (ClutterEventTranslatorIface *iface); +#define clutter_stage_x11_get_type _clutter_stage_x11_get_type + G_DEFINE_TYPE_WITH_CODE (ClutterStageX11, clutter_stage_x11, G_TYPE_OBJECT, @@ -120,7 +122,7 @@ update_state (ClutterStageX11 *stage_x11, } } -void +static void clutter_stage_x11_fix_window_size (ClutterStageX11 *stage_x11, gint new_width, gint new_height) @@ -179,7 +181,7 @@ clutter_stage_x11_fix_window_size (ClutterStageX11 *stage_x11, } } -void +static void clutter_stage_x11_set_wm_protocols (ClutterStageX11 *stage_x11) { ClutterBackend *backend = clutter_get_default_backend (); @@ -1218,7 +1220,7 @@ clutter_x11_get_stage_visual (ClutterStage *stage) g_return_val_if_fail (CLUTTER_IS_BACKEND_X11 (backend), NULL); backend_x11 = CLUTTER_BACKEND_X11 (backend); - return clutter_backend_x11_get_visual_info (backend_x11); + return _clutter_backend_x11_get_visual_info (backend_x11); } typedef struct { @@ -1297,7 +1299,7 @@ clutter_x11_set_stage_foreign (ClutterStage *stage, actor = CLUTTER_ACTOR (stage); - xvisinfo = clutter_backend_x11_get_visual_info (backend_x11); + xvisinfo = _clutter_backend_x11_get_visual_info (backend_x11); g_return_val_if_fail (xvisinfo != NULL, FALSE); clutter_x11_trap_x_errors (); @@ -1412,7 +1414,7 @@ _clutter_stage_x11_create_window (ClutterStageX11 *stage_x11) backend_x11 = CLUTTER_BACKEND_X11 (clutter_get_default_backend ()); - xvisinfo = clutter_backend_x11_get_visual_info (backend_x11); + xvisinfo = _clutter_backend_x11_get_visual_info (backend_x11); if (xvisinfo == NULL) { g_critical ("Unable to find suitable GL visual."); diff --git a/clutter/x11/clutter-stage-x11.h b/clutter/x11/clutter-stage-x11.h index b0e1e3657..da839a58c 100644 --- a/clutter/x11/clutter-stage-x11.h +++ b/clutter/x11/clutter-stage-x11.h @@ -31,7 +31,7 @@ G_BEGIN_DECLS -#define CLUTTER_TYPE_STAGE_X11 (clutter_stage_x11_get_type ()) +#define CLUTTER_TYPE_STAGE_X11 (_clutter_stage_x11_get_type ()) #define CLUTTER_STAGE_X11(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_STAGE_X11, ClutterStageX11)) #define CLUTTER_IS_STAGE_X11(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_STAGE_X11)) #define CLUTTER_STAGE_X11_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_STAGE_X11, ClutterStageX11Class)) @@ -78,18 +78,9 @@ struct _ClutterStageX11Class ClutterGroupClass parent_class; }; -GType clutter_stage_x11_get_type (void) G_GNUC_CONST; +GType _clutter_stage_x11_get_type (void) G_GNUC_CONST; /* Private to subclasses */ -void clutter_stage_x11_fix_window_size (ClutterStageX11 *stage_x11, - gint new_width, - gint new_height); -void clutter_stage_x11_set_wm_protocols (ClutterStageX11 *stage_x11); -void clutter_stage_x11_map (ClutterStageX11 *stage_x11); -void clutter_stage_x11_unmap (ClutterStageX11 *stage_x11); - -GList *clutter_stage_x11_get_input_devices (ClutterStageX11 *stage_x11); - gboolean _clutter_stage_x11_create_window (ClutterStageX11 *stage_x11); void _clutter_stage_x11_destroy_window_untrapped (ClutterStageX11 *stage_x11); void _clutter_stage_x11_destroy_window (ClutterStageX11 *stage_x11); From 6da51f6ed379f531bee72b688366dc734082a573 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 21 Jan 2011 10:24:34 +0000 Subject: [PATCH 35/41] device/x11: Store min/max keycode in the XI device class The generic device class shouldn't have the minimum and maximum keycode, since no other input backend provides those. --- clutter/clutter-device-manager-private.h | 8 ++------ clutter/clutter-input-device.c | 18 +++--------------- clutter/x11/clutter-device-manager-core-x11.c | 10 +++++++--- clutter/x11/clutter-device-manager-xi2.c | 6 ++---- clutter/x11/clutter-input-device-core-x11.c | 9 ++++++--- 5 files changed, 20 insertions(+), 31 deletions(-) diff --git a/clutter/clutter-device-manager-private.h b/clutter/clutter-device-manager-private.h index 210995170..eca8d119e 100644 --- a/clutter/clutter-device-manager-private.h +++ b/clutter/clutter-device-manager-private.h @@ -100,8 +100,6 @@ struct _ClutterInputDevice guint n_keys; GArray *keys; - gint min_keycode; - gint max_keycode; guint has_cursor : 1; guint is_enabled : 1; @@ -140,10 +138,8 @@ void _clutter_input_device_set_stage (ClutterInputDev void _clutter_input_device_set_actor (ClutterInputDevice *device, ClutterActor *actor); ClutterActor * _clutter_input_device_update (ClutterInputDevice *device); -void _clutter_input_device_set_keys (ClutterInputDevice *device, - guint n_keys, - gint min_keycode, - gint max_keycode); +void _clutter_input_device_set_n_keys (ClutterInputDevice *device, + guint n_keys); guint _clutter_input_device_add_axis (ClutterInputDevice *device, ClutterInputAxis axis, gdouble min_value, diff --git a/clutter/clutter-input-device.c b/clutter/clutter-input-device.c index 1f5360f1c..062e25b02 100644 --- a/clutter/clutter-input-device.c +++ b/clutter/clutter-input-device.c @@ -364,9 +364,6 @@ clutter_input_device_init (ClutterInputDevice *self) self->current_y = self->previous_y = -1; self->current_button_number = self->previous_button_number = -1; self->current_state = self->previous_state = 0; - - self->min_keycode = 0; - self->max_keycode = G_MAXUINT; } /*< private > @@ -1192,11 +1189,9 @@ clutter_input_device_get_n_axes (ClutterInputDevice *device) } /*< private > - * clutter_input_device_set_keys: + * clutter_input_device_set_n_keys: * @device: a #ClutterInputDevice * @n_keys: the number of keys of the device - * @min_keycode: the minimum key code - * @max_keycode: the maximum key code * * Initializes the keys of @device. * @@ -1204,10 +1199,8 @@ clutter_input_device_get_n_axes (ClutterInputDevice *device) * and modifiers. */ void -_clutter_input_device_set_keys (ClutterInputDevice *device, - guint n_keys, - gint min_keycode, - gint max_keycode) +_clutter_input_device_set_n_keys (ClutterInputDevice *device, + guint n_keys) { if (device->keys != NULL) g_array_free (device->keys, TRUE); @@ -1216,9 +1209,6 @@ _clutter_input_device_set_keys (ClutterInputDevice *device, device->keys = g_array_sized_new (FALSE, TRUE, sizeof (ClutterKeyInfo), n_keys); - - device->min_keycode = min_keycode; - device->max_keycode = max_keycode; } /** @@ -1263,8 +1253,6 @@ clutter_input_device_set_key (ClutterInputDevice *device, g_return_if_fail (CLUTTER_IS_INPUT_DEVICE (device)); g_return_if_fail (index_ < device->n_keys); - g_return_if_fail (keyval >= device->min_keycode && - keyval <= device->max_keycode); key_info = &g_array_index (device->keys, ClutterKeyInfo, index_); key_info->keyval = keyval; diff --git a/clutter/x11/clutter-device-manager-core-x11.c b/clutter/x11/clutter-device-manager-core-x11.c index af3397366..c9c8e93d5 100644 --- a/clutter/x11/clutter-device-manager-core-x11.c +++ b/clutter/x11/clutter-device-manager-core-x11.c @@ -86,12 +86,16 @@ translate_class_info (ClutterInputDevice *device, case KeyClass: { XKeyInfo *xk_info = (XKeyInfo *) any_class; + ClutterInputDeviceX11 *device_x11; guint n_keys; + device_x11 = CLUTTER_INPUT_DEVICE_X11 (device); + n_keys = xk_info->max_keycode - xk_info->min_keycode + 1; - _clutter_input_device_set_keys (device, n_keys, - xk_info->min_keycode, - xk_info->max_keycode); + + _clutter_input_device_set_n_keys (device, n_keys); + device_x11->min_keycode = xk_info->min_keycode; + device_x11->max_keycode = xk_info->max_keycode; } break; diff --git a/clutter/x11/clutter-device-manager-xi2.c b/clutter/x11/clutter-device-manager-xi2.c index e9fbbdb68..13c814871 100644 --- a/clutter/x11/clutter-device-manager-xi2.c +++ b/clutter/x11/clutter-device-manager-xi2.c @@ -126,10 +126,8 @@ translate_device_classes (Display *xdisplay, XIKeyClassInfo *key_info = (XIKeyClassInfo *) class_info; gint j; - _clutter_input_device_set_keys (device, - key_info->num_keycodes, - 0, - G_MAXUINT); + _clutter_input_device_set_n_keys (device, + key_info->num_keycodes); for (j = 0; j < key_info->num_keycodes; j++) { diff --git a/clutter/x11/clutter-input-device-core-x11.c b/clutter/x11/clutter-input-device-core-x11.c index d23cada39..216bddcb7 100644 --- a/clutter/x11/clutter-input-device-core-x11.c +++ b/clutter/x11/clutter-input-device-core-x11.c @@ -61,6 +61,9 @@ struct _ClutterInputDeviceX11 #endif /* HAVE_XINPUT */ gint *axis_data; + + gint min_keycode; + gint max_keycode; }; #define clutter_input_device_x11_get_type _clutter_input_device_x11_get_type @@ -349,15 +352,15 @@ _clutter_input_device_x11_translate_xi_event (ClutterInputDeviceX11 *device_x11, { XDeviceKeyEvent *xdke = (XDeviceKeyEvent *) xevent; - if (xdke->keycode < device->min_keycode || - xdke->keycode >= device->max_keycode) + if (xdke->keycode < device_x11->min_keycode || + xdke->keycode >= device_x11->max_keycode) { g_warning ("Invalid device key code received: %d", xdke->keycode); return FALSE; } clutter_input_device_get_key (device, - xdke->keycode - device->min_keycode, + xdke->keycode - device_x11->min_keycode, &event->key.keyval, &event->key.modifier_state); if (event->key.keyval == 0) From 7514f5fe92844a57d33a4811fd20687bbb391c8b Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 21 Jan 2011 10:49:12 +0000 Subject: [PATCH 36/41] glx: Clean up GLX implementation Like commit a277b4091a3fa09a39cf001809c3048eb27f3952, but targeted at the GLX backend. --- clutter/glx/clutter-backend-glx.c | 22 ++++++------ clutter/glx/clutter-stage-glx.c | 57 ++++++++++++++----------------- clutter/glx/clutter-stage-glx.h | 2 +- 3 files changed, 38 insertions(+), 43 deletions(-) diff --git a/clutter/glx/clutter-backend-glx.c b/clutter/glx/clutter-backend-glx.c index 2b6dcbd30..46bb6ff2a 100644 --- a/clutter/glx/clutter-backend-glx.c +++ b/clutter/glx/clutter-backend-glx.c @@ -51,8 +51,9 @@ #include "cogl/cogl.h" +#define clutter_backend_glx_get_type _clutter_backend_glx_get_type -G_DEFINE_TYPE (ClutterBackendGLX, _clutter_backend_glx, CLUTTER_TYPE_BACKEND_X11); +G_DEFINE_TYPE (ClutterBackendGLX, clutter_backend_glx, CLUTTER_TYPE_BACKEND_X11); /* singleton object */ static ClutterBackendGLX *backend_singleton = NULL; @@ -70,7 +71,7 @@ clutter_backend_glx_pre_parse (ClutterBackend *backend, GError **error) { ClutterBackendClass *parent_class = - CLUTTER_BACKEND_CLASS (_clutter_backend_glx_parent_class); + CLUTTER_BACKEND_CLASS (clutter_backend_glx_parent_class); const gchar *env_string; env_string = g_getenv ("CLUTTER_VBLANK"); @@ -90,7 +91,7 @@ clutter_backend_glx_post_parse (ClutterBackend *backend, ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (backend); ClutterBackendGLX *backend_glx = CLUTTER_BACKEND_GLX (backend); ClutterBackendClass *parent_class = - CLUTTER_BACKEND_CLASS (_clutter_backend_glx_parent_class); + CLUTTER_BACKEND_CLASS (clutter_backend_glx_parent_class); int glx_major, glx_minor; if (!parent_class->post_parse (backend, error)) @@ -137,7 +138,7 @@ clutter_backend_glx_add_options (ClutterBackend *backend, GOptionGroup *group) { ClutterBackendClass *parent_class = - CLUTTER_BACKEND_CLASS (_clutter_backend_glx_parent_class); + CLUTTER_BACKEND_CLASS (clutter_backend_glx_parent_class); g_option_group_add_entries (group, entries); @@ -150,7 +151,7 @@ clutter_backend_glx_finalize (GObject *gobject) if (backend_singleton) backend_singleton = NULL; - G_OBJECT_CLASS (_clutter_backend_glx_parent_class)->finalize (gobject); + G_OBJECT_CLASS (clutter_backend_glx_parent_class)->finalize (gobject); } static void @@ -181,7 +182,7 @@ clutter_backend_glx_dispose (GObject *gobject) backend_glx->dummy_xwin = None; } - G_OBJECT_CLASS (_clutter_backend_glx_parent_class)->dispose (gobject); + G_OBJECT_CLASS (clutter_backend_glx_parent_class)->dispose (gobject); } static GObject * @@ -194,7 +195,7 @@ clutter_backend_glx_constructor (GType gtype, if (!backend_singleton) { - parent_class = G_OBJECT_CLASS (_clutter_backend_glx_parent_class); + parent_class = G_OBJECT_CLASS (clutter_backend_glx_parent_class); retval = parent_class->constructor (gtype, n_params, params); backend_singleton = CLUTTER_BACKEND_GLX (retval); @@ -227,7 +228,7 @@ clutter_backend_glx_get_features (ClutterBackend *backend) ClutterFeatureFlags flags; gboolean use_dri = FALSE; - parent_class = CLUTTER_BACKEND_CLASS (_clutter_backend_glx_parent_class); + parent_class = CLUTTER_BACKEND_CLASS (clutter_backend_glx_parent_class); flags = parent_class->get_features (backend); flags |= CLUTTER_FEATURE_STAGE_MULTIPLE; @@ -824,7 +825,7 @@ clutter_backend_glx_create_stage (ClutterBackend *backend, } static void -_clutter_backend_glx_class_init (ClutterBackendGLXClass *klass) +clutter_backend_glx_class_init (ClutterBackendGLXClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterBackendClass *backend_class = CLUTTER_BACKEND_CLASS (klass); @@ -847,9 +848,8 @@ _clutter_backend_glx_class_init (ClutterBackendGLXClass *klass) } static void -_clutter_backend_glx_init (ClutterBackendGLX *backend_glx) +clutter_backend_glx_init (ClutterBackendGLX *backend_glx) { - } /* every backend must implement this function */ diff --git a/clutter/glx/clutter-stage-glx.c b/clutter/glx/clutter-stage-glx.c index f3dc2cb3f..f393cc0af 100644 --- a/clutter/glx/clutter-stage-glx.c +++ b/clutter/glx/clutter-stage-glx.c @@ -55,10 +55,13 @@ static void clutter_stage_window_iface_init (ClutterStageWindowIface *iface); static void clutter_event_translator_iface_init (ClutterEventTranslatorIface *iface); -static ClutterStageWindowIface *clutter_stage_glx_parent_iface = NULL; +static ClutterStageWindowIface *clutter_stage_window_parent_iface = NULL; +static ClutterEventTranslatorIface *clutter_event_translator_parent_iface = NULL; + +#define clutter_stage_glx_get_type _clutter_stage_glx_get_type G_DEFINE_TYPE_WITH_CODE (ClutterStageGLX, - _clutter_stage_glx, + clutter_stage_glx, CLUTTER_TYPE_STAGE_X11, G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_STAGE_WINDOW, clutter_stage_window_iface_init) @@ -150,7 +153,7 @@ clutter_stage_glx_realize (ClutterStageWindow *stage_window) #endif /* GLX_INTEL_swap_event */ /* chain up to the StageX11 implementation */ - return clutter_stage_glx_parent_iface->realize (stage_window); + return clutter_stage_window_parent_iface->realize (stage_window); } static int @@ -162,21 +165,12 @@ clutter_stage_glx_get_pending_swaps (ClutterStageWindow *stage_window) } static void -clutter_stage_glx_dispose (GObject *gobject) +clutter_stage_glx_class_init (ClutterStageGLXClass *klass) { - G_OBJECT_CLASS (_clutter_stage_glx_parent_class)->dispose (gobject); } static void -_clutter_stage_glx_class_init (ClutterStageGLXClass *klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - - gobject_class->dispose = clutter_stage_glx_dispose; -} - -static void -_clutter_stage_glx_init (ClutterStageGLX *stage) +clutter_stage_glx_init (ClutterStageGLX *stage) { } @@ -320,7 +314,7 @@ clutter_stage_glx_add_redraw_clip (ClutterStageWindow *stage_window, static void clutter_stage_window_iface_init (ClutterStageWindowIface *iface) { - clutter_stage_glx_parent_iface = g_type_interface_peek_parent (iface); + clutter_stage_window_parent_iface = g_type_interface_peek_parent (iface); iface->realize = clutter_stage_glx_realize; iface->unrealize = clutter_stage_glx_unrealize; @@ -333,8 +327,6 @@ clutter_stage_window_iface_init (ClutterStageWindowIface *iface) /* the rest is inherited from ClutterStageX11 */ } -static ClutterEventTranslatorIface *event_translator_parent_iface = NULL; - static ClutterTranslateReturn clutter_stage_glx_translate_event (ClutterEventTranslator *translator, gpointer native, @@ -371,15 +363,15 @@ clutter_stage_glx_translate_event (ClutterEventTranslator *translator, #endif /* chain up to the common X11 implementation */ - return event_translator_parent_iface->translate_event (translator, - native, - event); + return clutter_event_translator_parent_iface->translate_event (translator, + native, + event); } static void clutter_event_translator_iface_init (ClutterEventTranslatorIface *iface) { - event_translator_parent_iface = g_type_interface_peek_parent (iface); + clutter_event_translator_parent_iface = g_type_interface_peek_parent (iface); iface->translate_event = clutter_stage_glx_translate_event; } @@ -462,12 +454,14 @@ _clutter_stage_glx_redraw (ClutterStageGLX *stage_glx, "The time spent in _glx_blit_sub_buffer", 0 /* no application private data */); + stage_x11 = CLUTTER_STAGE_X11 (stage_glx); + if (stage_x11->xwin == None) + return; + backend = clutter_get_default_backend (); backend_x11 = CLUTTER_BACKEND_X11 (backend); backend_glx = CLUTTER_BACKEND_GLX (backend); - stage_x11 = CLUTTER_STAGE_X11 (stage_glx); - CLUTTER_TIMER_START (_clutter_uprof_context, painting_timer); if (G_LIKELY (backend_glx->can_blit_sub_buffer) && @@ -480,7 +474,9 @@ _clutter_stage_glx_redraw (ClutterStageGLX *stage_glx, * artefacts. See clutter-event-x11.c:event_translate for a * detailed explanation */ G_LIKELY (stage_x11->clipped_redraws_cool_off == 0)) - may_use_clipped_redraw = TRUE; + { + may_use_clipped_redraw = TRUE; + } else may_use_clipped_redraw = FALSE; @@ -503,7 +499,7 @@ _clutter_stage_glx_redraw (ClutterStageGLX *stage_glx, else _clutter_stage_do_paint (stage, NULL); - if (clutter_paint_debug_flags & CLUTTER_DEBUG_REDRAWS && + if ((clutter_paint_debug_flags & CLUTTER_DEBUG_REDRAWS) && may_use_clipped_redraw) { ClutterGeometry *clip = &stage_glx->bounding_redraw_clip; @@ -552,10 +548,9 @@ _clutter_stage_glx_redraw (ClutterStageGLX *stage_glx, cogl_flush (); CLUTTER_TIMER_STOP (_clutter_uprof_context, painting_timer); - if (stage_x11->xwin == None) - return; - - drawable = stage_glx->glxwin ? stage_glx->glxwin : stage_x11->xwin; + drawable = stage_glx->glxwin + ? stage_glx->glxwin + : stage_x11->xwin; /* If we might ever use _clutter_backend_glx_blit_sub_buffer then we * always need to keep track of the video_sync_count so that we can @@ -592,7 +587,8 @@ _clutter_stage_glx_redraw (ClutterStageGLX *stage_glx, * anyway so it should only exhibit temporary artefacts. */ copy_area.y = clutter_actor_get_height (CLUTTER_ACTOR (stage)) - - clip->y - clip->height; + - clip->y + - clip->height; copy_area.x = clip->x; copy_area.width = clip->width; copy_area.height = clip->height; @@ -691,4 +687,3 @@ _clutter_stage_glx_redraw (ClutterStageGLX *stage_glx, stage_glx->frame_count++; } - diff --git a/clutter/glx/clutter-stage-glx.h b/clutter/glx/clutter-stage-glx.h index bc81f0baa..0fdf1dcf6 100644 --- a/clutter/glx/clutter-stage-glx.h +++ b/clutter/glx/clutter-stage-glx.h @@ -70,7 +70,7 @@ struct _ClutterStageGLXClass GType _clutter_stage_glx_get_type (void) G_GNUC_CONST; void _clutter_stage_glx_redraw (ClutterStageGLX *stage_glx, - ClutterStage *stage); + ClutterStage *stage); G_END_DECLS From 0e993469155516dde673292bafe72d1c8308e27f Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 21 Jan 2011 11:41:36 +0000 Subject: [PATCH 37/41] device-manager/xi2: Fix device hotplugging Hierarchy and Device changed events come through with the X window set to be the root window, not the stage window. We need to whitelist them so that we can actually support hotplugging and device changes. --- clutter/x11/clutter-device-manager-xi2.c | 32 +++++---- tests/interactive/test-devices.c | 83 +++++++++++++++++++++--- 2 files changed, 95 insertions(+), 20 deletions(-) diff --git a/clutter/x11/clutter-device-manager-xi2.c b/clutter/x11/clutter-device-manager-xi2.c index 13c814871..bb438d56b 100644 --- a/clutter/x11/clutter-device-manager-xi2.c +++ b/clutter/x11/clutter-device-manager-xi2.c @@ -235,9 +235,6 @@ add_device (ClutterDeviceManagerXI2 *manager_xi2, /* we don't go through the DeviceManager::add_device() vfunc because * that emits the signal, and we only do it conditionally - * - * FIXME: add a boolean "emit_signal" argument to the add_device() - * wrapper in ClutterDeviceManager, and emit the signal only if true */ g_hash_table_replace (manager_xi2->devices_by_id, GINT_TO_POINTER (info->deviceid), @@ -329,6 +326,8 @@ translate_hierarchy_event (ClutterBackendX11 *backend_x11, XIDeviceInfo *info; int n_devices; + CLUTTER_NOTE (EVENT, "Hierarchy event: device enabled"); + info = XIQueryDevice (backend_x11->xdpy, ev->info[i].deviceid, &n_devices); @@ -336,6 +335,8 @@ translate_hierarchy_event (ClutterBackendX11 *backend_x11, } else if (ev->info[i].flags & XIDeviceDisabled) { + CLUTTER_NOTE (EVENT, "Hierarchy event: device disabled"); + remove_device (manager_xi2, ev->info[i].deviceid); } else if ((ev->info[i].flags & XISlaveAttached) || @@ -345,6 +346,11 @@ translate_hierarchy_event (ClutterBackendX11 *backend_x11, XIDeviceInfo *info; int n_devices; + CLUTTER_NOTE (EVENT, "Hierarchy event: slave %s", + (ev->info[i].flags & XISlaveAttached) + ? "attached" + : "detached"); + slave = g_hash_table_lookup (manager_xi2->devices_by_id, GINT_TO_POINTER (ev->info[i].deviceid)); master = clutter_input_device_get_associated_device (slave); @@ -533,8 +539,8 @@ clutter_device_manager_xi2_translate_event (ClutterEventTranslator *translator, ClutterDeviceManagerXI2 *manager_xi2 = CLUTTER_DEVICE_MANAGER_XI2 (translator); ClutterTranslateReturn retval = CLUTTER_TRANSLATE_CONTINUE; ClutterBackendX11 *backend_x11; - ClutterStageX11 *stage_x11; - ClutterStage *stage; + ClutterStageX11 *stage_x11 = NULL; + ClutterStage *stage = NULL; ClutterInputDevice *device; XGenericEventCookie *cookie; XIEvent *xi_event; @@ -558,15 +564,19 @@ clutter_device_manager_xi2_translate_event (ClutterEventTranslator *translator, xi_event = (XIEvent *) cookie->data; - stage = get_event_stage (translator, xi_event); - if (stage == NULL || CLUTTER_ACTOR_IN_DESTRUCTION (stage)) + if (!(xi_event->evtype == XI_HierarchyChanged || + xi_event->evtype == XI_DeviceChanged)) { - XFreeEventData (backend_x11->xdpy, cookie); - return CLUTTER_TRANSLATE_CONTINUE; + stage = get_event_stage (translator, xi_event); + if (stage == NULL || CLUTTER_ACTOR_IN_DESTRUCTION (stage)) + { + XFreeEventData (backend_x11->xdpy, cookie); + return CLUTTER_TRANSLATE_CONTINUE; + } + else + stage_x11 = CLUTTER_STAGE_X11 (_clutter_stage_get_window (stage)); } - stage_x11 = CLUTTER_STAGE_X11 (_clutter_stage_get_window (stage)); - event->any.stage = stage; switch (xi_event->evtype) diff --git a/tests/interactive/test-devices.c b/tests/interactive/test-devices.c index f38e3285e..e68a26d11 100644 --- a/tests/interactive/test-devices.c +++ b/tests/interactive/test-devices.c @@ -4,9 +4,9 @@ #include typedef struct { + ClutterActor *stage; GHashTable *devices; - } TestDevicesApp; static const gchar * @@ -77,11 +77,10 @@ axis_type_name (ClutterInputAxis axis) } static gboolean -stage_button_event_cb (ClutterActor *actor, - ClutterEvent *event, - gpointer userdata) +stage_button_event_cb (ClutterActor *actor, + ClutterEvent *event, + TestDevicesApp *app) { - TestDevicesApp *app = (TestDevicesApp *)userdata; ClutterInputDevice *device; ClutterInputDevice *source_device; ClutterActor *hand = NULL; @@ -128,11 +127,10 @@ stage_button_event_cb (ClutterActor *actor, } static gboolean -stage_motion_event_cb (ClutterActor *actor, - ClutterEvent *event, - gpointer userdata) +stage_motion_event_cb (ClutterActor *actor, + ClutterEvent *event, + TestDevicesApp *app) { - TestDevicesApp *app = (TestDevicesApp *)userdata; ClutterInputDevice *device; ClutterActor *hand = NULL; @@ -152,6 +150,65 @@ stage_motion_event_cb (ClutterActor *actor, return FALSE; } +static void +manager_device_added_cb (ClutterDeviceManager *manager, + ClutterInputDevice *device, + TestDevicesApp *app) +{ + ClutterInputDeviceType device_type; + ClutterActor *hand = NULL; + + g_print ("got a %s device '%s' with id %d\n", + device_type_name (device), + clutter_input_device_get_device_name (device), + clutter_input_device_get_device_id (device)); + + device_type = clutter_input_device_get_device_type (device); + if (device_type == CLUTTER_POINTER_DEVICE || + device_type == CLUTTER_PEN_DEVICE || + device_type == CLUTTER_POINTER_DEVICE) + { + g_print ("*** enabling device '%s' ***\n", + clutter_input_device_get_device_name (device)); + + clutter_input_device_set_enabled (device, TRUE); + + hand = clutter_texture_new_from_file (TESTS_DATADIR + G_DIR_SEPARATOR_S + "redhand.png", + NULL); + g_hash_table_insert (app->devices, device, hand); + + clutter_container_add_actor (CLUTTER_CONTAINER (app->stage), hand); + } +} + +static void +manager_device_removed_cb (ClutterDeviceManager *manager, + ClutterInputDevice *device, + TestDevicesApp *app) +{ + ClutterInputDeviceType device_type; + ClutterActor *hand = NULL; + + g_print ("removed a %s device '%s' with id %d\n", + device_type_name (device), + clutter_input_device_get_device_name (device), + clutter_input_device_get_device_id (device)); + + device_type = clutter_input_device_get_device_type (device); + if (device_type == CLUTTER_POINTER_DEVICE || + device_type == CLUTTER_PEN_DEVICE || + device_type == CLUTTER_POINTER_DEVICE) + { + hand = g_hash_table_lookup (app->devices, device); + if (hand != NULL) + clutter_container_add_actor (CLUTTER_CONTAINER (app->stage), hand); + + g_hash_table_remove (app->devices, device); + } +} + G_MODULE_EXPORT int test_devices_main (int argc, char **argv) { @@ -181,10 +238,18 @@ test_devices_main (int argc, char **argv) g_signal_connect (stage, "button-press-event", G_CALLBACK (stage_button_event_cb), app); + app->stage = stage; clutter_actor_show_all (stage); manager = clutter_device_manager_get_default (); + g_signal_connect (manager, + "device-added", G_CALLBACK (manager_device_added_cb), + app); + g_signal_connect (manager, + "device-removed", G_CALLBACK (manager_device_removed_cb), + app); + stage_devices = clutter_device_manager_peek_devices (manager); if (stage_devices == NULL) From 7a339d1663b94e4cbc07ffa8a8c3bf0a47d84cae Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 21 Jan 2011 14:12:19 +0000 Subject: [PATCH 38/41] device: unset the axes array pointer when resetting Otherwise we'll get a nice realloc() error from glibc. --- clutter/clutter-input-device.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/clutter/clutter-input-device.c b/clutter/clutter-input-device.c index 062e25b02..cc804bda5 100644 --- a/clutter/clutter-input-device.c +++ b/clutter/clutter-input-device.c @@ -975,6 +975,7 @@ _clutter_input_device_reset_axes (ClutterInputDevice *device) if (device->axes != NULL) { g_array_free (device->axes, TRUE); + device->axes = NULL; g_object_notify_by_pspec (G_OBJECT (device), obj_props[PROP_N_AXES]); } @@ -1025,6 +1026,7 @@ _clutter_input_device_add_axis (ClutterInputDevice *device, default: info.min_axis = 0; info.max_axis = 1; + break; } device->axes = g_array_append_val (device->axes, info); From 516366d7691128e9f2138781bcc144cd628b110f Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 21 Jan 2011 15:14:11 +0000 Subject: [PATCH 39/41] event: Clean up clutter_event_copy() Try to avoid duplication of variables and blocks. --- clutter/clutter-event.c | 46 +++++++++++++++++++---------------------- 1 file changed, 21 insertions(+), 25 deletions(-) diff --git a/clutter/clutter-event.c b/clutter/clutter-event.c index 47264a773..c569e07d0 100644 --- a/clutter/clutter-event.c +++ b/clutter/clutter-event.c @@ -677,6 +677,8 @@ clutter_event_copy (const ClutterEvent *event) { ClutterEvent *new_event; ClutterEventPrivate *new_real_event; + ClutterInputDevice *device; + gint n_axes = 0; g_return_val_if_fail (event != NULL, NULL); @@ -692,40 +694,29 @@ clutter_event_copy (const ClutterEvent *event) new_real_event->source_device = real_event->source_device; } + device = clutter_event_get_device (event); + if (device != NULL) + n_axes = clutter_input_device_get_n_axes (device); + switch (event->type) { case CLUTTER_BUTTON_PRESS: case CLUTTER_BUTTON_RELEASE: - if (event->button.device != NULL && event->button.axes != NULL) - { - gint n_axes; - - n_axes = clutter_input_device_get_n_axes (event->button.device); - new_event->button.axes = g_memdup (event->button.axes, - sizeof (gdouble) * n_axes); - } + if (event->button.axes != NULL) + new_event->button.axes = g_memdup (event->button.axes, + sizeof (gdouble) * n_axes); break; case CLUTTER_SCROLL: - if (event->scroll.device != NULL && event->scroll.axes != NULL) - { - gint n_axes; - - n_axes = clutter_input_device_get_n_axes (event->scroll.device); - new_event->scroll.axes = g_memdup (event->scroll.axes, - sizeof (gdouble) * n_axes); - } + if (event->scroll.axes != NULL) + new_event->scroll.axes = g_memdup (event->scroll.axes, + sizeof (gdouble) * n_axes); break; case CLUTTER_MOTION: - if (event->motion.device != NULL && event->motion.axes != NULL) - { - gint n_axes; - - n_axes = clutter_input_device_get_n_axes (event->motion.device); - new_event->motion.axes = g_memdup (event->motion.axes, - sizeof (gdouble) * n_axes); - } + if (event->motion.axes != NULL) + new_event->motion.axes = g_memdup (event->motion.axes, + sizeof (gdouble) * n_axes); break; default: @@ -1051,11 +1042,14 @@ clutter_event_get_axes (const ClutterEvent *event, case CLUTTER_DELETE: case CLUTTER_ENTER: case CLUTTER_LEAVE: - case CLUTTER_SCROLL: case CLUTTER_KEY_PRESS: case CLUTTER_KEY_RELEASE: break; + case CLUTTER_SCROLL: + retval = event->scroll.axes; + break; + case CLUTTER_BUTTON_PRESS: case CLUTTER_BUTTON_RELEASE: retval = event->button.axes; @@ -1073,6 +1067,8 @@ clutter_event_get_axes (const ClutterEvent *event, device = clutter_event_get_device (event); if (device != NULL) len = clutter_input_device_get_n_axes (device); + else + retval = NULL; } if (n_axes) From e46571d63911df23cb25fb0b3fcfe5e86f50d81b Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 21 Jan 2011 15:26:52 +0000 Subject: [PATCH 40/41] device-manager/xi2: Sync the stage of source devices Keep the slave devices in sync with their master, so that we don't ignore their events because they lack the stage pointer. --- clutter/x11/clutter-device-manager-xi2.c | 51 ++++++++++++++++-------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/clutter/x11/clutter-device-manager-xi2.c b/clutter/x11/clutter-device-manager-xi2.c index bb438d56b..ddff130e3 100644 --- a/clutter/x11/clutter-device-manager-xi2.c +++ b/clutter/x11/clutter-device-manager-xi2.c @@ -105,6 +105,14 @@ translate_valuator_class (Display *xdisplay, class->min, class->max, class->resolution); + + CLUTTER_NOTE (BACKEND, + "Added axis '%s' (min:%.2f, max:%.2fd, res:%d) of device %d", + clutter_input_axis_atom_names[axis], + class->min, + class->max, + class->resolution, + device->id); } static void @@ -541,7 +549,7 @@ clutter_device_manager_xi2_translate_event (ClutterEventTranslator *translator, ClutterBackendX11 *backend_x11; ClutterStageX11 *stage_x11 = NULL; ClutterStage *stage = NULL; - ClutterInputDevice *device; + ClutterInputDevice *device, *source_device; XGenericEventCookie *cookie; XIEvent *xi_event; XEvent *xevent; @@ -645,9 +653,9 @@ clutter_device_manager_xi2_translate_event (ClutterEventTranslator *translator, event_x11->caps_lock_set = _clutter_keymap_x11_get_caps_lock_state (backend_x11->keymap); - device = g_hash_table_lookup (manager_xi2->devices_by_id, - GINT_TO_POINTER (xev->sourceid)); - _clutter_event_set_source_device (event, device); + source_device = g_hash_table_lookup (manager_xi2->devices_by_id, + GINT_TO_POINTER (xev->sourceid)); + _clutter_event_set_source_device (event, source_device); device = g_hash_table_lookup (manager_xi2->devices_by_id, GINT_TO_POINTER (xev->deviceid)); @@ -716,9 +724,10 @@ clutter_device_manager_xi2_translate_event (ClutterEventTranslator *translator, event->scroll.modifier_state = _clutter_input_device_xi2_translate_state (&xev->mods, &xev->buttons); - device = g_hash_table_lookup (manager_xi2->devices_by_id, - GINT_TO_POINTER (xev->sourceid)); - _clutter_event_set_source_device (event, device); + + source_device = g_hash_table_lookup (manager_xi2->devices_by_id, + GINT_TO_POINTER (xev->sourceid)); + _clutter_event_set_source_device (event, source_device); device = g_hash_table_lookup (manager_xi2->devices_by_id, GINT_TO_POINTER (xev->deviceid)); @@ -747,9 +756,9 @@ clutter_device_manager_xi2_translate_event (ClutterEventTranslator *translator, _clutter_input_device_xi2_translate_state (&xev->mods, &xev->buttons); - device = g_hash_table_lookup (manager_xi2->devices_by_id, - GINT_TO_POINTER (xev->sourceid)); - _clutter_event_set_source_device (event, device); + source_device = g_hash_table_lookup (manager_xi2->devices_by_id, + GINT_TO_POINTER (xev->sourceid)); + _clutter_event_set_source_device (event, source_device); device = g_hash_table_lookup (manager_xi2->devices_by_id, GINT_TO_POINTER (xev->deviceid)); @@ -763,8 +772,11 @@ clutter_device_manager_xi2_translate_event (ClutterEventTranslator *translator, break; } + if (source_device != NULL && device->stage != NULL) + _clutter_input_device_set_stage (source_device, device->stage); + CLUTTER_NOTE (EVENT, - "%s: win:0x%x, device:%s (button:%d, x:%.2f, y:%.2f)", + "%s: win:0x%x, device:%s (button:%d, x:%.2f, y:%.2f, axes:%s)", event->any.type == CLUTTER_BUTTON_PRESS ? "button press " : "button release", @@ -772,7 +784,8 @@ clutter_device_manager_xi2_translate_event (ClutterEventTranslator *translator, event->button.device->device_name, event->button.button, event->button.x, - event->button.y); + event->button.y, + event->button.axes != NULL ? "yes" : "no"); if (xi_event->evtype == XI_ButtonPress) _clutter_stage_x11_set_user_time (stage_x11, event->button.time); @@ -796,9 +809,9 @@ clutter_device_manager_xi2_translate_event (ClutterEventTranslator *translator, _clutter_input_device_xi2_translate_state (&xev->mods, &xev->buttons); - device = g_hash_table_lookup (manager_xi2->devices_by_id, - GINT_TO_POINTER (xev->sourceid)); - _clutter_event_set_source_device (event, device); + source_device = g_hash_table_lookup (manager_xi2->devices_by_id, + GINT_TO_POINTER (xev->sourceid)); + _clutter_event_set_source_device (event, source_device); device = g_hash_table_lookup (manager_xi2->devices_by_id, GINT_TO_POINTER (xev->deviceid)); @@ -810,11 +823,15 @@ clutter_device_manager_xi2_translate_event (ClutterEventTranslator *translator, stage_x11, &xev->valuators); - CLUTTER_NOTE (EVENT, "motion: win:0x%x device:%s (x:%.2f, y:%.2f)", + if (source_device != NULL && device->stage != NULL) + _clutter_input_device_set_stage (source_device, device->stage); + + CLUTTER_NOTE (EVENT, "motion: win:0x%x device:%s (x:%.2f, y:%.2f, axes:%s)", (unsigned int) stage_x11->xwin, event->motion.device->device_name, event->motion.x, - event->motion.y); + event->motion.y, + event->motion.axes != NULL ? "yes" : "no"); retval = CLUTTER_TRANSLATE_QUEUE; } From b7d26b2d02729b2d5eeb1650ca4e89a723afc1c7 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 21 Jan 2011 15:27:56 +0000 Subject: [PATCH 41/41] test-devices: Actually print the axis data Returning in an if() block whose condition we assume to be true doesn't play nicely with having statements after the block. Let's fix that. --- tests/interactive/test-devices.c | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/tests/interactive/test-devices.c b/tests/interactive/test-devices.c index e68a26d11..fe060db66 100644 --- a/tests/interactive/test-devices.c +++ b/tests/interactive/test-devices.c @@ -84,19 +84,22 @@ stage_button_event_cb (ClutterActor *actor, ClutterInputDevice *device; ClutterInputDevice *source_device; ClutterActor *hand = NULL; + gdouble *axes; + guint n_axes, i; device = clutter_event_get_device (event); source_device = clutter_event_get_source_device (event); hand = g_hash_table_lookup (app->devices, device); - g_print ("Device: '%s' (id:%d, type: %s, source: '%s')\n", + g_print ("Device: '%s' (id:%d, type: %s, source: '%s', axes: %d)\n", clutter_input_device_get_device_name (device), clutter_input_device_get_device_id (device), device_type_name (device), source_device != device ? clutter_input_device_get_device_name (source_device) - : ""); + : "", + clutter_input_device_get_n_axes (device)); if (hand != NULL) { @@ -104,23 +107,21 @@ stage_button_event_cb (ClutterActor *actor, clutter_event_get_coords (event, &event_x, &event_y); clutter_actor_set_position (hand, event_x, event_y); - - return TRUE; } - if (event->motion.axes != NULL) + axes = clutter_event_get_axes (event, &n_axes); + for (i = 0; i < n_axes; i++) { - gdouble *axes; - guint n_axes, i; + ClutterInputAxis axis; - axes = clutter_event_get_axes (event, &n_axes); - for (i = 0; i < n_axes; i++) - { - g_print ("Axis[%02d][%s].value: %.2f\n", - i, - axis_type_name (clutter_input_device_get_axis (device, i)), - axes[i]); - } + axis = clutter_input_device_get_axis (device, i); + if (axis == CLUTTER_INPUT_AXIS_IGNORE) + continue; + + g_print ("\tAxis[%2d][%s].value: %.2f\n", + i, + axis_type_name (axis), + axes[i]); } return FALSE;