$(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 \
# 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 \
# 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 \
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 \
$(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)
# 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)
glx_source_h_priv = \
$(srcdir)/glx/clutter-backend-glx.h \
- $(srcdir)/glx/clutter-event-glx.h \
$(srcdir)/glx/clutter-stage-glx.h \
$(NULL)
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);
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__ */
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);
+}
#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;
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;
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
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
_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;
+}
#define __CLUTTER_DEVICE_MANAGER_H__
#include <clutter/clutter-input-device.h>
+#include <clutter/clutter-stage.h>
G_BEGIN_DECLS
--- /dev/null
+#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);
+}
--- /dev/null
+#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__ */
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,
{
_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);
}
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
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);
}
/**
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
#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"
{
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,
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:
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;
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:
*
* 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:
*
* 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:
*
* 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
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;
}
/*
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;
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))
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);
+}
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:
*
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
VOID:OBJECT
VOID:OBJECT,FLOAT,FLOAT
VOID:OBJECT,FLOAT,FLOAT,FLAGS
+VOID:OBJECT,INT
VOID:OBJECT,PARAM
VOID:OBJECT,POINTER
VOID:OBJECT,UINT
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,
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__ */
#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"
/* 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,
GError **error)
{
ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (backend);
+ ClutterEventTranslator *translator;
ClutterStageWindow *stage_window;
ClutterStageX11 *stage_x11;
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,
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
+++ /dev/null
-/* 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
-}
-
+++ /dev/null
-/* 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__ */
-
#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"
#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;
_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)
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 ();
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
ClutterBackendX11 *backend_x11;
ClutterBackendGLX *backend_glx;
ClutterBackend *backend;
- int event_flags;
CLUTTER_NOTE (ACTOR, "Realizing stage '%s' [%p]",
G_OBJECT_TYPE_NAME (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)
{
}
}
- /* 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
}
#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);
}
/* 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)
void
_clutter_stage_glx_redraw (ClutterStageGLX *stage_glx,
- ClutterStage *stage)
+ ClutterStage *stage)
{
ClutterBackend *backend;
ClutterBackendX11 *backend_x11;
#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"
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,
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)
env_string = NULL;
}
+ env_string = g_getenv ("CLUTTER_ENABLE_XINPUT");
+ if (env_string)
+ {
+ clutter_enable_xinput = TRUE;
+ env_string = NULL;
+ }
+
return TRUE;
}
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
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);
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 =
NULL,
backend_x11);
+ /* add event filter for XSETTINGS events */
clutter_x11_add_filter (xsettings_filter, backend_x11);
if (clutter_synchronise)
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,
_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
{
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;
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
* 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;
}
/**
}
}
-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)
{
* 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
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;
+}
#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
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;
int xkb_event_base;
gboolean use_xkb;
gboolean have_xkb_autorepeat;
+
+ GList *event_translators;
};
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);
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__ */
--- /dev/null
+/*
+ * 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);
+}
{
ClutterDeviceManager parent_instance;
+ GHashTable *devices_by_id;
+
/* the list of transient devices */
GSList *devices;
ClutterInputDevice *core_pointer;
ClutterInputDevice *core_keyboard;
- guint use_xinput_1 : 1;
+ int xi_event_base;
};
struct _ClutterDeviceManagerX11Class
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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__ */
#include <X11/XKBlib.h>
#endif
+#if 0
/* XEMBED protocol support for toolkit embedding */
#define XEMBED_MAPPED (1 << 0)
#define MAX_SUPPORTED_XEMBED_VERSION 1
#define XEMBED_ACTIVATE_ACCELERATOR 14
static Window ParentEmbedderWin = None;
+#endif
typedef struct _ClutterEventSource 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)
{
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;
return XPending (CLUTTER_BACKEND_X11 (backend)->xdpy);
}
+#if 0
static gboolean
xembed_send_message (ClutterBackendX11 *backend_x11,
Window window,
backend_x11->atom_XEMBED_INFO, 32,
PropModeReplace, (unsigned char *) list, 2);
}
+#endif
void
_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);
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;
}
}
+#if 0
static void
update_last_event_time (ClutterBackendX11 *backend_x11,
XEvent *xevent)
}
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;
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);
}
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
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
{
--spin;
}
- out:
+out:
clutter_threads_leave ();
return result;
/* 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);
--- /dev/null
+#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;
+}
--- /dev/null
+#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__ */
+++ /dev/null
-#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 */
-}
+++ /dev/null
-#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__ */
--- /dev/null
+#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;
+}
--- /dev/null
+#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__ */
#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 */
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;
}
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);
}
iface->realize = clutter_stage_x11_realize;
}
+static inline void
+set_user_time (ClutterBackendX11 *backend_x11,
+ ClutterStageX11 *stage_x11,
+ long timestamp)
+{
+ if (timestamp != CLUTTER_CURRENT_TIME)
+ {
+ XChangeProperty (backend_x11->xdpy,
+ stage_x11->xwin,
+ backend_x11->atom_NET_WM_USER_TIME,
+ XA_CARDINAL, 32,
+ PropModeReplace,
+ (unsigned char *) ×tamp, 1);
+ }
+}
+
+static gboolean
+handle_wm_protocols_event (ClutterBackendX11 *backend_x11,
+ ClutterStageX11 *stage_x11,
+ XEvent *xevent)
+{
+ Atom atom = (Atom) xevent->xclient.data.l[0];
+
+ if (atom == backend_x11->atom_WM_DELETE_WINDOW &&
+ xevent->xany.window == stage_x11->xwin)
+ {
+ /* the WM_DELETE_WINDOW is a request: we do not destroy
+ * the window right away, as it might contain vital data;
+ * we relay the event to the application and we let it
+ * handle the request
+ */
+ CLUTTER_NOTE (EVENT, "Delete stage %s[%p], win:0x%x",
+ _clutter_actor_get_debug_name (CLUTTER_ACTOR (stage_x11->wrapper)),
+ stage_x11->wrapper,
+ (unsigned int) stage_x11->xwin);
+
+ set_user_time (backend_x11, stage_x11, xevent->xclient.data.l[1]);
+
+ return TRUE;
+ }
+ else if (atom == backend_x11->atom_NET_WM_PING &&
+ xevent->xany.window == stage_x11->xwin)
+ {
+ XClientMessageEvent xclient = xevent->xclient;
+
+ xclient.window = backend_x11->xwin_root;
+ XSendEvent (backend_x11->xdpy, xclient.window,
+ False,
+ SubstructureRedirectMask | SubstructureNotifyMask,
+ (XEvent *) &xclient);
+ return FALSE;
+ }
+
+ /* do not send any of the WM_PROTOCOLS events to the queue */
+ return FALSE;
+}
+
+static gboolean
+clipped_redraws_cool_off_cb (void *data)
+{
+ ClutterStageX11 *stage_x11 = data;
+
+ stage_x11->clipped_redraws_cool_off = 0;
+
+ return FALSE;
+}
+
+static ClutterTranslateReturn
+clutter_stage_x11_translate_event (ClutterEventTranslator *translator,
+ gpointer native,
+ ClutterEvent *event)
+{
+ ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (translator);
+ ClutterTranslateReturn res = CLUTTER_TRANSLATE_CONTINUE;
+ XEvent *xevent = native;
+ ClutterBackendX11 *backend_x11;
+ ClutterStage *stage;
+ Window stage_xwindow = stage_x11->xwin;
+
+ stage = clutter_x11_get_stage_from_window (xevent->xany.window);
+ if (stage == NULL)
+ return CLUTTER_TRANSLATE_CONTINUE;
+
+ backend_x11 = CLUTTER_BACKEND_X11 (clutter_get_default_backend ());
+
+ switch (xevent->type)
+ {
+ case ConfigureNotify:
+ if (!stage_x11->is_foreign_xwin)
+ {
+ CLUTTER_NOTE (BACKEND, "%s: ConfigureNotify[%x] (%d, %d)",
+ G_STRLOC,
+ (unsigned int) stage_x11->xwin,
+ xevent->xconfigure.width,
+ xevent->xconfigure.height);
+
+ /* Queue a relayout - we want glViewport to be called
+ * with the correct values, and this is done in ClutterStage
+ * via _cogl_onscreen_clutter_backend_set_size ().
+ *
+ * We queue a relayout, because if this ConfigureNotify is
+ * in response to a size we set in the application, the
+ * set_size() call below is essentially a null-op.
+ *
+ * Make sure we do this only when the size has changed,
+ * otherwise we end up relayouting on window moves.
+ */
+ if ((stage_x11->state & CLUTTER_STAGE_STATE_FULLSCREEN) ||
+ (stage_x11->xwin_width != xevent->xconfigure.width) ||
+ (stage_x11->xwin_height != xevent->xconfigure.height))
+ {
+ clutter_actor_queue_relayout (CLUTTER_ACTOR (stage));
+ }
+
+ /* If we're fullscreened, we want these variables to
+ * represent the size of the window before it was set
+ * to fullscreen.
+ */
+ if (!(stage_x11->state & CLUTTER_STAGE_STATE_FULLSCREEN))
+ {
+ stage_x11->xwin_width = xevent->xconfigure.width;
+ stage_x11->xwin_height = xevent->xconfigure.height;
+ }
+
+ clutter_actor_set_size (CLUTTER_ACTOR (stage),
+ xevent->xconfigure.width,
+ xevent->xconfigure.height);
+
+ /* XXX: This is a workaround for a race condition when
+ * resizing windows while there are in-flight
+ * glXCopySubBuffer blits happening.
+ *
+ * The problem stems from the fact that rectangles for the
+ * blits are described relative to the bottom left of the
+ * window and because we can't guarantee control over the X
+ * window gravity used when resizing so the gravity is
+ * typically NorthWest not SouthWest.
+ *
+ * This means if you grow a window vertically the server
+ * will make sure to place the old contents of the window
+ * at the top-left/north-west of your new larger window, but
+ * that may happen asynchronous to GLX preparing to do a
+ * blit specified relative to the bottom-left/south-west of
+ * the window (based on the old smaller window geometry).
+ *
+ * When the GLX issued blit finally happens relative to the
+ * new bottom of your window, the destination will have
+ * shifted relative to the top-left where all the pixels you
+ * care about are so it will result in a nasty artefact
+ * making resizing look very ugly!
+ *
+ * We can't currently fix this completely, in-part because
+ * the window manager tends to trample any gravity we might
+ * set. This workaround instead simply disables blits for a
+ * while if we are notified of any resizes happening so if
+ * the user is resizing a window via the window manager then
+ * they may see an artefact for one frame but then we will
+ * fallback to redrawing the full stage until the cooling
+ * off period is over.
+ */
+ if (stage_x11->clipped_redraws_cool_off)
+ g_source_remove (stage_x11->clipped_redraws_cool_off);
+
+ stage_x11->clipped_redraws_cool_off =
+ g_timeout_add_seconds (1, clipped_redraws_cool_off_cb, stage_x11);
+
+ CLUTTER_UNSET_PRIVATE_FLAGS (stage_x11->wrapper, CLUTTER_IN_RESIZE);
+
+ /* the resize process is complete, so we can ask the stage
+ * to set up the GL viewport with the new size
+ */
+ clutter_stage_ensure_viewport (stage);
+ }
+ break;
+
+ case PropertyNotify:
+ if (xevent->xproperty.atom == backend_x11->atom_NET_WM_STATE &&
+ xevent->xproperty.window == stage_xwindow &&
+ !stage_x11->is_foreign_xwin)
+ {
+ Atom type;
+ gint format;
+ gulong n_items, bytes_after;
+ guchar *data = NULL;
+ gboolean fullscreen_set = FALSE;
+
+ clutter_x11_trap_x_errors ();
+ XGetWindowProperty (backend_x11->xdpy, stage_xwindow,
+ backend_x11->atom_NET_WM_STATE,
+ 0, G_MAXLONG,
+ False, XA_ATOM,
+ &type, &format, &n_items,
+ &bytes_after, &data);
+ clutter_x11_untrap_x_errors ();
+
+ if (type != None && data != NULL)
+ {
+ Atom *atoms = (Atom *) data;
+ gulong i;
+ gboolean is_fullscreen = FALSE;
+
+ for (i = 0; i < n_items; i++)
+ {
+ if (atoms[i] == backend_x11->atom_NET_WM_STATE_FULLSCREEN)
+ fullscreen_set = TRUE;
+ }
+
+ is_fullscreen =
+ (stage_x11->state & CLUTTER_STAGE_STATE_FULLSCREEN);
+
+ if (fullscreen_set != is_fullscreen)
+ {
+ if (fullscreen_set)
+ stage_x11->state |= CLUTTER_STAGE_STATE_FULLSCREEN;
+ else
+ stage_x11->state &= ~CLUTTER_STAGE_STATE_FULLSCREEN;
+
+ stage_x11->fullscreening = fullscreen_set;
+
+ event->any.type = CLUTTER_STAGE_STATE;
+ event->any.source = CLUTTER_ACTOR (stage);
+ event->any.stage = stage;
+ event->stage_state.changed_mask =
+ CLUTTER_STAGE_STATE_FULLSCREEN;
+ event->stage_state.new_state = stage_x11->state;
+
+ res = CLUTTER_TRANSLATE_QUEUE;
+ }
+
+ XFree (data);
+ }
+ }
+ break;
+
+ case FocusIn:
+ if (!(stage_x11->state & CLUTTER_STAGE_STATE_ACTIVATED))
+ {
+ /* TODO: check the detail? */
+ stage_x11->state |= CLUTTER_STAGE_STATE_ACTIVATED;
+
+ event->type = CLUTTER_STAGE_STATE;
+ event->any.source = CLUTTER_ACTOR (stage);
+ event->any.stage = stage;
+ event->stage_state.changed_mask = CLUTTER_STAGE_STATE_ACTIVATED;
+ event->stage_state.new_state = stage_x11->state;
+
+ res = CLUTTER_TRANSLATE_QUEUE;
+ }
+ break;
+
+ case FocusOut:
+ if (stage_x11->state & CLUTTER_STAGE_STATE_ACTIVATED)
+ {
+ /* TODO: check the detail? */
+ stage_x11->state &= ~CLUTTER_STAGE_STATE_ACTIVATED;
+
+ event->any.type = CLUTTER_STAGE_STATE;
+ event->any.source = CLUTTER_ACTOR (stage);
+ event->any.stage = stage;
+ event->stage_state.changed_mask = CLUTTER_STAGE_STATE_ACTIVATED;
+ event->stage_state.new_state = stage_x11->state;
+
+ res = CLUTTER_TRANSLATE_QUEUE;
+ }
+ break;
+
+ case Expose:
+ {
+ XExposeEvent *expose = (XExposeEvent *) xevent;
+ ClutterPaintVolume clip;
+ ClutterVertex origin;
+
+ CLUTTER_NOTE (EVENT,
+ "expose for stage: %s[%p], win:0x%x - "
+ "redrawing area (x: %d, y: %d, width: %d, height: %d)",
+ _clutter_actor_get_debug_name (CLUTTER_ACTOR (stage)),
+ stage,
+ (unsigned int) stage_xwindow,
+ expose->x,
+ expose->y,
+ expose->width,
+ expose->height);
+
+ origin.x = expose->x;
+ origin.y = expose->y;
+ origin.z = 0;
+
+ _clutter_paint_volume_init_static (CLUTTER_ACTOR (stage), &clip);
+
+ clutter_paint_volume_set_origin (&clip, &origin);
+ clutter_paint_volume_set_width (&clip, expose->width);
+ clutter_paint_volume_set_height (&clip, expose->height);
+
+ _clutter_actor_queue_redraw_with_clip (CLUTTER_ACTOR (stage), 0, &clip);
+
+ clutter_paint_volume_free (&clip);
+ }
+ break;
+
+ case DestroyNotify:
+ CLUTTER_NOTE (EVENT,
+ "Destroy notification received for stage %s[%p], win:0x%x",
+ _clutter_actor_get_debug_name (CLUTTER_ACTOR (stage)),
+ stage,
+ (unsigned int) stage_xwindow);
+ event->any.type = CLUTTER_DESTROY_NOTIFY;
+ event->any.stage = stage;
+ res = CLUTTER_TRANSLATE_QUEUE;
+ break;
+
+ case ClientMessage:
+ CLUTTER_NOTE (EVENT, "Client message for stage %s[%p], win:0x%x",
+ _clutter_actor_get_debug_name (CLUTTER_ACTOR (stage)),
+ stage,
+ (unsigned int) stage_xwindow);
+ if (handle_wm_protocols_event (backend_x11, stage_x11, xevent))
+ {
+ event->any.type = CLUTTER_DELETE;
+ event->any.stage = stage;
+ res = CLUTTER_TRANSLATE_QUEUE;
+ }
+ break;
+
+ default:
+ res = CLUTTER_TRANSLATE_CONTINUE;
+ break;
+ }
+
+ return res;
+}
+
+static void
+clutter_event_translator_iface_init (ClutterEventTranslatorIface *iface)
+{
+ iface->translate_event = clutter_stage_x11_translate_event;
+}
+
/**
* clutter_x11_get_stage_window:
* @stage: a #ClutterStage
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);
+}
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__ */
[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
]
)
+AM_CONDITIONAL([BUILD_XI2], [test "x$have_xinput2" = "xyes"])
AM_CONDITIONAL(X11_TESTS, [test "x$x11_tests" = "xyes"])
dnl === Enable debug level ====================================================
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
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;
}