event/x11: Rework the way we translate X11 events
authorEmmanuele Bassi <ebassi@linux.intel.com>
Tue, 4 Jan 2011 12:32:04 +0000 (12:32 +0000)
committerEmmanuele Bassi <ebassi@linux.intel.com>
Fri, 21 Jan 2011 10:25:43 +0000 (10:25 +0000)
This is a lump commit that is fairly difficult to break down without
either breaking bisecting or breaking the test cases.

The new design for handling X11 event translation works this way:

  - ClutterBackend::translate_event() has been added as the central
    point used by a ClutterBackend implementation to translate a
    native event into a ClutterEvent;

  - ClutterEventTranslator is a private interface that should be
    implemented by backend-specific objects, like stage
    implementations and ClutterDeviceManager sub-classes, and
    allows dealing with class-specific event translation;

  - ClutterStageX11 implements EventTranslator, and deals with the
    stage-relative X11 events coming from the X11 event source;

  - ClutterStageGLX overrides EventTranslator, in order to
    deal with the INTEL_GLX_swap_event extension, and it chains up
    to the X11 default implementation;

  - ClutterDeviceManagerX11 has been split into two separate classes,
    one that deals with core and (optionally) XI1 events, and the
    other that deals with XI2 events; the selection is done at run-time,
    since the core+XI1 and XI2 mechanisms are mutually exclusive.

All the other backends we officially support still use their own
custom event source and translation function, but the end goal is to
migrate them to the translate_event() virtual function, and have the
event source be a shared part of Clutter core.

37 files changed:
clutter/Makefile.am
clutter/clutter-backend-private.h
clutter/clutter-backend.c
clutter/clutter-device-manager-private.h
clutter/clutter-device-manager.c
clutter/clutter-device-manager.h
clutter/clutter-event-translator.c [new file with mode: 0644]
clutter/clutter-event-translator.h [new file with mode: 0644]
clutter/clutter-event.c
clutter/clutter-event.h
clutter/clutter-input-device.c
clutter/clutter-input-device.h
clutter/clutter-marshal.list
clutter/clutter-private.h
clutter/clutter-types.h
clutter/glx/clutter-backend-glx.c
clutter/glx/clutter-event-glx.c [deleted file]
clutter/glx/clutter-event-glx.h [deleted file]
clutter/glx/clutter-stage-glx.c
clutter/x11/clutter-backend-x11.c
clutter/x11/clutter-backend-x11.h
clutter/x11/clutter-device-manager-core-x11.c [new file with mode: 0644]
clutter/x11/clutter-device-manager-core-x11.h [moved from clutter/x11/clutter-device-manager-x11.h with 97% similarity]
clutter/x11/clutter-device-manager-x11.c [deleted file]
clutter/x11/clutter-device-manager-xi2.c [new file with mode: 0644]
clutter/x11/clutter-device-manager-xi2.h [new file with mode: 0644]
clutter/x11/clutter-event-x11.c
clutter/x11/clutter-input-device-core-x11.c [new file with mode: 0644]
clutter/x11/clutter-input-device-core-x11.h [new file with mode: 0644]
clutter/x11/clutter-input-device-x11.c [deleted file]
clutter/x11/clutter-input-device-x11.h [deleted file]
clutter/x11/clutter-input-device-xi2.c [new file with mode: 0644]
clutter/x11/clutter-input-device-xi2.h [new file with mode: 0644]
clutter/x11/clutter-stage-x11.c
clutter/x11/clutter-stage-x11.h
configure.ac
tests/interactive/test-devices.c

index e75ef70..a3a61e4 100644 (file)
@@ -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)
 
index fb63301..8d95acf 100644 (file)
@@ -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__ */
index ff4c973..014f239 100644 (file)
@@ -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);
+}
index ea21926..f3764d7 100644 (file)
 #ifndef __CLUTTER_DEVICE_MANAGER_PRIVATE_H__
 #define __CLUTTER_DEVICE_MANAGER_PRIVATE_H__
 
