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; }