+#include <clutter/clutter-backend.h>
 #include <clutter/clutter-device-manager.h>
 #include <clutter/clutter-event.h>
 
 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
 
index ccb911d..da67983 100644 (file)
@@ -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;
+}
index f954662..94cde3d 100644 (file)
@@ -29,6 +29,7 @@
 #define __CLUTTER_DEVICE_MANAGER_H__
 
 #include <clutter/clutter-input-device.h>
+#include <clutter/clutter-stage.h>
 
 G_BEGIN_DECLS
 
diff --git a/clutter/clutter-event-translator.c b/clutter/clutter-event-translator.c
new file mode 100644 (file)
index 0000000..b455496
--- /dev/null
@@ -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 (file)
index 0000000..f7facc7
--- /dev/null
@@ -0,0 +1,40 @@
+#ifndef __CLUTTER_EVENT_TRANSLATOR_H__
+#define __CLUTTER_EVENT_TRANSLATOR_H__
+
+#include <glib-object.h>
+#include <clutter/clutter-event.h>
+
+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__ */
index 12e5825..df823dc 100644 (file)
@@ -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);
 }
 
 /**
index adeb040..38bcd97 100644 (file)
 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
  * @CLUTTER_EVENT_FLAG_SYNTHETIC: Synthetic event
index 0d2652f..415efcf 100644 (file)
 #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,18 +49,68 @@ 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,
                                    const GValue *value,
@@ -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);
+}
index a07983e..e04d47d 100644 (file)
@@ -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);
-
-void                   clutter_input_device_update_from_event (ClutterInputDevice *device,
-                                                               ClutterEvent       *event,
-                                                               gboolean            update_stage);
+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);
+
+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
 
index e5d9d5c..49745ef 100644 (file)
@@ -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
index 8cf48c5..79b6dcb 100644 (file)
@@ -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,
index b5951c5..8ad7261 100644 (file)
@@ -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__ */
index d7a6117..6e5258d 100644 (file)
 #include <GL/gl.h>
 
 #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 (file)
index dadb3b1..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-/* Clutter.
- * An OpenGL based 'interactive canvas' library.
- * Authored By Matthew Allum  <mallum@openedhand.com>
- * 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 <http://www.gnu.org/licenses/>.
- *
- *
- */
-
-#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 <clutter/clutter-backend.h>
-#include <clutter/clutter-stage-manager.h>
-
-#include <X11/Xlib.h>
-
-#include <GL/glxext.h>
-
-#include <glib.h>
-
-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 (file)
index 79fa272..0000000
+++ /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 <http://www.gnu.org/licenses/>.
- *
- *
- */
-
-#ifndef __CLUTTER_EVENT_GLX_H__
-#define __CLUTTER_EVENT_GLX_H__
-
-#include <glib.h>
-#include <clutter/clutter-event.h>
-#include <clutter/clutter-backend.h>
-#include <X11/Xlib.h>
-
-G_BEGIN_DECLS
-
-gboolean
-_clutter_backend_glx_handle_event (ClutterBackendX11 *backend,
-                                  XEvent            *xevent);
-
-G_END_DECLS
-
-#endif /* __CLUTTER_EVENT_GLX_H__ */
-
index ae3c8cd..e0954b2 100644 (file)
@@ -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 <drm.h>
 #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;
index 5efb115..428644b 100644 (file)
@@ -38,8 +38,8 @@
 #include <errno.h>
 
 #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"
 #include <X11/extensions/XInput.h>
 #endif
 
+#if HAVE_XINPUT_2
+#include <X11/extensions/XInput2.h>
+#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,28 +573,121 @@ 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)
+static ClutterDeviceManager *
+clutter_backend_x11_get_device_manager (ClutterBackend *backend)
 {
-  return FALSE;
+  ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (backend);
+
+  clutter_backend_x11_create_device_manager (backend_x11);
+
+  return backend_x11->device_manager;
 }
 
-static ClutterDeviceManager *
-clutter_backend_x11_get_device_manager (ClutterBackend *backend)
+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 (G_UNLIKELY (backend_x11->device_manager == NULL))
+  if (backend_x11->event_filters)
     {
-      backend_x11->device_manager =
-        g_object_new (CLUTTER_TYPE_DEVICE_MANAGER_X11,
-                      "use-xinput-1", clutter_enable_xinput,
-                      "backend", backend_x11,
-                      NULL);
+      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;
+        }
     }
 
-  return backend_x11->device_manager;
+  /* 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
@@ -509,7 +695,6 @@ 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;
+}
index 1947803..5d710d7 100644 (file)
 #include <X11/Xatom.h>
 
 #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 (file)
index 0000000..8d005c6
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+ *
+ * Author: Emmanuele Bassi <ebassi@linux.intel.com>
+ */
+
+#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 <X11/extensions/XInput.h>
+
+/* 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);
+}
similarity index 97%
rename from clutter/x11/clutter-device-manager-x11.h
rename to clutter/x11/clutter-device-manager-core-x11.h
index f1fe819..8d65967 100644 (file)
@@ -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 (file)
index 5c5b2b5..0000000
+++ /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 <http://www.gnu.org/licenses/>.
- *
- * Author: Emmanuele Bassi <ebassi@linux.intel.com>
- */
-
-#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 <X11/extensions/XInput.h>
-#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 (file)
index 0000000..7ffe3c3
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+ *
+ * Author: Emmanuele Bassi <ebassi@linux.intel.com>
+ */
+
+#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 <X11/extensions/XInput2.h>
+
+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 (file)
index 0000000..f516fcc
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+ *
+ * Author: Emmanuele Bassi <ebassi@linux.intel.com>
+ */
+
+#ifndef __CLUTTER_DEVICE_MANAGER_XI2_H__
+#define __CLUTTER_DEVICE_MANAGER_XI2_H__
+
+#include <clutter/clutter-device-manager.h>
+
+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__ */
index 77b7f4f..b5bd313 100644 (file)
@@ -63,6 +63,7 @@
 #include <X11/XKBlib.h>
 #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)
@@ -427,51 +427,6 @@ handle_wm_protocols_event (ClutterBackendX11 *backend_x11,
 }
 
 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)
 {
   ClutterStageX11 *stage_x11 = 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 (file)
index 0000000..4c1bf2f
--- /dev/null
@@ -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 <X11/extensions/XInput.h>
+#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 (file)
index 0000000..fc86106
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef __CLUTTER_INPUT_DEVICE_X11_H__
+#define __CLUTTER_INPUT_DEVICE_X11_H__
+
+#include <clutter/clutter-input-device.h>
+#include <X11/Xlib.h>
+
+#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 (file)
index 342950c..0000000
+++ /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 <X11/extensions/XInput.h>
-#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 (file)
index b8d60ac..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-#ifndef __CLUTTER_INPUT_DEVICE_X11_H__
-#define __CLUTTER_INPUT_DEVICE_X11_H__
-
-#include <clutter/clutter-input-device.h>
-#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 (file)
index 0000000..ac90b25
--- /dev/null
@@ -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 <X11/extensions/XInput2.h>
+
+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 (file)
index 0000000..8daf86d
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef __CLUTTER_INPUT_DEVICE_XI2_H__
+#define __CLUTTER_INPUT_DEVICE_XI2_H__
+
+#include <clutter/clutter-input-device.h>
+#include <X11/extensions/XInput2.h>
+
+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__ */
index a3666d6..56240cc 100644 (file)
@@ -23,6 +23,8 @@
 #include "config.h"
 #endif
 
+#include <math.h>
+
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
 #endif
 
 #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"
 
 
 #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 *) &timestamp, 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);
+}
index b62c2d2..b0e1e36 100644 (file)
@@ -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__ */
index 0f1db8d..e7ace50 100644 (file)
@@ -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])
-
-                  X11_LIBS="$X11_LIBS -lXi"
+                  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 $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
index e286a06..8304d6d 100644 (file)
@@ -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;
 }