Merge branch 'device-manager'

* device-manager: (37 commits)
  x11: Re-enable XI1 extension keyboards
  x11: Always handle core device events before XI events
  docs: Documentation fixes for DeviceManager
  device-manager: Fix the signals definition
  docs: Add sections for InputDevice and DeviceManager
  docs: Add clutter_input_device_get_device_name()
  tests: Print out the device details on motion
  Always register core devices
  device: Remove unused is_default member
  win32: Experimental implementation of device support
  tests: Print the device name, as well as its Id
  x11: Fill out the :name property of the InputDevices
  device: Add the :name property to InputDevice
  x11: Store core devices on the X11 Backend singleton
  device: Unset the cursor actor when leaving the stage
  device: Add pointer actor getter
  x11: Discard the LeaveNotify for off-stage ButtonRelease
  device: Do not overwrite the stage for an InputDevice
  event: Off-stage button releases have a click count of 1
  event: Scroll events do not have click count
  ...
This commit is contained in:
Emmanuele Bassi 2010-02-01 11:26:56 +00:00
commit 5f1c8a17e4
30 changed files with 2206 additions and 907 deletions

View File

@ -82,8 +82,9 @@ source_h = \
$(srcdir)/clutter-child-meta.h \ $(srcdir)/clutter-child-meta.h \
$(srcdir)/clutter-clone.h \ $(srcdir)/clutter-clone.h \
$(srcdir)/clutter-color.h \ $(srcdir)/clutter-color.h \
$(srcdir)/clutter-container.h \ $(srcdir)/clutter-container.h \
$(srcdir)/clutter-deprecated.h \ $(srcdir)/clutter-deprecated.h \
$(srcdir)/clutter-device-manager.h \
$(srcdir)/clutter-event.h \ $(srcdir)/clutter-event.h \
$(srcdir)/clutter-feature.h \ $(srcdir)/clutter-feature.h \
$(srcdir)/clutter-fixed.h \ $(srcdir)/clutter-fixed.h \
@ -91,6 +92,7 @@ source_h = \
$(srcdir)/clutter-flow-layout.h \ $(srcdir)/clutter-flow-layout.h \
$(srcdir)/clutter-frame-source.h \ $(srcdir)/clutter-frame-source.h \
$(srcdir)/clutter-group.h \ $(srcdir)/clutter-group.h \
$(srcdir)/clutter-input-device.h \
$(srcdir)/clutter-interval.h \ $(srcdir)/clutter-interval.h \
$(srcdir)/clutter-keysyms.h \ $(srcdir)/clutter-keysyms.h \
$(srcdir)/clutter-layout-manager.h \ $(srcdir)/clutter-layout-manager.h \
@ -154,6 +156,7 @@ source_c = \
$(srcdir)/clutter-clone.c \ $(srcdir)/clutter-clone.c \
$(srcdir)/clutter-color.c \ $(srcdir)/clutter-color.c \
$(srcdir)/clutter-container.c \ $(srcdir)/clutter-container.c \
$(srcdir)/clutter-device-manager.c \
clutter-enum-types.c \ clutter-enum-types.c \
$(srcdir)/clutter-event.c \ $(srcdir)/clutter-event.c \
$(srcdir)/clutter-feature.c \ $(srcdir)/clutter-feature.c \
@ -162,6 +165,7 @@ source_c = \
$(srcdir)/clutter-flow-layout.c \ $(srcdir)/clutter-flow-layout.c \
$(srcdir)/clutter-frame-source.c \ $(srcdir)/clutter-frame-source.c \
$(srcdir)/clutter-group.c \ $(srcdir)/clutter-group.c \
$(srcdir)/clutter-input-device.c \
$(srcdir)/clutter-interval.c \ $(srcdir)/clutter-interval.c \
$(srcdir)/clutter-layout-manager.c \ $(srcdir)/clutter-layout-manager.c \
$(srcdir)/clutter-layout-meta.c \ $(srcdir)/clutter-layout-meta.c \

View File

@ -308,6 +308,7 @@ struct _ClutterActorPrivate
guint clip_to_allocation : 1; guint clip_to_allocation : 1;
guint enable_model_view_transform : 1; guint enable_model_view_transform : 1;
guint enable_paint_unmapped : 1; guint enable_paint_unmapped : 1;
guint has_pointer : 1;
gfloat clip[4]; gfloat clip[4];
@ -429,7 +430,8 @@ enum
PROP_SHOW_ON_SET_PARENT, PROP_SHOW_ON_SET_PARENT,
PROP_TEXT_DIRECTION PROP_TEXT_DIRECTION,
PROP_HAS_POINTER
}; };
enum enum
@ -3031,6 +3033,10 @@ clutter_actor_get_property (GObject *object,
g_value_set_enum (value, priv->text_direction); g_value_set_enum (value, priv->text_direction);
break; break;
case PROP_HAS_POINTER:
g_value_set_boolean (value, priv->has_pointer);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
@ -3858,6 +3864,24 @@ clutter_actor_class_init (ClutterActorClass *klass)
PROP_TEXT_DIRECTION, PROP_TEXT_DIRECTION,
pspec); pspec);
/**
* ClutterActor:has-pointer:
*
* Whether the actor contains the pointer of a #ClutterInputDevice
* or not.
*
* Since: 1.2
*/
pspec = g_param_spec_boolean ("has-pointer",
"Has Pointer",
"Whether the actor contains the pointer "
"of an input device",
FALSE,
CLUTTER_PARAM_READABLE);
g_object_class_install_property (object_class,
PROP_HAS_POINTER,
pspec);
/** /**
* ClutterActor::destroy: * ClutterActor::destroy:
* @actor: the object which received the signal * @actor: the object which received the signal
@ -9638,6 +9662,20 @@ clutter_actor_set_text_direction (ClutterActor *self,
} }
} }
void
_clutter_actor_set_has_pointer (ClutterActor *self,
gboolean has_pointer)
{
ClutterActorPrivate *priv = self->priv;
if (priv->has_pointer != has_pointer)
{
priv->has_pointer = has_pointer;
g_object_notify (G_OBJECT (self), "has-pointer");
}
}
/** /**
* clutter_actor_get_text_direction: * clutter_actor_get_text_direction:
* @self: a #ClutterActor * @self: a #ClutterActor
@ -9748,3 +9786,23 @@ clutter_actor_pop_internal (void)
ctx->internal_child -= 1; ctx->internal_child -= 1;
} }
/**
* clutter_actor_has_pointer:
* @self: a #ClutterActor
*
* Checks whether an actor contains the the pointer of a
* #ClutterInputDevice
*
* Return value: %TRUE if the actor contains the pointer, and
* %FALSE otherwise
*
* Since: 1.2
*/
gboolean
clutter_actor_has_pointer (ClutterActor *self)
{
g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
return self->priv->has_pointer;
}

View File

@ -532,6 +532,7 @@ void clutter_actor_get_transformation_matrix (ClutterActor *self
CoglMatrix *matrix); CoglMatrix *matrix);
gboolean clutter_actor_is_in_clone_paint (ClutterActor *self); gboolean clutter_actor_is_in_clone_paint (ClutterActor *self);
gboolean clutter_actor_has_pointer (ClutterActor *self);
void clutter_actor_set_text_direction (ClutterActor *self, void clutter_actor_set_text_direction (ClutterActor *self,
ClutterTextDirection text_dir); ClutterTextDirection text_dir);

View File

@ -0,0 +1,321 @@
/*
* 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>
*/
/**
* SECTION:clutter-device-manager
* @short_description: Maintains the list of input devices
*
* #ClutterDeviceManager is a singleton object, owned by Clutter, which
* maintains the list of #ClutterInputDevice<!-- -->s.
*
* Depending on the backend used by Clutter it is possible to use the
* #ClutterDeviceManager::device-added and
* #ClutterDeviceManager::device-removed to monitor addition and removal
* of devices.
*
* #ClutterDeviceManager is available since Clutter 1.2
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "clutter-debug.h"
#include "clutter-device-manager.h"
#include "clutter-enum-types.h"
#include "clutter-marshal.h"
#include "clutter-private.h"
#define CLUTTER_DEVICE_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_DEVICE_MANAGER, ClutterDeviceManagerClass))
#define CLUTTER_IS_DEVICE_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_DEVICE_MANAGER))
#define CLUTTER_DEVICE_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_DEVICE_MANAGER, ClutterDeviceManagerClass))
typedef struct _ClutterDeviceManagerClass ClutterDeviceManagerClass;
struct _ClutterDeviceManagerClass
{
GObjectClass parent_instance;
};
enum
{
DEVICE_ADDED,
DEVICE_REMOVED,
LAST_SIGNAL
};
static ClutterDeviceManager *default_manager = NULL;
static guint manager_signals[LAST_SIGNAL] = { 0, };
G_DEFINE_TYPE (ClutterDeviceManager, clutter_device_manager, G_TYPE_OBJECT);
static void
clutter_device_manager_class_init (ClutterDeviceManagerClass *klass)
{
/**
* ClutterDeviceManager::device-added:
* @manager: the #ClutterDeviceManager that emitted the signal
* @device: the newly added #ClutterInputDevice
*
* The ::device-added signal is emitted each time a device has been
* added to the #ClutterDeviceManager
*
* Since: 1.2
*/
manager_signals[DEVICE_ADDED] =
g_signal_new (I_("device-added"),
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
clutter_marshal_VOID__OBJECT,
G_TYPE_NONE, 1,
CLUTTER_TYPE_INPUT_DEVICE);
/**
* ClutterDeviceManager::device-removed:
* @manager: the #ClutterDeviceManager that emitted the signal
* @device: the removed #ClutterInputDevice
*
* The ::device-removed signal is emitted each time a device has been
* removed from the #ClutterDeviceManager
*
* Since: 1.2
*/
manager_signals[DEVICE_REMOVED] =
g_signal_new (I_("device-removed"),
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
clutter_marshal_VOID__OBJECT,
G_TYPE_NONE, 1,
CLUTTER_TYPE_INPUT_DEVICE);
}
static void
clutter_device_manager_init (ClutterDeviceManager *self)
{
}
/**
* clutter_device_manager_get_default:
*
* Retrieves the device manager singleton
*
* Return value: (transfer none): the #ClutterDeviceManager singleton.
* The returned instance is owned by Clutter and it should not be
* modified or freed
*
* Since: 1.2
*/
ClutterDeviceManager *
clutter_device_manager_get_default (void)
{
if (G_UNLIKELY (default_manager == NULL))
default_manager = g_object_new (CLUTTER_TYPE_DEVICE_MANAGER, NULL);
return default_manager;
}
/**
* clutter_device_manager_list_devices:
* @device_manager: a #ClutterDeviceManager
*
* Lists all currently registered input devices
*
* Return value: (transfer container) (element-type ClutterInputDevice):
* a newly allocated list of #ClutterInputDevice objects. Use
* g_slist_free() to deallocate it when done
*
* Since: 1.2
*/
GSList *
clutter_device_manager_list_devices (ClutterDeviceManager *device_manager)
{
g_return_val_if_fail (CLUTTER_IS_DEVICE_MANAGER (device_manager), NULL);
return g_slist_copy (device_manager->devices);
}
/**
* clutter_device_manager_peek_devices:
* @device_manager: a #ClutterDeviceManager
*
* Lists all currently registered input devices
*
* Return value: (transfer none) (element-type ClutterInputDevice):
* a pointer to the internal list of #ClutterInputDevice objects. The
* returned list is owned by the #ClutterDeviceManager and should never
* be modified or freed
*
* Since: 1.2
*/
const GSList *
clutter_device_manager_peek_devices (ClutterDeviceManager *device_manager)
{
g_return_val_if_fail (CLUTTER_IS_DEVICE_MANAGER (device_manager), NULL);
return device_manager->devices;
}
/**
* clutter_device_manager_get_device:
* @device_manager: a #ClutterDeviceManager
* @device_id: the integer id of a device
*
* Retrieves the #ClutterInputDevice with the given @device_id
*
* Return value: (transfer none): a #ClutterInputDevice or %NULL. The
* returned device is owned by the #ClutterDeviceManager and should
* never be modified or freed
*
* Since: 1.2
*/
ClutterInputDevice *
clutter_device_manager_get_device (ClutterDeviceManager *device_manager,
gint device_id)
{
GSList *l;
g_return_val_if_fail (CLUTTER_IS_DEVICE_MANAGER (device_manager), NULL);
for (l = device_manager->devices; l != NULL; l = l->next)
{
ClutterInputDevice *device = l->data;
if (device->id == device_id)
return device;
}
return NULL;
}
static gint
input_device_cmp (gconstpointer a,
gconstpointer b)
{
const ClutterInputDevice *device_a = a;
const ClutterInputDevice *device_b = b;
if (device_a->id < device_b->id)
return -1;
if (device_a->id > device_b->id)
return 1;
return 0;
}
/*
* _clutter_device_manager_add_device:
* @device_manager: a #ClutterDeviceManager
* @device: a #ClutterInputDevice
*
* Adds @device to the list of #ClutterInputDevice<!-- -->s maintained
* by @device_manager
*
* The reference count of @device is not increased
*
* The #ClutterDeviceManager::device-added signal is emitted after
* adding @device to the list
*/
void
_clutter_device_manager_add_device (ClutterDeviceManager *device_manager,
ClutterInputDevice *device)
{
g_return_if_fail (CLUTTER_IS_DEVICE_MANAGER (device_manager));
device_manager->devices = g_slist_insert_sorted (device_manager->devices,
device,
input_device_cmp);
g_signal_emit (device_manager, manager_signals[DEVICE_ADDED], 0, device);
}
/*
* _clutter_device_manager_remove_device:
* @device_manager: a #ClutterDeviceManager
* @device: a #ClutterInputDevice
*
* Removes @device from the list of #ClutterInputDevice<!-- -->s
* maintained by @device_manager
*
* The reference count of @device is not decreased
*
* The #ClutterDeviceManager::device-removed signal is emitted after
* removing @device from the list
*/
void
_clutter_device_manager_remove_device (ClutterDeviceManager *device_manager,
ClutterInputDevice *device)
{
g_return_if_fail (CLUTTER_IS_DEVICE_MANAGER (device_manager));
if (g_slist_find (device_manager->devices, device) == NULL)
return;
device_manager->devices = g_slist_remove (device_manager->devices, device);
g_signal_emit (device_manager, manager_signals[DEVICE_REMOVED], 0, device);
}
/*
* _clutter_device_manager_update_devices:
* @device_manager: a #ClutterDeviceManager
*
* Updates every #ClutterInputDevice handled by @device_manager
* by performing a pick paint at the coordinates of each pointer
* device
*/
void
_clutter_device_manager_update_devices (ClutterDeviceManager *device_manager)
{
GSList *d;
/* the user disabled motion events delivery on actors; we
* don't perform any picking since the source of the events
* will always be set to be the stage
*/
if (!clutter_get_motion_events_enabled ())
return;
for (d = device_manager->devices; d != NULL; d = d->next)
{
ClutterInputDevice *device = d->data;
ClutterInputDeviceType device_type;
/* we only care about pointer devices */
device_type = clutter_input_device_get_device_type (device);
if (device_type != CLUTTER_POINTER_DEVICE)
continue;
/* out of stage */
if (device->stage == NULL)
continue;
_clutter_input_device_update (device);
}
}

View File

@ -0,0 +1,59 @@
/*
* 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>
*/
#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION)
#error "Only <clutter/clutter.h> can be included directly."
#endif
#ifndef __CLUTTER_DEVICE_MANAGER_H__
#define __CLUTTER_DEVICE_MANAGER_H__
#include <glib-object.h>
#include <clutter/clutter-event.h>
G_BEGIN_DECLS
#define CLUTTER_TYPE_DEVICE_MANAGER (clutter_device_manager_get_type ())
#define CLUTTER_DEVICE_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_DEVICE_MANAGER, ClutterDeviceManager))
#define CLUTTER_IS_DEVICE_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_DEVICE_MANAGER))
/**
* ClutterDeviceManager:
*
* The #ClutterDeviceManager structure contains only
* private data
*/
typedef struct _ClutterDeviceManager ClutterDeviceManager;
GType clutter_device_manager_get_type (void) G_GNUC_CONST;
ClutterDeviceManager *clutter_device_manager_get_default (void);
GSList * clutter_device_manager_list_devices (ClutterDeviceManager *device_manager);
const GSList * clutter_device_manager_peek_devices (ClutterDeviceManager *device_manager);
ClutterInputDevice * clutter_device_manager_get_device (ClutterDeviceManager *device_manager,
gint device_id);
G_END_DECLS
#endif /* __CLUTTER_DEVICE_MANAGER_H__ */

View File

@ -787,39 +787,3 @@ clutter_get_current_event (void)
return context->current_event; return context->current_event;
} }
/**
* clutter_input_device_get_device_type:
* @device: a #ClutterInputDevice
*
* Retrieves the type of @device
*
* Return value: the type of the device
*
* Since: 1.0
*/
ClutterInputDeviceType
clutter_input_device_get_device_type (ClutterInputDevice *device)
{
g_return_val_if_fail (device != NULL, CLUTTER_POINTER_DEVICE);
return device->device_type;
}
/**
* clutter_input_device_get_device_id:
* @device: a #ClutterInputDevice
*
* Retrieves the unique identifier of @device
*
* Return value: the identifier of the device
*
* Since: 1.0
*/
gint
clutter_input_device_get_device_id (ClutterInputDevice *device)
{
g_return_val_if_fail (device != NULL, -1);
return device->id;
}

View File

@ -29,6 +29,7 @@
#define __CLUTTER_EVENT_H__ #define __CLUTTER_EVENT_H__
#include <glib-object.h> #include <glib-object.h>
#include <clutter/clutter-input-device.h>
#include <clutter/clutter-types.h> #include <clutter/clutter-types.h>
#define CLUTTER_TYPE_EVENT (clutter_event_get_type ()) #define CLUTTER_TYPE_EVENT (clutter_event_get_type ())
@ -201,37 +202,6 @@ typedef struct _ClutterScrollEvent ClutterScrollEvent;
typedef struct _ClutterStageStateEvent ClutterStageStateEvent; typedef struct _ClutterStageStateEvent ClutterStageStateEvent;
typedef struct _ClutterCrossingEvent ClutterCrossingEvent; typedef struct _ClutterCrossingEvent ClutterCrossingEvent;
/**
* ClutterInputDevice:
*
* Generic representation of an input device. The
* actual contents of this structure depend on the
* backend used.
*/
typedef struct _ClutterInputDevice ClutterInputDevice;
/**
* ClutterInputDeviceType:
* @CLUTTER_POINTER_DEVICE: A pointer device
* @CLUTTER_KEYBOARD_DEVICE: A keyboard device
* @CLUTTER_EXTENSION_DEVICE: A generic extension device
* @CLUTTER_N_DEVICE_TYPES: The number of device types
*
* The types of input devices available.
*
* The #ClutterInputDeviceType enumeration can be extended at later
* date; not every platform supports every input device type.
*
* Since: 1.0
*/
typedef enum {
CLUTTER_POINTER_DEVICE,
CLUTTER_KEYBOARD_DEVICE,
CLUTTER_EXTENSION_DEVICE,
CLUTTER_N_DEVICE_TYPES
} ClutterInputDeviceType;
/** /**
* ClutterAnyEvent: * ClutterAnyEvent:
* @type: event type * @type: event type
@ -509,9 +479,6 @@ guint32 clutter_keysym_to_unicode (guint k
guint32 clutter_get_current_event_time (void); guint32 clutter_get_current_event_time (void);
G_CONST_RETURN ClutterEvent *clutter_get_current_event (void); G_CONST_RETURN ClutterEvent *clutter_get_current_event (void);
ClutterInputDeviceType clutter_input_device_get_device_type (ClutterInputDevice *device);
gint clutter_input_device_get_device_id (ClutterInputDevice *device);
G_END_DECLS G_END_DECLS
#endif /* __CLUTTER_EVENT_H__ */ #endif /* __CLUTTER_EVENT_H__ */

View File

@ -0,0 +1,524 @@
/*
* 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>
*/
/**
* SECTION:clutter-input-device
* @short_description: An input device managed by Clutter
*
* #ClutterInputDevice represents an input device known to Clutter.
*
* The #ClutterInputDevice class holds the state of the device, but
* its contents are usually defined by the Clutter backend in use.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "clutter-debug.h"
#include "clutter-enum-types.h"
#include "clutter-input-device.h"
#include "clutter-private.h"
enum
{
PROP_0,
PROP_ID,
PROP_DEVICE_TYPE,
PROP_NAME
};
G_DEFINE_TYPE (ClutterInputDevice, clutter_input_device, G_TYPE_OBJECT);
static void
clutter_input_device_set_property (GObject *gobject,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
ClutterInputDevice *self = CLUTTER_INPUT_DEVICE (gobject);
switch (prop_id)
{
case PROP_ID:
self->id = g_value_get_int (value);
break;
case PROP_DEVICE_TYPE:
self->device_type = g_value_get_enum (value);
break;
case PROP_NAME:
self->device_name = g_strdup (g_value_get_string (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
}
}
static void
clutter_input_device_get_property (GObject *gobject,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
ClutterInputDevice *self = CLUTTER_INPUT_DEVICE (gobject);
switch (prop_id)
{
case PROP_ID:
g_value_set_int (value, self->id);
break;
case PROP_DEVICE_TYPE:
g_value_set_enum (value, self->device_type);
break;
case PROP_NAME:
g_value_set_string (value, self->device_name);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
}
}
static void
clutter_input_device_class_init (ClutterInputDeviceClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GParamSpec *pspec;
gobject_class->set_property = clutter_input_device_set_property;
gobject_class->get_property = clutter_input_device_get_property;
/**
* ClutterInputDevice:id:
*
* The unique identifier of the device
*
* Since: 1.2
*/
pspec = g_param_spec_int ("id",
"Id",
"Unique identifier of the device",
-1, G_MAXINT,
0,
CLUTTER_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY);
g_object_class_install_property (gobject_class, PROP_ID, pspec);
/**
* ClutterInputDevice:name:
*
* The name of the device
*
* Since: 1.2
*/
pspec = g_param_spec_string ("name",
"Name",
"The name of the device",
NULL,
CLUTTER_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY);
g_object_class_install_property (gobject_class, PROP_NAME, pspec);
/**
* ClutterInputDevice:device-type:
*
* The type of the device
*
* Since: 1.2
*/
pspec = g_param_spec_enum ("device-type",
"Device Type",
"The type of the device",
CLUTTER_TYPE_INPUT_DEVICE_TYPE,
CLUTTER_POINTER_DEVICE,
CLUTTER_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY);
g_object_class_install_property (gobject_class, PROP_DEVICE_TYPE, pspec);
}
static void
clutter_input_device_init (ClutterInputDevice *self)
{
self->id = -1;
self->device_type = CLUTTER_POINTER_DEVICE;
self->click_count = 0;
self->current_time = self->previous_time = CLUTTER_CURRENT_TIME;
self->current_x = self->previous_x = -1;
self->current_y = self->previous_y = -1;
self->current_button_number = self->previous_button_number = -1;
self->current_state = self->previous_state = 0;
}
/*
* _clutter_input_device_set_coords:
* @device: a #ClutterInputDevice
* @x: X coordinate of the device
* @y: Y coordinate of the device
*
* Stores the last known coordinates of the device
*/
void
_clutter_input_device_set_coords (ClutterInputDevice *device,
gint x,
gint y)
{
g_return_if_fail (CLUTTER_IS_INPUT_DEVICE (device));
if (device->current_x != x)
device->current_x = x;
if (device->current_y != y)
device->current_y = y;
}
/*
* _clutter_input_device_set_state:
* @device: a #ClutterInputDevice
* @state: a bitmask of modifiers
*
* Stores the last known modifiers state of the device
*/
void
_clutter_input_device_set_state (ClutterInputDevice *device,
ClutterModifierType state)
{
g_return_if_fail (CLUTTER_IS_INPUT_DEVICE (device));
device->current_state = state;
}
/*
* _clutter_input_device_set_time:
* @device: a #ClutterInputDevice
* @time_: the time
*
* Stores the last known event time of the device
*/
void
_clutter_input_device_set_time (ClutterInputDevice *device,
guint32 time_)
{
g_return_if_fail (CLUTTER_IS_INPUT_DEVICE (device));
if (device->current_time != time_)
device->current_time = time_;
}
/*
* cursor_weak_unref:
*
* #ClutterInputDevice keeps a weak reference on the actor
* under its pointer; this function unsets the reference on
* the actor to avoid keeping around stale pointers
*/
static void
cursor_weak_unref (gpointer user_data,
GObject *object_pointer)
{
ClutterInputDevice *device = user_data;
device->cursor_actor = NULL;
}
/*
* _clutter_input_device_set_stage:
* @device: a #ClutterInputDevice
* @stage: a #ClutterStage or %NULL
*
* Stores the stage under the device
*/
void
_clutter_input_device_set_stage (ClutterInputDevice *device,
ClutterStage *stage)
{
g_return_if_fail (CLUTTER_IS_INPUT_DEVICE (device));
device->stage = stage;
/* if we left the stage then we also need to unset the
* cursor actor (and update its :has-pointer property)
*/
if (device->stage == NULL && device->cursor_actor != NULL)
{
_clutter_actor_set_has_pointer (device->cursor_actor, FALSE);
g_object_weak_unref (G_OBJECT (device->cursor_actor),
cursor_weak_unref,
device);
device->cursor_actor = NULL;
}
}
/*
* _clutter_input_device_set_actor:
* @device: a #ClutterInputDevice
* @actor: a #ClutterActor
*
* Sets the actor under the pointer coordinates of @device
*
* This function is called by _clutter_input_device_update()
* and it will:
*
* - queue a %CLUTTER_LEAVE event on the previous pointer actor
* of @device, if any
* - set to %FALSE the :has-pointer property of the previous
* pointer actor of @device, if any
* - queue a %CLUTTER_ENTER event on the new pointer actor
* - set to %TRUE the :has-pointer property of the new pointer
* actor
*/
void
_clutter_input_device_set_actor (ClutterInputDevice *device,
ClutterActor *actor)
{
ClutterActor *old_actor;
ClutterEvent cev;
g_return_if_fail (CLUTTER_IS_INPUT_DEVICE (device));
if (actor == device->cursor_actor)
return;
old_actor = device->cursor_actor;
if (old_actor != NULL)
{
cev.crossing.type = CLUTTER_LEAVE;
cev.crossing.time = device->current_time;
cev.crossing.flags = 0;
cev.crossing.stage = device->stage;
cev.crossing.source = device->cursor_actor;
cev.crossing.x = device->current_x;
cev.crossing.y = device->current_y;
cev.crossing.device = device;
cev.crossing.related = actor;
/* we need to make sure that this event is processed before
* any other event we might have queued up until now, so we
* go on and synthesize the event emission
*/
_clutter_process_event (&cev);
_clutter_actor_set_has_pointer (device->cursor_actor, FALSE);
g_object_weak_unref (G_OBJECT (device->cursor_actor),
cursor_weak_unref,
device);
device->cursor_actor = NULL;
}
if (actor != NULL)
{
cev.crossing.type = CLUTTER_ENTER;
cev.crossing.time = device->current_time;
cev.crossing.flags = 0;
cev.crossing.stage = device->stage;
cev.crossing.source = actor;
cev.crossing.x = device->current_x;
cev.crossing.y = device->current_y;
cev.crossing.device = device;
cev.crossing.related = old_actor;
/* as above: we need to make sure that this event is processed
* before any other event we might have queued up until now, so
* we go on and synthesize the event emission
*/
_clutter_process_event (&cev);
}
device->cursor_actor = actor;
if (device->cursor_actor != NULL)
{
g_object_weak_ref (G_OBJECT (device->cursor_actor),
cursor_weak_unref,
device);
_clutter_actor_set_has_pointer (device->cursor_actor, TRUE);
}
}
/**
* clutter_input_device_get_device_type:
* @device: a #ClutterInputDevice
*
* Retrieves the type of @device
*
* Return value: the type of the device
*
* Since: 1.0
*/
ClutterInputDeviceType
clutter_input_device_get_device_type (ClutterInputDevice *device)
{
g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device),
CLUTTER_POINTER_DEVICE);
return device->device_type;
}
/**
* clutter_input_device_get_device_id:
* @device: a #ClutterInputDevice
*
* Retrieves the unique identifier of @device
*
* Return value: the identifier of the device
*
* Since: 1.0
*/
gint
clutter_input_device_get_device_id (ClutterInputDevice *device)
{
g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), -1);
return device->id;
}
/**
* clutter_input_device_get_device_coords:
* @device: a #ClutterInputDevice of type %CLUTTER_POINTER_DEVICE
* @x: (out): return location for the X coordinate
* @y: (out): return location for the Y coordinate
*
* Retrieves the latest coordinates of the pointer of @device
*
* Since: 1.2
*/
void
clutter_input_device_get_device_coords (ClutterInputDevice *device,
gint *x,
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;
if (y)
*y = device->current_y;
}
/*
* _clutter_input_device_update:
* @device: a #ClutterInputDevice
*
* Updates the input @device by determining the #ClutterActor underneath the
* pointer's cursor
*
* This function calls _clutter_input_device_set_actor() if needed.
*
* This function only works for #ClutterInputDevice of type
* %CLUTTER_POINTER_DEVICE.
*
* Since: 1.2
*/
ClutterActor *
_clutter_input_device_update (ClutterInputDevice *device)
{
ClutterStage *stage;
ClutterActor *new_cursor_actor;
ClutterActor *old_cursor_actor;
gint x, y;
g_return_val_if_fail (device->device_type == CLUTTER_POINTER_DEVICE, NULL);
clutter_input_device_get_device_coords (device, &x, &y);
stage = device->stage;
old_cursor_actor = device->cursor_actor;
new_cursor_actor = _clutter_do_pick (stage, x, y, CLUTTER_PICK_REACTIVE);
/* if the pick could not find an actor then we do not update the
* input device, to avoid ghost enter/leave events; the pick should
* never fail, except for bugs in the glReadPixels() implementation
* in which case this is the safest course of action anyway
*/
if (new_cursor_actor == NULL)
return NULL;
CLUTTER_NOTE (EVENT,
"Actor under cursor (device %d, at %d, %d): %s",
clutter_input_device_get_device_id (device),
x, y,
clutter_actor_get_name (new_cursor_actor) != NULL
? clutter_actor_get_name (new_cursor_actor)
: G_OBJECT_TYPE_NAME (new_cursor_actor));
/* short-circuit here */
if (new_cursor_actor == old_cursor_actor)
return old_cursor_actor;
_clutter_input_device_set_actor (device, new_cursor_actor);
return device->cursor_actor;
}
/**
* clutter_input_device_get_pointer_actor:
* @device: a #ClutterInputDevice of type %CLUTTER_POINTER_DEVICE
*
* Retrieves the #ClutterActor underneath the pointer of @device
*
* Return value: (transfer none): a pointer to the #ClutterActor or %NULL
*
* Since: 1.2
*/
ClutterActor *
clutter_input_device_get_pointer_actor (ClutterInputDevice *device)
{
g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), NULL);
g_return_val_if_fail (device->device_type == CLUTTER_POINTER_DEVICE, NULL);
return device->cursor_actor;
}
/**
* clutter_input_device_get_device_name:
* @device: a #ClutterInputDevice
*
* Retrieves the name of the @device
*
* Return value: the name of the device, or %NULL. The returned string
* is owned by the #ClutterInputDevice and should never be modified
* or freed
*
* Since: 1.2
*/
G_CONST_RETURN gchar *
clutter_input_device_get_device_name (ClutterInputDevice *device)
{
g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), NULL);
return device->device_name;
}

View File

@ -0,0 +1,99 @@
/*
* 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>
*/
#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION)
#error "Only <clutter/clutter.h> can be included directly."
#endif
#ifndef __CLUTTER_INPUT_DEVICE_H__
#define __CLUTTER_INPUT_DEVICE_H__
#include <clutter/clutter-types.h>
G_BEGIN_DECLS
#define CLUTTER_TYPE_INPUT_DEVICE (clutter_input_device_get_type ())
#define CLUTTER_INPUT_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_INPUT_DEVICE, ClutterInputDevice))
#define CLUTTER_IS_INPUT_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_INPUT_DEVICE))
#define CLUTTER_INPUT_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_INPUT_DEVICE, ClutterInputDeviceClass))
#define CLUTTER_IS_INPUT_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_INPUT_DEVICE))
#define CLUTTER_INPUT_DEVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_INPUT_DEVICE, ClutterInputDeviceClass))
/**
* ClutterInputDevice:
*
* Generic representation of an input device. The actual contents of this
* structure depend on the backend used.
*/
typedef struct _ClutterInputDevice ClutterInputDevice;
typedef struct _ClutterInputDeviceClass ClutterInputDeviceClass;
/**
* ClutterInputDeviceType:
* @CLUTTER_POINTER_DEVICE: A pointer device
* @CLUTTER_KEYBOARD_DEVICE: A keyboard device
* @CLUTTER_EXTENSION_DEVICE: A generic extension device
* @CLUTTER_N_DEVICE_TYPES: The number of device types
*
* The types of input devices available.
*
* The #ClutterInputDeviceType enumeration can be extended at later
* date; not every platform supports every input device type.
*
* Since: 1.0
*/
typedef enum {
CLUTTER_POINTER_DEVICE,
CLUTTER_KEYBOARD_DEVICE,
CLUTTER_EXTENSION_DEVICE,
CLUTTER_N_DEVICE_TYPES
} ClutterInputDeviceType;
/**
* ClutterInputDeviceClass:
*
* The #ClutterInputDeviceClass structure contains only private
* data and should not be accessed directly
*
* Since: 1.2
*/
struct _ClutterInputDeviceClass
{
/*< private >*/
GObjectClass parent_class;
};
GType clutter_input_device_get_type (void) G_GNUC_CONST;
ClutterInputDeviceType clutter_input_device_get_device_type (ClutterInputDevice *device);
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);
G_CONST_RETURN gchar * clutter_input_device_get_device_name (ClutterInputDevice *device);
G_END_DECLS
#endif /* __CLUTTER_INPUT_DEVICE_H__ */

View File

@ -2045,69 +2045,93 @@ event_click_count_generate (ClutterEvent *event)
static guint32 previous_time = 0; static guint32 previous_time = 0;
static gint previous_button_number = -1; static gint previous_button_number = -1;
ClutterInputDevice *device = NULL;
ClutterBackend *backend; ClutterBackend *backend;
guint double_click_time; guint double_click_time;
guint double_click_distance; guint double_click_distance;
backend = _clutter_context_get_default ()->backend; backend = clutter_get_default_backend ();
double_click_distance = clutter_backend_get_double_click_distance (backend); double_click_distance = clutter_backend_get_double_click_distance (backend);
double_click_time = clutter_backend_get_double_click_time (backend); double_click_time = clutter_backend_get_double_click_time (backend);
if (event->button.device != NULL) device = clutter_event_get_device (event);
if (device != NULL)
{ {
click_count = event->button.device->click_count; click_count = device->click_count;
previous_x = event->button.device->previous_x; previous_x = device->previous_x;
previous_y = event->button.device->previous_y; previous_y = device->previous_y;
previous_time = event->button.device->previous_time; previous_time = device->previous_time;
previous_button_number = event->button.device->previous_button_number; previous_button_number = device->previous_button_number;
CLUTTER_NOTE (EVENT,
"Restoring previous click count:%d (device:%d, time:%u)",
click_count,
clutter_input_device_get_device_id (device),
previous_time);
}
else
{
CLUTTER_NOTE (EVENT,
"Restoring previous click count:%d (time:%u)",
click_count,
previous_time);
} }
switch (event->type) switch (clutter_event_type (event))
{ {
case CLUTTER_BUTTON_PRESS: case CLUTTER_BUTTON_PRESS:
case CLUTTER_SCROLL:
/* check if we are in time and within distance to increment an /* check if we are in time and within distance to increment an
* existing click count * existing click count
*/ */
if (event->button.time < previous_time + double_click_time && if (event->button.button == previous_button_number &&
event->button.time < (previous_time + double_click_time) &&
(ABS (event->button.x - previous_x) <= double_click_distance) && (ABS (event->button.x - previous_x) <= double_click_distance) &&
(ABS (event->button.y - previous_y) <= double_click_distance) (ABS (event->button.y - previous_y) <= double_click_distance))
&& event->button.button == previous_button_number)
{ {
click_count ++; CLUTTER_NOTE (EVENT, "Increase click count (button: %d, time: %u)",
event->button.button,
event->button.time);
click_count += 1;
} }
else /* start a new click count*/ else /* start a new click count*/
{ {
click_count=1; CLUTTER_NOTE (EVENT, "Reset click count (button: %d, time: %u)",
event->button.button,
event->button.time);
click_count = 1;
previous_button_number = event->button.button; previous_button_number = event->button.button;
} }
/* store time and position for this click for comparison with previous_x = event->button.x;
* next event previous_y = event->button.y;
*/
previous_time = event->button.time; previous_time = event->button.time;
previous_x = event->button.x;
previous_y = event->button.y;
/* fallthrough */ /* fallthrough */
case CLUTTER_BUTTON_RELEASE: case CLUTTER_BUTTON_RELEASE:
event->button.click_count=click_count; event->button.click_count = click_count;
break; break;
default: default:
g_assert (NULL); g_assert (NULL);
} }
if (event->button.device != NULL) if (event->type == CLUTTER_BUTTON_PRESS && device != NULL)
{ {
event->button.device->click_count = click_count; CLUTTER_NOTE (EVENT, "Storing click count: %d (device:%d, time:%u)",
event->button.device->previous_x = previous_x; click_count,
event->button.device->previous_y = previous_y; clutter_input_device_get_device_id (device),
event->button.device->previous_time = previous_time; previous_time);
event->button.device->previous_button_number = previous_button_number;
device->click_count = click_count;
device->previous_x = previous_x;
device->previous_y = previous_y;
device->previous_time = previous_time;
device->previous_button_number = previous_button_number;
} }
} }
static inline void static inline void
emit_event (ClutterEvent *event, emit_event (ClutterEvent *event,
gboolean is_key_event) gboolean is_key_event)
@ -2118,7 +2142,7 @@ emit_event (ClutterEvent *event,
ClutterActor *actor; ClutterActor *actor;
gint i = 0; gint i = 0;
if (!event->any.source) if (event->any.source == NULL)
{ {
CLUTTER_NOTE (EVENT, "No source set, discarding event"); CLUTTER_NOTE (EVENT, "No source set, discarding event");
return; return;
@ -2182,136 +2206,38 @@ static inline void
emit_pointer_event (ClutterEvent *event, emit_pointer_event (ClutterEvent *event,
ClutterInputDevice *device) ClutterInputDevice *device)
{ {
/* Using the global variable directly, since it has to be initialized ClutterMainContext *context = _clutter_context_get_default ();
* at this point
*/
ClutterMainContext *context = ClutterCntx;
if (G_UNLIKELY (context->pointer_grab_actor != NULL && if (context->pointer_grab_actor == NULL &&
device == NULL)) (device == NULL || device->pointer_grab_actor == NULL))
{
/* global grab */
clutter_actor_event (context->pointer_grab_actor, event, FALSE);
}
else if (G_UNLIKELY (device != NULL &&
device->pointer_grab_actor != NULL))
{
/* per device grab */
clutter_actor_event (device->pointer_grab_actor, event, FALSE);
}
else
{ {
/* no grab, time to capture and bubble */ /* no grab, time to capture and bubble */
emit_event (event, FALSE); emit_event (event, FALSE);
} }
else
{
if (context->pointer_grab_actor != NULL)
{
/* global grab */
clutter_actor_event (context->pointer_grab_actor, event, FALSE);
}
else if (device != NULL && device->pointer_grab_actor != NULL)
{
/* per device grab */
clutter_actor_event (device->pointer_grab_actor, event, FALSE);
}
}
} }
static inline void static inline void
emit_keyboard_event (ClutterEvent *event) emit_keyboard_event (ClutterEvent *event)
{ {
ClutterMainContext *context = ClutterCntx; ClutterMainContext *context = _clutter_context_get_default ();
if (G_UNLIKELY (context->keyboard_grab_actor != NULL)) if (context->keyboard_grab_actor == NULL)
clutter_actor_event (context->keyboard_grab_actor, event, FALSE);
else
emit_event (event, TRUE); emit_event (event, TRUE);
}
static void
unset_motion_last_actor (ClutterActor *actor, ClutterInputDevice *dev)
{
ClutterMainContext *context = ClutterCntx;
if (dev == NULL)
context->motion_last_actor = NULL;
else else
dev->motion_last_actor = NULL; clutter_actor_event (context->keyboard_grab_actor, event, FALSE);
}
static void
set_motion_last_actor (ClutterActor *motion_current_actor,
ClutterInputDevice *device)
{
ClutterMainContext *context = ClutterCntx;
ClutterActor *last_actor = context->motion_last_actor;
if (device != NULL)
last_actor = device->motion_last_actor;
if (last_actor && last_actor != motion_current_actor)
{
g_signal_handlers_disconnect_by_func
(last_actor,
G_CALLBACK (unset_motion_last_actor),
device);
}
if (motion_current_actor && last_actor != motion_current_actor)
{
g_signal_connect (motion_current_actor, "destroy",
G_CALLBACK (unset_motion_last_actor),
device);
}
if (device != NULL)
device->motion_last_actor = motion_current_actor;
else
context->motion_last_actor = motion_current_actor;
}
static inline void
generate_enter_leave_events (ClutterEvent *event)
{
ClutterMainContext *context = ClutterCntx;
ClutterActor *motion_current_actor = event->motion.source;
ClutterActor *last_actor = context->motion_last_actor;
ClutterInputDevice *device = clutter_event_get_device (event);
if (device != NULL)
last_actor = device->motion_last_actor;
if (last_actor != motion_current_actor)
{
if (motion_current_actor)
{
ClutterEvent cev;
gfloat x, y;
cev.crossing.device = device;
clutter_event_get_coords (event, &x, &y);
if (context->motion_last_actor)
{
cev.crossing.type = CLUTTER_LEAVE;
cev.crossing.time = event->any.time;
cev.crossing.flags = 0;
cev.crossing.x = x;
cev.crossing.y = y;
cev.crossing.source = last_actor;
cev.crossing.stage = event->any.stage;
cev.crossing.related = motion_current_actor;
emit_pointer_event (&cev, device);
}
cev.crossing.type = CLUTTER_ENTER;
cev.crossing.time = event->any.time;
cev.crossing.flags = 0;
cev.crossing.x = x;
cev.crossing.y = y;
cev.crossing.source = motion_current_actor;
cev.crossing.stage = event->any.stage;
if (context->motion_last_actor)
cev.crossing.related = last_actor;
else
cev.crossing.related = NULL;
emit_pointer_event (&cev, device);
}
}
set_motion_last_actor (motion_current_actor, device);
} }
/** /**
@ -2343,7 +2269,9 @@ _clutter_process_event_details (ClutterActor *stage,
ClutterMainContext *context, ClutterMainContext *context,
ClutterEvent *event) ClutterEvent *event)
{ {
ClutterInputDevice *device = NULL; ClutterInputDevice *device = NULL;
device = clutter_event_get_device (event);
switch (event->type) switch (event->type)
{ {
@ -2352,23 +2280,8 @@ _clutter_process_event_details (ClutterActor *stage,
break; break;
case CLUTTER_LEAVE: case CLUTTER_LEAVE:
/* The source is set for generated events, not for events
* resulting from the cursor leaving the stage
*/
if (event->any.source == NULL)
{
ClutterActor *last_actor = context->motion_last_actor;
if (event->crossing.device != NULL)
last_actor = event->crossing.device->motion_last_actor;
event->any.source = last_actor;
set_motion_last_actor (NULL, event->crossing.device);
}
/* flow through */
case CLUTTER_ENTER: case CLUTTER_ENTER:
emit_pointer_event (event, event->crossing.device); emit_pointer_event (event, device);
break; break;
case CLUTTER_DESTROY_NOTIFY: case CLUTTER_DESTROY_NOTIFY:
@ -2400,8 +2313,6 @@ _clutter_process_event_details (ClutterActor *stage,
break; break;
case CLUTTER_MOTION: case CLUTTER_MOTION:
device = event->motion.device;
/* Only stage gets motion events if clutter_set_motion_events is TRUE, /* Only stage gets motion events if clutter_set_motion_events is TRUE,
* and the event is not a synthetic event with source set. * and the event is not a synthetic event with source set.
*/ */
@ -2434,8 +2345,7 @@ _clutter_process_event_details (ClutterActor *stage,
break; break;
} }
/* fallthrough */ /* fallthrough from motion */
case CLUTTER_BUTTON_PRESS: case CLUTTER_BUTTON_PRESS:
case CLUTTER_BUTTON_RELEASE: case CLUTTER_BUTTON_RELEASE:
case CLUTTER_SCROLL: case CLUTTER_SCROLL:
@ -2462,18 +2372,29 @@ _clutter_process_event_details (ClutterActor *stage,
x, y); x, y);
event->button.source = stage; event->button.source = stage;
emit_pointer_event (event, event->button.device); event->button.click_count = 1;
emit_pointer_event (event, device);
} }
break; break;
} }
/* Map the event to a reactive actor */ /* if the backend provides a device then we should
actor = _clutter_do_pick (CLUTTER_STAGE (stage), * already have everything we need to update it and
x, y, * get the actor underneath
CLUTTER_PICK_REACTIVE); */
if (device != NULL)
actor = _clutter_input_device_update (device);
else
{
CLUTTER_NOTE (EVENT, "No device found: picking");
actor = _clutter_do_pick (CLUTTER_STAGE (stage),
x, y,
CLUTTER_PICK_REACTIVE);
}
event->any.source = actor; event->any.source = actor;
if (!actor) if (event->any.source == NULL)
break; break;
} }
else else
@ -2482,7 +2403,6 @@ _clutter_process_event_details (ClutterActor *stage,
actor = event->any.source; actor = event->any.source;
} }
/* FIXME: for an optimisation should check if there are /* FIXME: for an optimisation should check if there are
* actually any reactive actors and avoid the pick all together * actually any reactive actors and avoid the pick all together
* (signalling just the stage). Should be big help for gles. * (signalling just the stage). Should be big help for gles.
@ -2493,33 +2413,12 @@ _clutter_process_event_details (ClutterActor *stage,
x, y, x, y,
actor); actor);
/* Create, enter/leave events if needed */
generate_enter_leave_events (event);
if (event->type != CLUTTER_MOTION) if (event->type != CLUTTER_MOTION)
{ {
/* Generate click count */ /* Generate click count */
event_click_count_generate (event); event_click_count_generate (event);
} }
if (device == NULL)
{
switch (event->type)
{
case CLUTTER_BUTTON_PRESS:
case CLUTTER_BUTTON_RELEASE:
device = event->button.device;
break;
case CLUTTER_SCROLL:
device = event->scroll.device;
break;
case CLUTTER_MOTION:
/* already handled in the MOTION case of the switch */
default:
break;
}
}
emit_pointer_event (event, device); emit_pointer_event (event, device);
break; break;
} }
@ -3023,9 +2922,19 @@ clutter_get_font_flags (void)
/** /**
* clutter_get_input_device_for_id: * clutter_get_input_device_for_id:
* @id: a device id * @id: the unique id for a device
* *
* Retrieves the #ClutterInputDevice from its id. * Retrieves the #ClutterInputDevice from its @id. This is a convenience
* wrapper for clutter_device_manager_get_device() and it is functionally
* equivalent to:
*
* |[
* ClutterDeviceManager *manager;
* ClutterInputDevice *device;
*
* manager = clutter_device_manager_get_default ();
* device = clutter_device_manager_get_device (manager, id);
* ]|
* *
* Return value: (transfer none): a #ClutterInputDevice, or %NULL * Return value: (transfer none): a #ClutterInputDevice, or %NULL
* *
@ -3034,23 +2943,11 @@ clutter_get_font_flags (void)
ClutterInputDevice * ClutterInputDevice *
clutter_get_input_device_for_id (gint id) clutter_get_input_device_for_id (gint id)
{ {
GSList *item; ClutterDeviceManager *manager;
ClutterInputDevice *device = NULL;
ClutterMainContext *context;
context = _clutter_context_get_default (); manager = clutter_device_manager_get_default ();
for (item = context->input_devices; return clutter_device_manager_get_device (manager, id);
item != NULL;
item = item->next)
{
device = item->data;
if (device->id == id)
return device;
}
return NULL;
} }
/** /**

View File

@ -12,6 +12,7 @@ VOID:INT,INT,INT,INT
VOID:OBJECT VOID:OBJECT
VOID:OBJECT,OBJECT,PARAM VOID:OBJECT,OBJECT,PARAM
VOID:OBJECT,POINTER VOID:OBJECT,POINTER
VOID:POINTER
VOID:STRING,BOOLEAN,BOOLEAN VOID:STRING,BOOLEAN,BOOLEAN
VOID:STRING,INT VOID:STRING,INT
VOID:UINT VOID:UINT

View File

@ -288,14 +288,13 @@ clutter_clock_dispatch (GSource *source,
* event handling * event handling
*/ */
stages = clutter_stage_manager_list_stages (stage_manager); stages = clutter_stage_manager_list_stages (stage_manager);
g_slist_foreach (stages, (GFunc)g_object_ref, NULL); g_slist_foreach (stages, (GFunc) g_object_ref, NULL);
CLUTTER_TIMER_START (_clutter_uprof_context, master_event_process); CLUTTER_TIMER_START (_clutter_uprof_context, master_event_process);
master_clock->updated_stages = FALSE; master_clock->updated_stages = FALSE;
/* Process queued events /* Process queued events */
*/
for (l = stages; l != NULL; l = l->next) for (l = stages; l != NULL; l = l->next)
_clutter_stage_process_queued_events (l->data); _clutter_stage_process_queued_events (l->data);
@ -311,7 +310,7 @@ clutter_clock_dispatch (GSource *source,
for (l = stages; l != NULL; l = l->next) for (l = stages; l != NULL; l = l->next)
master_clock->updated_stages |= _clutter_stage_do_update (l->data); master_clock->updated_stages |= _clutter_stage_do_update (l->data);
g_slist_foreach (stages, (GFunc)g_object_unref, NULL); g_slist_foreach (stages, (GFunc) g_object_unref, NULL);
g_slist_free (stages); g_slist_free (stages);
master_clock->prev_tick = master_clock->cur_tick; master_clock->prev_tick = master_clock->cur_tick;

View File

@ -41,6 +41,7 @@
#include "pango/cogl-pango.h" #include "pango/cogl-pango.h"
#include "clutter-backend.h" #include "clutter-backend.h"
#include "clutter-device-manager.h"
#include "clutter-event.h" #include "clutter-event.h"
#include "clutter-feature.h" #include "clutter-feature.h"
#include "clutter-id-pool.h" #include "clutter-id-pool.h"
@ -52,6 +53,8 @@
G_BEGIN_DECLS G_BEGIN_DECLS
typedef struct _ClutterMainContext ClutterMainContext;
typedef enum { typedef enum {
CLUTTER_ACTOR_UNUSED_FLAG = 0, CLUTTER_ACTOR_UNUSED_FLAG = 0,
@ -82,21 +85,54 @@ typedef enum {
struct _ClutterInputDevice struct _ClutterInputDevice
{ {
gint id; GObject parent_instance;
gint id;
ClutterInputDeviceType device_type; ClutterInputDeviceType device_type;
ClutterActor *pointer_grab_actor; gchar *device_name;
ClutterActor *motion_last_actor;
gint click_count; /* the actor underneath the pointer */
gint previous_x; ClutterActor *cursor_actor;
gint previous_y;
guint32 previous_time; /* the actor that has a grab in place for the device */
gint previous_button_number; ClutterActor *pointer_grab_actor;
/* the current click count */
gint click_count;
/* the stage the device is on */
ClutterStage *stage;
/* the current state */
gint current_x;
gint current_y;
guint32 current_time;
gint current_button_number;
ClutterModifierType current_state;
/* the previous state, used for click count generation */
gint previous_x;
gint previous_y;
guint32 previous_time;
gint previous_button_number;
ClutterModifierType previous_state;
}; };
typedef struct _ClutterMainContext ClutterMainContext; struct _ClutterStageManager
{
GObject parent_instance;
GSList *stages;
};
struct _ClutterDeviceManager
{
GObject parent_instance;
GSList *devices;
};
struct _ClutterMainContext struct _ClutterMainContext
{ {
@ -138,9 +174,6 @@ struct _ClutterMainContext
PangoContext *pango_context; /* Global Pango context */ PangoContext *pango_context; /* Global Pango context */
CoglPangoFontMap *font_map; /* Global font map */ CoglPangoFontMap *font_map; /* Global font map */
GSList *input_devices; /* For extra input devices, i.e
MultiTouch */
ClutterEvent *current_event; ClutterEvent *current_event;
guint32 last_event_time; guint32 last_event_time;
@ -170,21 +203,34 @@ PangoContext *_clutter_context_get_pango_context (ClutterMainContext *self);
#define I_(str) (g_intern_static_string ((str))) #define I_(str) (g_intern_static_string ((str)))
/* 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);
/* 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);
/* stage manager */ /* stage manager */
struct _ClutterStageManager
{
GObject parent_instance;
GSList *stages;
};
void _clutter_stage_manager_add_stage (ClutterStageManager *stage_manager, void _clutter_stage_manager_add_stage (ClutterStageManager *stage_manager,
ClutterStage *stage); ClutterStage *stage);
void _clutter_stage_manager_remove_stage (ClutterStageManager *stage_manager, void _clutter_stage_manager_remove_stage (ClutterStageManager *stage_manager,
ClutterStage *stage); ClutterStage *stage);
/* stage */ /* stage */
void _clutter_stage_set_window (ClutterStage *stage, void _clutter_stage_set_window (ClutterStage *stage,
ClutterStageWindow *stage_window); ClutterStageWindow *stage_window);
ClutterStageWindow *_clutter_stage_get_window (ClutterStage *stage); ClutterStageWindow *_clutter_stage_get_window (ClutterStage *stage);
@ -198,6 +244,7 @@ void _clutter_stage_queue_event (ClutterStage *stage,
ClutterEvent *event); ClutterEvent *event);
gboolean _clutter_stage_has_queued_events (ClutterStage *stage); gboolean _clutter_stage_has_queued_events (ClutterStage *stage);
void _clutter_stage_process_queued_events (ClutterStage *stage); void _clutter_stage_process_queued_events (ClutterStage *stage);
void _clutter_stage_update_input_devices (ClutterStage *stage);
/* vfuncs implemented by backend */ /* vfuncs implemented by backend */
GType _clutter_backend_impl_get_type (void); GType _clutter_backend_impl_get_type (void);
@ -271,6 +318,9 @@ void _clutter_actor_set_enable_model_view_transform (ClutterActor *self,
void _clutter_actor_set_enable_paint_unmapped (ClutterActor *self, void _clutter_actor_set_enable_paint_unmapped (ClutterActor *self,
gboolean enable); gboolean enable);
void _clutter_actor_set_has_pointer (ClutterActor *self,
gboolean has_pointer);
void _clutter_run_repaint_functions (void); void _clutter_run_repaint_functions (void);
gint32 _clutter_backend_get_units_serial (ClutterBackend *backend); gint32 _clutter_backend_get_units_serial (ClutterBackend *backend);

View File

@ -69,6 +69,7 @@
#include "clutter-id-pool.h" #include "clutter-id-pool.h"
#include "clutter-container.h" #include "clutter-container.h"
#include "clutter-profile.h" #include "clutter-profile.h"
#include "clutter-input-device.h"
#include "cogl/cogl.h" #include "cogl/cogl.h"
@ -455,6 +456,7 @@ _clutter_stage_queue_event (ClutterStage *stage,
{ {
ClutterStagePrivate *priv; ClutterStagePrivate *priv;
gboolean first_event; gboolean first_event;
ClutterInputDevice *device;
g_return_if_fail (CLUTTER_IS_STAGE (stage)); g_return_if_fail (CLUTTER_IS_STAGE (stage));
@ -462,14 +464,31 @@ _clutter_stage_queue_event (ClutterStage *stage,
first_event = priv->event_queue->length == 0; first_event = priv->event_queue->length == 0;
g_queue_push_tail (priv->event_queue, g_queue_push_tail (priv->event_queue, clutter_event_copy (event));
clutter_event_copy (event));
if (first_event) if (first_event)
{ {
ClutterMasterClock *master_clock = _clutter_master_clock_get_default (); ClutterMasterClock *master_clock = _clutter_master_clock_get_default ();
_clutter_master_clock_start_running (master_clock); _clutter_master_clock_start_running (master_clock);
} }
/* if needed, update the state of the input device of the event.
* we do it here to avoid calling the same code from every backend
* event processing function
*/
device = clutter_event_get_device (event);
if (device != NULL)
{
ClutterModifierType event_state = clutter_event_get_state (event);
guint32 event_time = clutter_event_get_time (event);
gfloat event_x, event_y;
clutter_event_get_coords (event, &event_x, &event_y);
_clutter_input_device_set_coords (device, event_x, event_y);
_clutter_input_device_set_state (device, event_state);
_clutter_input_device_set_time (device, event_time);
}
} }
gboolean gboolean
@ -488,7 +507,7 @@ void
_clutter_stage_process_queued_events (ClutterStage *stage) _clutter_stage_process_queued_events (ClutterStage *stage)
{ {
ClutterStagePrivate *priv; ClutterStagePrivate *priv;
GList *events, *l;; GList *events, *l;
g_return_if_fail (CLUTTER_IS_STAGE (stage)); g_return_if_fail (CLUTTER_IS_STAGE (stage));
@ -507,7 +526,7 @@ _clutter_stage_process_queued_events (ClutterStage *stage)
priv->event_queue->tail = NULL; priv->event_queue->tail = NULL;
priv->event_queue->length = 0; priv->event_queue->length = 0;
for (l = events; l; l = l->next) for (l = events; l != NULL; l = l->next)
{ {
ClutterEvent *event; ClutterEvent *event;
ClutterEvent *next_event; ClutterEvent *next_event;

View File

@ -51,12 +51,14 @@
#include "clutter-clone.h" #include "clutter-clone.h"
#include "clutter-color.h" #include "clutter-color.h"
#include "clutter-container.h" #include "clutter-container.h"
#include "clutter-device-manager.h"
#include "clutter-event.h" #include "clutter-event.h"
#include "clutter-feature.h" #include "clutter-feature.h"
#include "clutter-fixed-layout.h" #include "clutter-fixed-layout.h"
#include "clutter-flow-layout.h" #include "clutter-flow-layout.h"
#include "clutter-frame-source.h" #include "clutter-frame-source.h"
#include "clutter-group.h" #include "clutter-group.h"
#include "clutter-input-device.h"
#include "clutter-interval.h" #include "clutter-interval.h"
#include "clutter-keysyms.h" #include "clutter-keysyms.h"
#include "clutter-layout-manager.h" #include "clutter-layout-manager.h"

View File

@ -31,6 +31,7 @@
#include "../clutter-event.h" #include "../clutter-event.h"
#include "../clutter-main.h" #include "../clutter-main.h"
#include "../clutter-input-device.h"
#include "../clutter-debug.h" #include "../clutter-debug.h"
#include "../clutter-private.h" #include "../clutter-private.h"
#include "../clutter-version.h" #include "../clutter-version.h"
@ -64,8 +65,30 @@ clutter_backend_win32_pre_parse (ClutterBackend *backend,
static void static void
clutter_backend_win32_init_events (ClutterBackend *backend) clutter_backend_win32_init_events (ClutterBackend *backend)
{ {
ClutterBackendWin32 *backend_win32 = CLUTTER_BACKEND_WIN32 (backend);
ClutterDeviceManager *manager;
ClutterInputDevice *device;
CLUTTER_NOTE (EVENT, "initialising the event loop"); CLUTTER_NOTE (EVENT, "initialising the event loop");
manager = clutter_device_manager_get_default ();
device = g_object_new (CLUTTER_TYPE_INPUT_DEVICE,
"id", 0,
"name", "Core Pointer",
"device-type", CLUTTER_POINTER_DEVICE,
NULL);
_clutter_device_manager_add_device (manager, device);
backend_win32->core_pointer = device;
device = g_object_new (CLUTTER_TYPE_INPUT_DEVICE,
"id", 1,
"name", "Core Keyboard",
"device-type", CLUTTER_KEYBOARD_DEVICE,
NULL);
_clutter_device_manager_add_device (manager, device);
backend_win32->core_keyboard = device;
_clutter_backend_win32_events_init (backend); _clutter_backend_win32_events_init (backend);
} }

View File

@ -51,6 +51,9 @@ struct _ClutterBackendWin32
HCURSOR invisible_cursor; HCURSOR invisible_cursor;
GSource *event_source; GSource *event_source;
ClutterInputDevice *core_pointer;
ClutterInputDevice *core_keyboard;
}; };
struct _ClutterBackendWin32Class struct _ClutterBackendWin32Class

View File

@ -201,9 +201,13 @@ get_modifier_state (WPARAM wparam)
return ret; return ret;
} }
static void static inline void
make_button_event (const MSG *msg, ClutterEvent *event, make_button_event (const MSG *msg,
int button, int click_count, gboolean release) ClutterEvent *event,
int button,
int click_count,
gboolean release,
ClutterInputDevice *device)
{ {
event->type = release ? CLUTTER_BUTTON_RELEASE : CLUTTER_BUTTON_PRESS; event->type = release ? CLUTTER_BUTTON_RELEASE : CLUTTER_BUTTON_PRESS;
event->button.time = msg->time; event->button.time = msg->time;
@ -212,6 +216,7 @@ make_button_event (const MSG *msg, ClutterEvent *event,
event->button.modifier_state = get_modifier_state (msg->wParam); event->button.modifier_state = get_modifier_state (msg->wParam);
event->button.button = button; event->button.button = button;
event->button.click_count = click_count; event->button.click_count = click_count;
event->button.device = device;
} }
/** /**
@ -326,11 +331,11 @@ message_translate (ClutterBackend *backend,
const MSG *msg, const MSG *msg,
gboolean *call_def_window_proc) gboolean *call_def_window_proc)
{ {
ClutterBackendWin32 *backend_win32; ClutterBackendWin32 *backend_win32;
ClutterStageWin32 *stage_win32; ClutterStageWin32 *stage_win32;
ClutterStage *stage; ClutterStage *stage;
ClutterStageWindow *impl; ClutterStageWindow *impl;
gboolean res; gboolean res;
backend_win32 = CLUTTER_BACKEND_WIN32 (backend); backend_win32 = CLUTTER_BACKEND_WIN32 (backend);
@ -429,39 +434,39 @@ message_translate (ClutterBackend *backend,
break; break;
case WM_LBUTTONDOWN: case WM_LBUTTONDOWN:
make_button_event (msg, event, 1, 1, FALSE); make_button_event (msg, event, 1, 1, FALSE, backend_win32->core_pointer);
break; break;
case WM_MBUTTONDOWN: case WM_MBUTTONDOWN:
make_button_event (msg, event, 2, 1, FALSE); make_button_event (msg, event, 2, 1, FALSE, backend_win32->core_pointer);
break; break;
case WM_RBUTTONDOWN: case WM_RBUTTONDOWN:
make_button_event (msg, event, 3, 1, FALSE); make_button_event (msg, event, 3, 1, FALSE, backend_win32->core_pointer);
break; break;
case WM_LBUTTONUP: case WM_LBUTTONUP:
make_button_event (msg, event, 1, 1, TRUE); make_button_event (msg, event, 1, 1, TRUE, backend_win32->core_pointer);
break; break;
case WM_MBUTTONUP: case WM_MBUTTONUP:
make_button_event (msg, event, 2, 1, TRUE); make_button_event (msg, event, 2, 1, TRUE, backend_win32->core_pointer);
break; break;
case WM_RBUTTONUP: case WM_RBUTTONUP:
make_button_event (msg, event, 3, 1, TRUE); make_button_event (msg, event, 3, 1, TRUE, backend_win32->core_pointer);
break; break;
case WM_LBUTTONDBLCLK: case WM_LBUTTONDBLCLK:
make_button_event (msg, event, 1, 2, FALSE); make_button_event (msg, event, 1, 2, FALSE, backend_win32->core_pointer);
break; break;
case WM_MBUTTONDBLCLK: case WM_MBUTTONDBLCLK:
make_button_event (msg, event, 2, 2, FALSE); make_button_event (msg, event, 2, 2, FALSE, backend_win32->core_pointer);
break; break;
case WM_RBUTTONDBLCLK: case WM_RBUTTONDBLCLK:
make_button_event (msg, event, 3, 2, FALSE); make_button_event (msg, event, 3, 2, FALSE, backend_win32->core_pointer);
break; break;
case WM_MOUSEWHEEL: case WM_MOUSEWHEEL:
@ -469,8 +474,8 @@ message_translate (ClutterBackend *backend,
event->type = CLUTTER_SCROLL; event->type = CLUTTER_SCROLL;
event->scroll.time = msg->time; event->scroll.time = msg->time;
event->scroll.modifier_state event->scroll.modifier_state = get_modifier_state (LOWORD (msg->wParam));
= get_modifier_state (LOWORD (msg->wParam)); event->scroll.device = backend_win32->core_pointer;
/* conversion to window coordinates is required */ /* conversion to window coordinates is required */
{ {
@ -500,7 +505,9 @@ message_translate (ClutterBackend *backend,
event->motion.x = GET_X_LPARAM (msg->lParam); event->motion.x = GET_X_LPARAM (msg->lParam);
event->motion.y = GET_Y_LPARAM (msg->lParam); event->motion.y = GET_Y_LPARAM (msg->lParam);
event->motion.modifier_state = get_modifier_state (msg->wParam); event->motion.modifier_state = get_modifier_state (msg->wParam);
/* We need to start tracking when the mouse leaves the stage if event->motion.device = backend_win32->core_pointer;
/* We need to start tracking when the mouse enters the stage if
we're not already */ we're not already */
if (!stage_win32->tracking_mouse) if (!stage_win32->tracking_mouse)
{ {
@ -511,6 +518,9 @@ message_translate (ClutterBackend *backend,
tmevent.hwndTrack = stage_win32->hwnd; tmevent.hwndTrack = stage_win32->hwnd;
TrackMouseEvent (&tmevent); TrackMouseEvent (&tmevent);
/* we entered the stage */
_clutter_input_device_set_stage (event->motion.device, stage);
stage_win32->tracking_mouse = TRUE; stage_win32->tracking_mouse = TRUE;
} }
break; break;
@ -520,6 +530,10 @@ message_translate (ClutterBackend *backend,
event->crossing.time = msg->time; event->crossing.time = msg->time;
event->crossing.x = msg->pt.x; event->crossing.x = msg->pt.x;
event->crossing.y = msg->pt.y; event->crossing.y = msg->pt.y;
event->crossing.device = backend_win32->core_pointer;
/* we left the stage */
_clutter_input_device_set_stage (event->crossing.device, NULL);
/* When we get a leave message the mouse tracking is /* When we get a leave message the mouse tracking is
automatically cancelled so we'll need to start it again when automatically cancelled so we'll need to start it again when
@ -604,6 +618,7 @@ message_translate (ClutterBackend *backend,
event->key.time = msg->time; event->key.time = msg->time;
event->key.modifier_state = get_key_modifier_state (key_states); event->key.modifier_state = get_key_modifier_state (key_states);
event->key.hardware_keycode = scan_code; event->key.hardware_keycode = scan_code;
event->key.device = backend_win32->core_keyboard;
} }
break; break;

View File

@ -43,6 +43,8 @@ libclutter_x11_la_SOURCES = \
clutter-backend-x11.h \ clutter-backend-x11.h \
clutter-backend-x11.c \ clutter-backend-x11.c \
clutter-event-x11.c \ clutter-event-x11.c \
clutter-input-device-x11.h \
clutter-input-device-x11.c \
clutter-stage-x11.h \ clutter-stage-x11.h \
clutter-stage-x11.c \ clutter-stage-x11.c \
clutter-x11-enum-types.h \ clutter-x11-enum-types.h \

View File

@ -37,6 +37,7 @@
#include <errno.h> #include <errno.h>
#include "clutter-backend-x11.h" #include "clutter-backend-x11.h"
#include "clutter-input-device-x11.h"
#include "clutter-stage-x11.h" #include "clutter-stage-x11.h"
#include "clutter-x11.h" #include "clutter-x11.h"
@ -46,31 +47,16 @@
#include <X11/extensions/XInput.h> #include <X11/extensions/XInput.h>
#endif #endif
#include "../clutter-event.h"
#include "../clutter-main.h"
#include "../clutter-debug.h"
#include "../clutter-private.h"
#include "cogl/cogl.h" #include "cogl/cogl.h"
#include "../clutter-debug.h"
#include "../clutter-device-manager.h"
#include "../clutter-event.h"
#include "../clutter-main.h"
#include "../clutter-private.h"
G_DEFINE_TYPE (ClutterBackendX11, clutter_backend_x11, CLUTTER_TYPE_BACKEND); G_DEFINE_TYPE (ClutterBackendX11, clutter_backend_x11, CLUTTER_TYPE_BACKEND);
struct _ClutterX11XInputDevice
{
ClutterInputDevice device;
#ifdef HAVE_XINPUT
XDevice *xdevice;
XEventClass xevent_list[5]; /* MAX 5 event types */
int num_events;
#endif
};
#ifdef HAVE_XINPUT
void _clutter_x11_register_xinput ();
#endif
/* atoms; remember to add the code that assigns the atom value to /* atoms; remember to add the code that assigns the atom value to
* the member of the ClutterBackendX11 structure if you add an * the member of the ClutterBackendX11 structure if you add an
* atom name here. do not change the order! * atom name here. do not change the order!
@ -109,6 +95,129 @@ static gboolean clutter_synchronise = FALSE;
static int TrappedErrorCode = 0; static int TrappedErrorCode = 0;
static int (* old_error_handler) (Display *, XErrorEvent *); static int (* old_error_handler) (Display *, XErrorEvent *);
static void
clutter_x11_register_input_devices (ClutterBackendX11 *backend)
{
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_get_default ();
if (!clutter_enable_xinput)
{
CLUTTER_NOTE (BACKEND, "XInput support not enabled");
goto default_device;
}
#ifdef HAVE_XINPUT
res = XQueryExtension (backend->xdpy, "XInputExtension",
&opcode,
&event,
&error);
if (!res)
{
CLUTTER_NOTE (BACKEND, "No XInput extension available");
goto default_device;
}
backend->xi_event_base = event;
x_devices = XListInputDevices (backend->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:
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);
_clutter_device_manager_add_device (manager, device);
if (info->use == IsXExtensionPointer && n_events > 0)
backend->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");
_clutter_device_manager_add_device (manager, device);
backend->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");
_clutter_device_manager_add_device (manager, device);
backend->core_keyboard = device;
}
gboolean gboolean
clutter_backend_x11_pre_parse (ClutterBackend *backend, clutter_backend_x11_pre_parse (ClutterBackend *backend,
GError **error) GError **error)
@ -199,9 +308,8 @@ clutter_backend_x11_post_parse (ClutterBackend *backend,
clutter_backend_set_resolution (backend, dpi); clutter_backend_set_resolution (backend, dpi);
#ifdef HAVE_XINPUT /* register input devices */
_clutter_x11_register_xinput (); clutter_x11_register_input_devices (backend_x11);
#endif
if (clutter_synchronise) if (clutter_synchronise)
XSynchronize (backend_x11->xdpy, True); XSynchronize (backend_x11->xdpy, True);
@ -644,239 +752,29 @@ clutter_x11_remove_filter (ClutterX11FilterFunc func,
} }
} }
void
_clutter_x11_register_xinput ()
{
#ifdef HAVE_XINPUT
XDeviceInfo *xdevices = NULL;
XDeviceInfo *info = NULL;
XDevice *xdevice = NULL;
XInputClassInfo *xclass_info = NULL;
gint opcode, event, error;
gint res;
gint num_devices = 0;
gint num_events = 0;
gint i = 0, j = 0;
gboolean have_an_xpointer = FALSE;
ClutterBackendX11 *x11b;
ClutterX11XInputDevice *device = NULL;
ClutterMainContext *context;
GSList *input_devices = NULL;
if (!backend_singleton)
{
g_critical ("X11 backend has not been initialised");
return;
}
if (!clutter_enable_xinput)
{
CLUTTER_NOTE (BACKEND, "Not enabling XInput");
return;
}
context = _clutter_context_get_default ();
backend_singleton->have_xinput = FALSE;
/* is the XInput extension available? */
res = XQueryExtension (backend_singleton->xdpy, "XInputExtension",
&opcode, &event,
&error);
if (!res)
{
CLUTTER_NOTE (BACKEND, "X Input extension not available");
return;
}
x11b = backend_singleton;
xdevices = XListInputDevices (x11b->xdpy, &num_devices);
CLUTTER_NOTE (BACKEND, "%d XINPUT devices found", num_devices);
if (num_devices == 0)
return;
for (i = 0; i < num_devices; i++)
{
info = xdevices + i;
num_events = 0;
CLUTTER_NOTE (BACKEND, "Considering %li with type %d",
info->id,
info->use);
/* Only want 'raw' devices themselves not virtual ones */
if (info->use == IsXExtensionPointer ||
/*info->use == IsXExtensionKeyboard || XInput 1.x is broken */
info->use == IsXExtensionDevice)
{
clutter_x11_trap_x_errors ();
xdevice = XOpenDevice (backend_singleton->xdpy, info->id);
if (clutter_x11_untrap_x_errors () || xdevice == NULL)
continue;
/* Create the appropriate Clutter device */
device = g_slice_new0 (ClutterX11XInputDevice);
device->device.id = info->id;
switch (info->use)
{
case IsXExtensionPointer:
device->device.device_type = CLUTTER_POINTER_DEVICE;
have_an_xpointer = TRUE;
break;
#if 0
/* XInput 1.x is broken for keyboards: */
case IsXExtensionKeyboard:
device->device.type = CLUTTER_KEYBOARD_DEVICE;
break;
#endif
case IsXExtensionDevice:
device->device.device_type = CLUTTER_EXTENSION_DEVICE;
break;
}
/* FIXME: some kind of general device_init() call should do below */
device->device.click_count = 0;
device->device.previous_time = 0;
device->device.previous_x = -1;
device->device.previous_y = -1;
device->device.previous_button_number = -1;
device->num_events = 0;
device->xdevice = xdevice;
CLUTTER_NOTE (BACKEND, "Registering XINPUT device with XID: %li",
xdevice->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 (j = 0; j < xdevice->num_classes; j++)
{
xclass_info = xdevice->classes + j;
switch (xclass_info->input_class)
{
#if 0
/* XInput 1.x is broken for keyboards: */
case KeyClass:
DeviceKeyPress (xdevice,
x11b->event_types[CLUTTER_X11_XINPUT_KEY_PRESS_EVENT],
device->xevent_list [num_events]);
num_events++;
DeviceKeyRelease (xdevice,
x11b->event_types[CLUTTER_X11_XINPUT_KEY_RELEASE_EVENT],
device->xevent_list [num_events]);
num_events++;
break;
#endif
case ButtonClass:
DeviceButtonPress (xdevice,
x11b->event_types[CLUTTER_X11_XINPUT_BUTTON_PRESS_EVENT],
device->xevent_list [num_events]);
num_events++;
DeviceButtonRelease (xdevice,
x11b->event_types[CLUTTER_X11_XINPUT_BUTTON_RELEASE_EVENT],
device->xevent_list [num_events]);
num_events++;
break;
case ValuatorClass:
DeviceMotionNotify (xdevice,
x11b->event_types[CLUTTER_X11_XINPUT_MOTION_NOTIFY_EVENT],
device->xevent_list [num_events]);
num_events++;
break;
}
}
if (info->use == IsXExtensionPointer && num_events > 0)
have_an_xpointer = TRUE;
device->num_events = num_events;
input_devices = g_slist_prepend (input_devices, device);
}
}
XFree (xdevices);
if (!have_an_xpointer)
{
GSList *l;
/* Something is likely wrong with Xinput setup so we basically
* abort here and fall back to lofi regular xinput.
*/
g_warning ("No usuable XInput pointing devices found");
for (l = input_devices; l != NULL; l = l->next)
g_slice_free (ClutterX11XInputDevice, l->data);
g_slist_free (input_devices);
context->input_devices = NULL;
return;
}
/* store the list of input devices */
context->input_devices = g_slist_reverse (input_devices);
/* why yes, we are awesome */
backend_singleton->have_xinput = TRUE;
#endif /* HAVE_XINPUT */
}
void
_clutter_x11_unregister_xinput ()
{
}
void void
_clutter_x11_select_events (Window xwin) _clutter_x11_select_events (Window xwin)
{ {
#ifdef HAVE_XINPUT #ifdef HAVE_XINPUT
GSList *list_it; ClutterDeviceManager *manager;
ClutterX11XInputDevice *device = NULL; const GSList *l;
ClutterMainContext *context; if (G_UNLIKELY (backend_singleton == NULL))
context = _clutter_context_get_default ();
if (!backend_singleton)
{ {
g_critical ("X11 backend has not been initialised"); g_critical ("X11 backend has not been initialised");
return; return;
} }
for (list_it = context->input_devices; manager = clutter_device_manager_get_default ();
list_it != NULL;
list_it = list_it->next)
{
device = (ClutterX11XInputDevice *)list_it->data;
XSelectExtensionEvent (backend_singleton->xdpy, for (l = clutter_device_manager_peek_devices (manager);
xwin, l != NULL;
device->xevent_list, l = l->next)
device->num_events); {
ClutterInputDevice *device = l->data;
_clutter_input_device_x11_select_events (device, backend_singleton, xwin);
} }
#endif /* HAVE_XINPUT */ #endif /* HAVE_XINPUT */
} }
@ -884,49 +782,34 @@ _clutter_x11_select_events (Window xwin)
ClutterInputDevice * ClutterInputDevice *
_clutter_x11_get_device_for_xid (XID id) _clutter_x11_get_device_for_xid (XID id)
{ {
#ifdef HAVE_XINPUT ClutterDeviceManager *manager;
ClutterMainContext *context;
GSList *l;
context = _clutter_context_get_default (); manager = clutter_device_manager_get_default ();
if (!backend_singleton) return clutter_device_manager_get_device (manager, (gint) id);
{
g_critical ("X11 backend has not been initialised");
return NULL;
}
for (l = context->input_devices; l != NULL; l = l->next)
{
ClutterX11XInputDevice *device = l->data;
if (device->xdevice->device_id == id)
return (ClutterInputDevice *) device;
}
#endif /* HAVE_XINPUT */
return NULL;
} }
/* FIXME: This nasty little func needs moving elsewhere.. */ /**
* clutter_x11_get_input_devices:
*
* Retrieves a pointer to the list of input devices
*
* Deprecated: 1.2: Use clutter_device_manager_peek_devices() instead
*
* Since: 0.8
*
* Return value: a pointer to the internal list of input devices; the
* returned list is owned by Clutter and should not be modified or
* freed
*/
G_CONST_RETURN GSList * G_CONST_RETURN GSList *
clutter_x11_get_input_devices (void) clutter_x11_get_input_devices (void)
{ {
#ifdef HAVE_XINPUT ClutterDeviceManager *manager;
ClutterMainContext *context;
if (!backend_singleton) manager = clutter_device_manager_get_default ();
{
g_critical ("X11 backend has not been initialised");
return NULL;
}
context = _clutter_context_get_default (); return clutter_device_manager_peek_devices (manager);
return context->input_devices;
#else /* !HAVE_XINPUT */
return NULL;
#endif /* HAVE_XINPUT */
} }
/** /**
@ -943,7 +826,7 @@ gboolean
clutter_x11_has_xinput (void) clutter_x11_has_xinput (void)
{ {
#ifdef HAVE_XINPUT #ifdef HAVE_XINPUT
if (!backend_singleton) if (backend_singleton == NULL)
{ {
g_critical ("X11 backend has not been initialised"); g_critical ("X11 backend has not been initialised");
return FALSE; return FALSE;

View File

@ -76,10 +76,14 @@ struct _ClutterBackendX11
Atom atom_NET_WM_NAME; Atom atom_NET_WM_NAME;
Atom atom_UTF8_STRING; Atom atom_UTF8_STRING;
int xi_event_base;
int event_types[CLUTTER_X11_XINPUT_LAST_EVENT]; int event_types[CLUTTER_X11_XINPUT_LAST_EVENT];
gboolean have_xinput; gboolean have_xinput;
Time last_event_time; Time last_event_time;
ClutterInputDevice *core_pointer;
ClutterInputDevice *core_keyboard;
}; };
struct _ClutterBackendX11Class struct _ClutterBackendX11Class
@ -125,12 +129,6 @@ clutter_backend_x11_get_features (ClutterBackend *backend);
XVisualInfo * XVisualInfo *
clutter_backend_x11_get_visual_info (ClutterBackendX11 *backend_x11); clutter_backend_x11_get_visual_info (ClutterBackendX11 *backend_x11);
void
_clutter_x11_register_xinput (void);
void
_clutter_x11_unregister_xinput (void);
ClutterInputDevice * ClutterInputDevice *
_clutter_x11_get_device_for_xid (XID id); _clutter_x11_get_device_for_xid (XID id);

View File

@ -1,7 +1,8 @@
/* Clutter. /* Clutter.
* An OpenGL based 'interactive canvas' library. * An OpenGL based 'interactive canvas' library.
* Authored By Matthew Allum <mallum@openedhand.com> *
* Copyright (C) 2006-2007 OpenedHand * Copyright (C) 2006, 2007, 2008 OpenedHand Ltd
* Copyright (C) 2009, 2010 Intel Corp.
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
@ -17,6 +18,10 @@
* License along with this library; if not, write to the * License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA. * Boston, MA 02111-1307, USA.
*
* Authored by:
* Matthew Allum <mallum@openedhand.com>
* Emmanuele Bassi <ebassi@linux.intel.com>
*/ */
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
@ -183,7 +188,6 @@ _clutter_backend_x11_events_init (ClutterBackend *backend)
g_source_add_poll (source, &event_source->event_poll_fd); g_source_add_poll (source, &event_source->event_poll_fd);
g_source_set_can_recurse (source, TRUE); g_source_set_can_recurse (source, TRUE);
g_source_attach (source, NULL); g_source_attach (source, NULL);
} }
void void
@ -263,9 +267,10 @@ set_user_time (ClutterBackendX11 *backend_x11,
} }
} }
#if 0 /* See XInput keyboard comment below HAVE_XINPUT */ #ifdef HAVE_XINPUT
static void static void
convert_xdevicekey_to_xkey (XDeviceKeyEvent *xkev, XEvent *xevent) convert_xdevicekey_to_xkey (XDeviceKeyEvent *xkev,
XEvent *xevent)
{ {
xevent->xany.type = xevent->xkey.type = xkev->type; xevent->xany.type = xevent->xkey.type = xkev->type;
xevent->xkey.serial = xkev->serial; xevent->xkey.serial = xkev->serial;
@ -410,27 +415,28 @@ event_translate (ClutterBackend *backend,
ClutterEvent *event, ClutterEvent *event,
XEvent *xevent) XEvent *xevent)
{ {
ClutterBackendX11 *backend_x11; ClutterBackendX11 *backend_x11;
ClutterStageX11 *stage_x11; ClutterStageX11 *stage_x11;
ClutterStage *stage; ClutterStage *stage;
ClutterStageWindow *impl; ClutterStageWindow *impl;
gboolean res, not_yet_handled = FALSE; gboolean res, not_yet_handled = FALSE;
Window xwindow, stage_xwindow; Window xwindow, stage_xwindow;
ClutterDeviceManager *manager;
ClutterInputDevice *device;
backend_x11 = CLUTTER_BACKEND_X11 (backend); backend_x11 = CLUTTER_BACKEND_X11 (backend);
xwindow = xevent->xany.window; xwindow = xevent->xany.window;
if (backend_x11->event_filters) if (backend_x11->event_filters)
{ {
GSList *node; GSList *node;
ClutterX11EventFilter *filter;
node = backend_x11->event_filters; node = backend_x11->event_filters;
while (node) while (node)
{ {
filter = node->data; ClutterX11EventFilter *filter = node->data;
switch (filter->func (xevent, event, filter->data)) switch (filter->func (xevent, event, filter->data))
{ {
@ -448,19 +454,19 @@ event_translate (ClutterBackend *backend,
} }
} }
/* /* Do further processing only on events for the stage window (the x11
* Do further processing only on events for the stage window * filters might be getting events for other windows, so do not mess
* (the x11 filters might be getting events for other windows, so do not * them about.
* mess them about.
*/ */
stage = clutter_x11_get_stage_from_window (xwindow); stage = clutter_x11_get_stage_from_window (xwindow);
if (stage == NULL) if (stage == NULL)
return FALSE; return FALSE;
impl = _clutter_stage_get_window (stage); manager = clutter_device_manager_get_default ();
stage_x11 = CLUTTER_STAGE_X11 (impl);
stage_xwindow = xwindow; /* clutter_x11_get_stage_window (stage); */ impl = _clutter_stage_get_window (stage);
stage_x11 = CLUTTER_STAGE_X11 (impl);
stage_xwindow = xwindow; /* clutter_x11_get_stage_window (stage); */
event->any.stage = stage; event->any.stage = stage;
@ -621,6 +627,8 @@ event_translate (ClutterBackend *backend,
case KeyPress: case KeyPress:
event->key.type = event->type = CLUTTER_KEY_PRESS; event->key.type = event->type = CLUTTER_KEY_PRESS;
event->key.device = backend_x11->core_keyboard;
translate_key_event (backend, event, xevent); translate_key_event (backend, event, xevent);
set_user_time (backend_x11, &xwindow, xevent->xkey.time); set_user_time (backend_x11, &xwindow, xevent->xkey.time);
@ -654,6 +662,8 @@ event_translate (ClutterBackend *backend,
} }
event->key.type = event->type = CLUTTER_KEY_RELEASE; event->key.type = event->type = CLUTTER_KEY_RELEASE;
event->key.device = backend_x11->core_keyboard;
translate_key_event (backend, event, xevent); translate_key_event (backend, event, xevent);
break; break;
@ -666,239 +676,287 @@ event_translate (ClutterBackend *backend,
/* Input device event handling.. */ /* Input device event handling.. */
if (not_yet_handled) if (not_yet_handled)
{ {
if (!clutter_x11_has_xinput ()) device = backend_x11->core_pointer;
/* Regular X event */
switch (xevent->type)
{ {
/* Regular X event */ case ButtonPress:
switch (xevent->type) switch (xevent->xbutton.button)
{ {
/* KeyPress / KeyRelease should reside here if XInput case 4: /* up */
* worked properly case 5: /* down */
*/ case 6: /* left */
case ButtonPress: case 7: /* right */
switch (xevent->xbutton.button) event->scroll.type = event->type = CLUTTER_SCROLL;
{
case 4: /* up */ if (xevent->xbutton.button == 4)
case 5: /* down */ event->scroll.direction = CLUTTER_SCROLL_UP;
case 6: /* left */ else if (xevent->xbutton.button == 5)
case 7: /* right */ event->scroll.direction = CLUTTER_SCROLL_DOWN;
event->scroll.type = event->type = CLUTTER_SCROLL; else if (xevent->xbutton.button == 6)
event->scroll.direction = CLUTTER_SCROLL_LEFT;
if (xevent->xbutton.button == 4) else
event->scroll.direction = CLUTTER_SCROLL_UP; event->scroll.direction = CLUTTER_SCROLL_RIGHT;
else if (xevent->xbutton.button == 5)
event->scroll.direction = CLUTTER_SCROLL_DOWN; event->scroll.time = xevent->xbutton.time;
else if (xevent->xbutton.button == 6) event->scroll.x = xevent->xbutton.x;
event->scroll.direction = CLUTTER_SCROLL_LEFT; event->scroll.y = xevent->xbutton.y;
else event->scroll.modifier_state = xevent->xbutton.state;
event->scroll.direction = CLUTTER_SCROLL_RIGHT; event->scroll.device = device;
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;
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;
break;
}
set_user_time (backend_x11, &xwindow, event->button.time);
break; break;
case ButtonRelease: default:
/* scroll events don't have a corresponding release */ event->button.type = event->type = CLUTTER_BUTTON_PRESS;
if (xevent->xbutton.button == 4 ||
xevent->xbutton.button == 5 ||
xevent->xbutton.button == 6 ||
xevent->xbutton.button == 7)
{
res = FALSE;
break;
}
event->button.type = event->type = CLUTTER_BUTTON_RELEASE;
event->button.time = xevent->xbutton.time; event->button.time = xevent->xbutton.time;
event->button.x = xevent->xbutton.x; event->button.x = xevent->xbutton.x;
event->button.y = xevent->xbutton.y; event->button.y = xevent->xbutton.y;
event->button.modifier_state = xevent->xbutton.state; event->button.modifier_state = xevent->xbutton.state;
event->button.button = xevent->xbutton.button; event->button.button = xevent->xbutton.button;
event->button.device = device;
break; break;
}
case MotionNotify:
event->motion.type = event->type = CLUTTER_MOTION;
event->motion.time = xevent->xmotion.time;
event->motion.x = xevent->xmotion.x;
event->motion.y = xevent->xmotion.y;
event->motion.modifier_state = xevent->xmotion.state;
break;
case EnterNotify: set_user_time (backend_x11, &xwindow, event->button.time);
/* 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;
break;
case LeaveNotify: res = TRUE;
event->crossing.type = event->type = CLUTTER_LEAVE; break;
event->crossing.time = xevent->xcrossing.time;
event->crossing.x = xevent->xcrossing.x; case ButtonRelease:
event->crossing.y = xevent->xcrossing.y; /* scroll events don't have a corresponding release */
if (xevent->xbutton.button == 4 ||
xevent->xbutton.button == 5 ||
xevent->xbutton.button == 6 ||
xevent->xbutton.button == 7)
{
res = FALSE;
goto out;
}
event->button.type = event->type = CLUTTER_BUTTON_RELEASE;
event->button.time = xevent->xbutton.time;
event->button.x = xevent->xbutton.x;
event->button.y = xevent->xbutton.y;
event->button.modifier_state = xevent->xbutton.state;
event->button.button = xevent->xbutton.button;
event->button.device = device;
res = TRUE;
break;
case MotionNotify:
event->motion.type = event->type = CLUTTER_MOTION;
event->motion.time = xevent->xmotion.time;
event->motion.x = xevent->xmotion.x;
event->motion.y = xevent->xmotion.y;
event->motion.modifier_state = xevent->xmotion.state;
event->motion.device = device;
res = TRUE;
break;
case EnterNotify:
/* we know that we are entering the stage here */
_clutter_input_device_set_stage (device, stage);
CLUTTER_NOTE (EVENT, "Entering the stage");
/* Convert enter notifies to motion events because X
doesn't emit the corresponding motion notify */
event->motion.type = event->type = CLUTTER_MOTION;
event->motion.time = xevent->xcrossing.time;
event->motion.x = xevent->xcrossing.x;
event->motion.y = xevent->xcrossing.y;
event->motion.modifier_state = xevent->xcrossing.state;
event->motion.source = CLUTTER_ACTOR (stage);
event->motion.device = device;
res = TRUE;
break;
case LeaveNotify:
if (device->stage == NULL)
{
CLUTTER_NOTE (EVENT,
"Discarding LeaveNotify for ButtonRelease "
"event off-stage");
res = FALSE;
goto out;
}
/* we know that we are leaving the stage here */
_clutter_input_device_set_stage (device, NULL);
CLUTTER_NOTE (EVENT, "Leaving the stage (time:%u)",
event->crossing.time);
event->crossing.type = event->type = CLUTTER_LEAVE;
event->crossing.time = xevent->xcrossing.time;
event->crossing.x = xevent->xcrossing.x;
event->crossing.y = xevent->xcrossing.y;
event->crossing.source = CLUTTER_ACTOR (stage);
event->crossing.device = device;
res = TRUE;
break;
default:
res = FALSE;
break;
}
}
/* XInput fun...*/
if (!res && clutter_x11_has_xinput ())
{
#ifdef HAVE_XINPUT
int *ev_types = backend_x11->event_types;
int button_press, button_release;
int key_press, key_release;
int motion_notify;
button_press = ev_types[CLUTTER_X11_XINPUT_BUTTON_PRESS_EVENT];
button_release = ev_types[CLUTTER_X11_XINPUT_BUTTON_RELEASE_EVENT];
motion_notify = ev_types[CLUTTER_X11_XINPUT_MOTION_NOTIFY_EVENT];
key_press = ev_types[CLUTTER_X11_XINPUT_KEY_PRESS_EVENT];
key_release = ev_types[CLUTTER_X11_XINPUT_KEY_RELEASE_EVENT];
CLUTTER_NOTE (EVENT, "XInput event type: %d", xevent->type);
if (xevent->type == button_press)
{
XDeviceButtonEvent *xbev = (XDeviceButtonEvent *) xevent;
device = _clutter_x11_get_device_for_xid (xbev->deviceid);
_clutter_input_device_set_stage (device, stage);
CLUTTER_NOTE (EVENT,
"XI ButtonPress for %li ('%s') at %d, %d",
xbev->deviceid,
device->device_name,
xbev->x,
xbev->y);
switch (xbev->button)
{
case 4:
case 5:
case 6:
case 7:
event->scroll.type = event->type = CLUTTER_SCROLL;
if (xbev->button == 4)
event->scroll.direction = CLUTTER_SCROLL_UP;
else if (xbev->button == 5)
event->scroll.direction = CLUTTER_SCROLL_DOWN;
else if (xbev->button == 6)
event->scroll.direction = CLUTTER_SCROLL_LEFT;
else
event->scroll.direction = CLUTTER_SCROLL_RIGHT;
event->scroll.time = xbev->time;
event->scroll.x = xbev->x;
event->scroll.y = xbev->y;
event->scroll.modifier_state = xbev->state;
event->scroll.device = device;
break; break;
default: default:
/* ignore every other event */ event->button.type = event->type = CLUTTER_BUTTON_PRESS;
res = FALSE;
break;
}
}
else
{ /* XInput fun.. Needs clean up. */
#ifdef HAVE_XINPUT
int *ev_types = backend_x11->event_types;
CLUTTER_NOTE (EVENT, "XInput event type: %d", xevent->type);
if (xevent->type == ev_types [CLUTTER_X11_XINPUT_BUTTON_PRESS_EVENT])
{
XDeviceButtonEvent *xbev = (XDeviceButtonEvent *) xevent;
CLUTTER_NOTE (EVENT, "XINPUT Button press event for %li at %d, %d",
xbev->deviceid,
xbev->x,
xbev->y);
switch (xbev->button)
{
case 4:
case 5:
case 6:
case 7:
event->scroll.type = event->type = CLUTTER_SCROLL;
if (xbev->button == 4)
event->scroll.direction = CLUTTER_SCROLL_UP;
else if (xbev->button == 5)
event->scroll.direction = CLUTTER_SCROLL_DOWN;
else if (xbev->button == 6)
event->scroll.direction = CLUTTER_SCROLL_LEFT;
else
event->scroll.direction = CLUTTER_SCROLL_RIGHT;
event->scroll.time = xbev->time;
event->scroll.x = xbev->x;
event->scroll.y = xbev->y;
event->scroll.modifier_state = xbev->state;
event->scroll.device = _clutter_x11_get_device_for_xid (xbev->deviceid);
break;
default:
event->button.type = event->type = CLUTTER_BUTTON_PRESS;
event->button.time = xbev->time;
event->button.x = xbev->x;
event->button.y = xbev->y;
event->button.modifier_state = xbev->state;
event->button.button = xbev->button;
event->button.device = _clutter_x11_get_device_for_xid (xbev->deviceid);
break;
}
set_user_time (backend_x11, &xwindow, xbev->time);
}
else if (xevent->type
== ev_types[CLUTTER_X11_XINPUT_BUTTON_RELEASE_EVENT])
{
XDeviceButtonEvent *xbev = (XDeviceButtonEvent *)xevent;
CLUTTER_NOTE (EVENT, "XINPUT Button release event for %li at %d, %d",
xbev->deviceid,
xbev->x,
xbev->y);
/* scroll events don't have a corresponding release */
if (xbev->button == 4 ||
xbev->button == 5 ||
xbev->button == 6 ||
xbev->button == 7)
{
return FALSE;
}
event->button.type = event->type = CLUTTER_BUTTON_RELEASE;
event->button.time = xbev->time; event->button.time = xbev->time;
event->button.x = xbev->x; event->button.x = xbev->x;
event->button.y = xbev->y; event->button.y = xbev->y;
event->button.modifier_state = xbev->state; event->button.modifier_state = xbev->state;
event->button.button = xbev->button; event->button.button = xbev->button;
event->button.device = _clutter_x11_get_device_for_xid (xbev->deviceid); event->button.device = device;
} break;
else if (xevent->type
== ev_types [CLUTTER_X11_XINPUT_MOTION_NOTIFY_EVENT])
{
XDeviceMotionEvent *xmev = (XDeviceMotionEvent *)xevent;
CLUTTER_NOTE(EVENT, "XINPUT Motion event for %li at %d, %d",
xmev->deviceid,
xmev->x,
xmev->y);
event->motion.type = event->type = CLUTTER_MOTION;
event->motion.time = xmev->time;
event->motion.x = xmev->x;
event->motion.y = xmev->y;
event->motion.modifier_state = xmev->state;
event->motion.device = _clutter_x11_get_device_for_xid (xmev->deviceid);
}
#if 0
/* the Xinput handling of key presses/releases disabled for now since
* it makes keyrepeat, and key presses and releases outside the window
* not generate events even when the window has focus
*/
else if (xevent->type
== ev_types [CLUTTER_X11_XINPUT_KEY_PRESS_EVENT])
{
XEvent xevent_converted;
XDeviceKeyEvent *xkev = (XDeviceKeyEvent *)xevent;
convert_xdevicekey_to_xkey (xkev, &xevent_converted);
event->key.type = event->type = CLUTTER_KEY_PRESS;
translate_key_event (backend, event, &xevent_converted);
set_user_time (backend_x11, &xwindow, xkev->time);
}
else if (xevent->type
== ev_types [CLUTTER_X11_XINPUT_KEY_RELEASE_EVENT])
{
XEvent xevent_converted;
XDeviceKeyEvent *xkev = (XDeviceKeyEvent *)xevent;
convert_xdevicekey_to_xkey (xkev, &xevent_converted);
event->key.type = event->type = CLUTTER_KEY_RELEASE;
translate_key_event (backend, event, &xevent_converted);
} }
#endif
else set_user_time (backend_x11, &xwindow, xbev->time);
#endif /* HAVE_XINPUT */
res = TRUE;
}
else if (xevent->type == button_release)
{
XDeviceButtonEvent *xbev = (XDeviceButtonEvent *)xevent;
device = _clutter_x11_get_device_for_xid (xbev->deviceid);
_clutter_input_device_set_stage (device, stage);
CLUTTER_NOTE (EVENT, "XI ButtonRelease for %li ('%s') at %d, %d",
xbev->deviceid,
device->device_name,
xbev->x,
xbev->y);
/* scroll events don't have a corresponding release */
if (xbev->button == 4 ||
xbev->button == 5 ||
xbev->button == 6 ||
xbev->button == 7)
{ {
CLUTTER_NOTE (EVENT, "Uknown Event");
res = FALSE; res = FALSE;
goto out;
} }
event->button.type = event->type = CLUTTER_BUTTON_RELEASE;
event->button.time = xbev->time;
event->button.x = xbev->x;
event->button.y = xbev->y;
event->button.modifier_state = xbev->state;
event->button.button = xbev->button;
event->button.device = device;
res = TRUE;
}
else if (xevent->type == motion_notify)
{
XDeviceMotionEvent *xmev = (XDeviceMotionEvent *)xevent;
device = _clutter_x11_get_device_for_xid (xmev->deviceid);
_clutter_input_device_set_stage (device, stage);
CLUTTER_NOTE (EVENT, "XI Motion for %li ('%s') at %d, %d",
xmev->deviceid,
device->device_name,
xmev->x,
xmev->y);
event->motion.type = event->type = CLUTTER_MOTION;
event->motion.time = xmev->time;
event->motion.x = xmev->x;
event->motion.y = xmev->y;
event->motion.modifier_state = xmev->state;
event->motion.device = device;
res = TRUE;
}
else if (xevent->type == key_press || xevent->type == key_release)
{
/* the XInput 1.x handling of key presses/releases is broken:
* it makes key repeat, key presses and releases outside the
* window not generate events even when the window has focus
*/
XDeviceKeyEvent *xkev = (XDeviceKeyEvent *) xevent;
XEvent xevent_converted;
convert_xdevicekey_to_xkey (xkev, &xevent_converted);
event->key.type = event->type = (xevent->type == key_press)
? CLUTTER_KEY_PRESS
: CLUTTER_KEY_RELEASE;
translate_key_event (backend, event, &xevent_converted);
if (xevent->type == key_press)
set_user_time (backend_x11, &xwindow, xkev->time);
}
else
#endif /* HAVE_XINPUT */
{
CLUTTER_NOTE (EVENT, "Uknown Event");
res = FALSE;
} }
} }
out:
return res; return res;
} }

View File

@ -0,0 +1,221 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "clutter-input-device-x11.h"
#include "../clutter-debug.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
};
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);
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 */
}

View File

@ -0,0 +1,25 @@
#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__ */

View File

@ -121,7 +121,9 @@ gboolean clutter_x11_has_event_retrieval (void);
ClutterStage *clutter_x11_get_stage_from_window (Window win); ClutterStage *clutter_x11_get_stage_from_window (Window win);
#ifndef CLUTTER_DISABLE_DEPRECATED
G_CONST_RETURN GSList* clutter_x11_get_input_devices (void); G_CONST_RETURN GSList* clutter_x11_get_input_devices (void);
#endif
void clutter_x11_enable_xinput (void); void clutter_x11_enable_xinput (void);
gboolean clutter_x11_has_xinput (void); gboolean clutter_x11_has_xinput (void);

View File

@ -139,6 +139,8 @@
<xi:include href="xml/clutter-main.xml"/> <xi:include href="xml/clutter-main.xml"/>
<xi:include href="xml/clutter-shader.xml"/> <xi:include href="xml/clutter-shader.xml"/>
<xi:include href="xml/clutter-stage-manager.xml"/> <xi:include href="xml/clutter-stage-manager.xml"/>
<xi:include href="xml/clutter-input-device.xml"/>
<xi:include href="xml/clutter-device-manager.xml"/>
<xi:include href="xml/clutter-units.xml"/> <xi:include href="xml/clutter-units.xml"/>
<xi:include href="xml/clutter-util.xml"/> <xi:include href="xml/clutter-util.xml"/>
<xi:include href="xml/clutter-feature.xml"/> <xi:include href="xml/clutter-feature.xml"/>

View File

@ -404,6 +404,7 @@ clutter_actor_create_pango_layout
clutter_actor_is_in_clone_paint clutter_actor_is_in_clone_paint
clutter_actor_set_text_direction clutter_actor_set_text_direction
clutter_actor_get_text_direction clutter_actor_get_text_direction
clutter_actor_has_pointer
<SUBSECTION> <SUBSECTION>
ClutterActorBox ClutterActorBox
@ -940,7 +941,6 @@ ClutterMotionEvent
ClutterScrollEvent ClutterScrollEvent
ClutterStageStateEvent ClutterStageStateEvent
ClutterCrossingEvent ClutterCrossingEvent
ClutterInputDevice
clutter_event_new clutter_event_new
clutter_event_copy clutter_event_copy
clutter_event_free clutter_event_free
@ -977,13 +977,9 @@ clutter_event_get_related
clutter_event_get_scroll_direction clutter_event_get_scroll_direction
<SUBSECTION> <SUBSECTION>
ClutterInputDeviceType
clutter_event_get_device clutter_event_get_device
clutter_event_get_device_id clutter_event_get_device_id
clutter_event_get_device_type clutter_event_get_device_type
clutter_get_input_device_for_id
clutter_input_device_get_device_id
clutter_input_device_get_device_type
<SUBSECTION> <SUBSECTION>
clutter_get_current_event_time clutter_get_current_event_time
@ -996,6 +992,51 @@ ClutterAnyEvent
clutter_event_get_type clutter_event_get_type
</SECTION> </SECTION>
<SECTION>
<FILE>clutter-input-device</FILE>
<TITLE>ClutterInputDevice</TITLE>
ClutterInputDeviceType
ClutterInputDevice
ClutterInputDeviceClass
clutter_input_device_get_device_id
clutter_input_device_get_device_type
clutter_input_device_get_device_name
clutter_input_device_get_device_coords
clutter_input_device_get_pointer_actor
<SUBSECTION Standard>
CLUTTER_TYPE_INPUT_DEVICE
CLUTTER_INPUT_DEVICE
CLUTTER_INPUT_DEVICE_CLASS
CLUTTER_IS_INPUT_DEVICE
CLUTTER_IS_INPUT_DEVICE_CLASS
CLUTTER_INPUT_DEVICE_GET_CLASS
<SUBSECTION Private>
clutter_input_device_get_type
</SECTION>
<SECTION>
<FILE>clutter-device-manager</FILE>
<TITLE>ClutterDeviceManager</TITLE>
ClutterDeviceManager
clutter_device_manager_get_default
clutter_device_manager_list_devices
clutter_device_manager_peek_devices
clutter_device_manager_get_device
<SUBSECTION>
clutter_get_input_device_for_id
<SUBSECTION Standard>
CLUTTER_TYPE_DEVICE_MANAGER
CLUTTER_DEVICE_MANAGER
CLUTTER_IS_DEVICE_MANAGER
<SUBSECTION Private>
clutter_device_manager_get_type
</SECTION>
<SECTION> <SECTION>
<FILE>clutter-main</FILE> <FILE>clutter-main</FILE>
<TITLE>General</TITLE> <TITLE>General</TITLE>

View File

@ -40,3 +40,5 @@ clutter_fixed_layout_get_type
clutter_bin_layout_get_type clutter_bin_layout_get_type
clutter_flow_layout_get_type clutter_flow_layout_get_type
clutter_box_layout_get_type clutter_box_layout_get_type
clutter_input_device_get_type
clutter_device_manager_get_type

View File

@ -9,7 +9,31 @@ typedef struct {
} TestDevicesApp; } TestDevicesApp;
static const gchar *
device_type_name (ClutterInputDevice *device)
{
ClutterInputDeviceType d_type;
d_type = clutter_input_device_get_device_type (device);
switch (d_type)
{
case CLUTTER_POINTER_DEVICE:
return "Pointer";
case CLUTTER_KEYBOARD_DEVICE:
return "Keyboard";
case CLUTTER_EXTENSION_DEVICE:
return "Extension";
default:
return "Unknown";
}
g_warn_if_reached ();
return NULL;
}
static gboolean static gboolean
stage_motion_event_cb (ClutterActor *actor, stage_motion_event_cb (ClutterActor *actor,
@ -24,6 +48,11 @@ stage_motion_event_cb (ClutterActor *actor,
hand = g_hash_table_lookup (app->devices, device); hand = g_hash_table_lookup (app->devices, device);
g_print ("Device: '%s' (id:%d, type:%s)\n",
clutter_input_device_get_device_name (device),
clutter_input_device_get_device_id (device),
device_type_name (device));
if (hand != NULL) if (hand != NULL)
{ {
gfloat event_x, event_y; gfloat event_x, event_y;
@ -43,6 +72,7 @@ test_devices_main (int argc, char **argv)
ClutterActor *stage; ClutterActor *stage;
TestDevicesApp *app; TestDevicesApp *app;
ClutterColor stage_color = { 0x61, 0x64, 0x8c, 0xff }; ClutterColor stage_color = { 0x61, 0x64, 0x8c, 0xff };
ClutterDeviceManager *manager;
const GSList *stage_devices, *l; const GSList *stage_devices, *l;
/* force enabling X11 support */ /* force enabling X11 support */
@ -63,10 +93,11 @@ test_devices_main (int argc, char **argv)
clutter_actor_show_all (stage); clutter_actor_show_all (stage);
stage_devices = clutter_x11_get_input_devices (); manager = clutter_device_manager_get_default ();
stage_devices = clutter_device_manager_peek_devices (manager);
if (stage_devices == NULL) if (stage_devices == NULL)
g_error ("No extended input devices found."); g_error ("No input devices found.");
for (l = stage_devices; l != NULL; l = l->next) for (l = stage_devices; l != NULL; l = l->next)
{ {
@ -74,12 +105,15 @@ test_devices_main (int argc, char **argv)
ClutterInputDeviceType device_type; ClutterInputDeviceType device_type;
ClutterActor *hand = NULL; ClutterActor *hand = NULL;
device_type = clutter_input_device_get_device_type (device); g_print ("got a %s device '%s' with id %d...\n",
if (device_type == CLUTTER_POINTER_DEVICE) device_type_name (device),
{ clutter_input_device_get_device_name (device),
g_print ("got a pointer device with id %d...\n", clutter_input_device_get_device_id (device));
clutter_input_device_get_device_id (device));
device_type = clutter_input_device_get_device_type (device);
if (device_type == CLUTTER_POINTER_DEVICE ||
device_type == CLUTTER_EXTENSION_DEVICE)
{
hand = clutter_texture_new_from_file (TESTS_DATADIR hand = clutter_texture_new_from_file (TESTS_DATADIR
G_DIR_SEPARATOR_S G_DIR_SEPARATOR_S
"redhand.png", "redhand.png",

View File

@ -27,6 +27,12 @@ get_event_type_name (const ClutterEvent *event)
case CLUTTER_LEAVE: case CLUTTER_LEAVE:
return "LEAVE"; return "LEAVE";
case CLUTTER_MOTION:
return "MOTION";
case CLUTTER_DELETE:
return "DELETE";
default: default:
return "EVENT"; return "EVENT";
} }
@ -124,94 +130,122 @@ fill_keybuf (char *keybuf, ClutterKeyEvent *event)
/* printable character, if any (ß, ∑) */ /* printable character, if any (ß, ∑) */
len = g_unichar_to_utf8 (event->unicode_value, utf8); len = g_unichar_to_utf8 (event->unicode_value, utf8);
utf8[len] = '\0'; utf8[len] = '\0';
sprintf(keybuf, "'%s' ", utf8); sprintf (keybuf, "'%s' ", utf8);
/* key combination (<Mod1>s, <Shift><Mod1>S, <Ctrl><Mod1>Delete) */ /* key combination (<Mod1>s, <Shift><Mod1>S, <Ctrl><Mod1>Delete) */
len = g_unichar_to_utf8 (clutter_keysym_to_unicode (event->keyval), len = g_unichar_to_utf8 (clutter_keysym_to_unicode (event->keyval), utf8);
utf8);
utf8[len] = '\0'; utf8[len] = '\0';
if (event->modifier_state & CLUTTER_SHIFT_MASK) if (event->modifier_state & CLUTTER_SHIFT_MASK)
strcat (keybuf, "<Shift>"); strcat (keybuf, "<Shift>");
if (event->modifier_state & CLUTTER_LOCK_MASK) if (event->modifier_state & CLUTTER_LOCK_MASK)
strcat (keybuf, "<Lock>"); strcat (keybuf, "<Lock>");
if (event->modifier_state & CLUTTER_CONTROL_MASK) if (event->modifier_state & CLUTTER_CONTROL_MASK)
strcat (keybuf, "<Control>"); strcat (keybuf, "<Control>");
if (event->modifier_state & CLUTTER_MOD1_MASK) if (event->modifier_state & CLUTTER_MOD1_MASK)
strcat (keybuf, "<Mod1>"); strcat (keybuf, "<Mod1>");
if (event->modifier_state & CLUTTER_MOD2_MASK) if (event->modifier_state & CLUTTER_MOD2_MASK)
strcat (keybuf, "<Mod2>"); strcat (keybuf, "<Mod2>");
if (event->modifier_state & CLUTTER_MOD3_MASK) if (event->modifier_state & CLUTTER_MOD3_MASK)
strcat (keybuf, "<Mod3>"); strcat (keybuf, "<Mod3>");
if (event->modifier_state & CLUTTER_MOD4_MASK) if (event->modifier_state & CLUTTER_MOD4_MASK)
strcat (keybuf, "<Mod4>"); strcat (keybuf, "<Mod4>");
if (event->modifier_state & CLUTTER_MOD5_MASK) if (event->modifier_state & CLUTTER_MOD5_MASK)
strcat (keybuf, "<Mod5>"); strcat (keybuf, "<Mod5>");
strcat (keybuf, utf8); strcat (keybuf, utf8);
} }
static gboolean static gboolean
input_cb (ClutterActor *actor, input_cb (ClutterActor *actor,
ClutterEvent *event, ClutterEvent *event,
gpointer data) gpointer data)
{ {
ClutterStage *stage = CLUTTER_STAGE (clutter_stage_get_default ()); ClutterStage *stage = CLUTTER_STAGE (clutter_stage_get_default ());
gchar keybuf[128], *source = (gchar*)data; ClutterActor *source_actor = clutter_event_get_source (event);
gchar keybuf[128];
switch (event->type) switch (event->type)
{ {
case CLUTTER_KEY_PRESS: case CLUTTER_KEY_PRESS:
fill_keybuf (keybuf, &event->key); fill_keybuf (keybuf, &event->key);
printf ("[%s] KEY PRESS %s", source, keybuf); printf ("[%s] KEY PRESS %s",
clutter_actor_get_name (source_actor),
keybuf);
break; break;
case CLUTTER_KEY_RELEASE: case CLUTTER_KEY_RELEASE:
fill_keybuf (keybuf, &event->key); fill_keybuf (keybuf, &event->key);
printf ("[%s] KEY RELEASE %s", source, keybuf); printf ("[%s] KEY RELEASE %s",
clutter_actor_get_name (source_actor),
keybuf);
break; break;
case CLUTTER_MOTION: case CLUTTER_MOTION:
g_print ("[%s] MOTION", source); g_print ("[%s] MOTION",
clutter_actor_get_name (source_actor));
break; break;
case CLUTTER_ENTER: case CLUTTER_ENTER:
g_print ("[%s] ENTER", source); g_print ("[%s] ENTER (from:%s)",
clutter_actor_get_name (source_actor),
clutter_event_get_related (event) != NULL
? clutter_actor_get_name (clutter_event_get_related (event))
: "<out of stage>");
break; break;
case CLUTTER_LEAVE: case CLUTTER_LEAVE:
g_print ("[%s] LEAVE", source); g_print ("[%s] LEAVE (to:%s)",
clutter_actor_get_name (source_actor),
clutter_event_get_related (event) != NULL
? clutter_actor_get_name (clutter_event_get_related (event))
: "<out of stage>");
break; break;
case CLUTTER_BUTTON_PRESS: case CLUTTER_BUTTON_PRESS:
g_print ("[%s] BUTTON PRESS (click count:%i)", g_print ("[%s] BUTTON PRESS (button:%i, click count:%i)",
source, event->button.click_count); clutter_actor_get_name (source_actor),
clutter_event_get_button (event),
clutter_event_get_click_count (event));
break; break;
case CLUTTER_BUTTON_RELEASE: case CLUTTER_BUTTON_RELEASE:
g_print ("[%s] BUTTON RELEASE (click count:%i)", g_print ("[%s] BUTTON RELEASE (button:%i, click count:%i)",
source, event->button.click_count); clutter_actor_get_name (source_actor),
clutter_event_get_button (event),
clutter_event_get_click_count (event));
if (clutter_event_get_source (event) == CLUTTER_ACTOR (stage)) if (source_actor == CLUTTER_ACTOR (stage))
clutter_stage_set_key_focus (stage, NULL); clutter_stage_set_key_focus (stage, NULL);
else if (clutter_event_get_source (event) == actor else if (source_actor == actor &&
&& clutter_actor_get_parent (actor) == CLUTTER_ACTOR (stage)) clutter_actor_get_parent (actor) == CLUTTER_ACTOR (stage))
clutter_stage_set_key_focus (stage, actor); clutter_stage_set_key_focus (stage, actor);
break; break;
case CLUTTER_SCROLL: case CLUTTER_SCROLL:
g_print ("[%s] BUTTON SCROLL (click count:%i)", g_print ("[%s] BUTTON SCROLL (direction:%s)",
source, event->button.click_count); clutter_actor_get_name (source_actor),
clutter_event_get_scroll_direction (event) == CLUTTER_SCROLL_UP
? "up"
: "down");
break; break;
case CLUTTER_STAGE_STATE: case CLUTTER_STAGE_STATE:
g_print ("[%s] STAGE STATE", source); g_print ("[%s] STAGE STATE", clutter_actor_get_name (source_actor));
break; break;
case CLUTTER_DESTROY_NOTIFY: case CLUTTER_DESTROY_NOTIFY:
g_print ("[%s] DESTROY NOTIFY", source); g_print ("[%s] DESTROY NOTIFY", clutter_actor_get_name (source_actor));
break; break;
case CLUTTER_CLIENT_MESSAGE: case CLUTTER_CLIENT_MESSAGE:
g_print ("[%s] CLIENT MESSAGE", source); g_print ("[%s] CLIENT MESSAGE", clutter_actor_get_name (source_actor));
break; break;
case CLUTTER_DELETE: case CLUTTER_DELETE:
g_print ("[%s] DELETE", source); g_print ("[%s] DELETE", clutter_actor_get_name (source_actor));
break; break;
case CLUTTER_NOTHING: case CLUTTER_NOTHING:
return FALSE; return FALSE;
} }
if (clutter_event_get_source (event) == actor) if (source_actor == actor)
g_print (" *source*"); g_print (" *source*");
g_print ("\n"); g_print ("\n");
@ -234,8 +268,8 @@ test_events_main (int argc, char *argv[])
stage = clutter_stage_get_default (); stage = clutter_stage_get_default ();
clutter_actor_set_name (stage, "Stage");
g_signal_connect (stage, "event", G_CALLBACK (input_cb), "stage"); g_signal_connect (stage, "event", G_CALLBACK (input_cb), "stage");
g_signal_connect (stage, "fullscreen", g_signal_connect (stage, "fullscreen",
G_CALLBACK (stage_state_cb), "fullscreen"); G_CALLBACK (stage_state_cb), "fullscreen");
g_signal_connect (stage, "unfullscreen", g_signal_connect (stage, "unfullscreen",
@ -244,24 +278,21 @@ test_events_main (int argc, char *argv[])
G_CALLBACK (stage_state_cb), "activate"); G_CALLBACK (stage_state_cb), "activate");
g_signal_connect (stage, "deactivate", g_signal_connect (stage, "deactivate",
G_CALLBACK (stage_state_cb), "deactivate"); G_CALLBACK (stage_state_cb), "deactivate");
/*g_signal_connect (stage, "captured-event", G_CALLBACK (capture_cb), NULL);*/
g_signal_connect (stage, "captured-event", G_CALLBACK (capture_cb), NULL);
focus_box = clutter_rectangle_new_with_color (&ncol); focus_box = clutter_rectangle_new_with_color (&ncol);
clutter_actor_set_name (focus_box, "Focus Box");
clutter_container_add (CLUTTER_CONTAINER(stage), focus_box, NULL); clutter_container_add (CLUTTER_CONTAINER(stage), focus_box, NULL);
actor = clutter_rectangle_new_with_color (&rcol); actor = clutter_rectangle_new_with_color (&rcol);
clutter_actor_set_name (actor, "Red Box");
clutter_actor_set_size (actor, 100, 100); clutter_actor_set_size (actor, 100, 100);
clutter_actor_set_position (actor, 100, 100); clutter_actor_set_position (actor, 100, 100);
clutter_actor_set_reactive (actor, TRUE); clutter_actor_set_reactive (actor, TRUE);
clutter_container_add (CLUTTER_CONTAINER (stage), actor, NULL); clutter_container_add (CLUTTER_CONTAINER (stage), actor, NULL);
g_signal_connect (actor, "event", G_CALLBACK (input_cb), "red box"); g_signal_connect (actor, "event", G_CALLBACK (input_cb), "red box");
g_signal_connect (actor, "key-focus-in", G_CALLBACK (key_focus_in_cb), g_signal_connect (actor, "key-focus-in", G_CALLBACK (key_focus_in_cb),
focus_box); focus_box);
/* Toggle motion - enter/leave capture */ /* Toggle motion - enter/leave capture */
g_signal_connect (actor, "button-press-event", g_signal_connect (actor, "button-press-event",
G_CALLBACK (red_button_cb), NULL); G_CALLBACK (red_button_cb), NULL);
@ -269,27 +300,22 @@ test_events_main (int argc, char *argv[])
clutter_stage_set_key_focus (CLUTTER_STAGE (stage), actor); clutter_stage_set_key_focus (CLUTTER_STAGE (stage), actor);
actor = clutter_rectangle_new_with_color (&gcol); actor = clutter_rectangle_new_with_color (&gcol);
clutter_actor_set_name (actor, "Green Box");
clutter_actor_set_size (actor, 100, 100); clutter_actor_set_size (actor, 100, 100);
clutter_actor_set_position (actor, 250, 100); clutter_actor_set_position (actor, 250, 100);
clutter_actor_set_reactive (actor, TRUE); clutter_actor_set_reactive (actor, TRUE);
clutter_container_add (CLUTTER_CONTAINER (stage), actor, NULL); clutter_container_add (CLUTTER_CONTAINER (stage), actor, NULL);
g_signal_connect (actor, "event", G_CALLBACK (input_cb), "green box"); g_signal_connect (actor, "event", G_CALLBACK (input_cb), "green box");
g_signal_connect (actor, "key-focus-in", G_CALLBACK (key_focus_in_cb), g_signal_connect (actor, "key-focus-in", G_CALLBACK (key_focus_in_cb),
focus_box); focus_box);
g_signal_connect (actor, "captured-event", G_CALLBACK (capture_cb), NULL); g_signal_connect (actor, "captured-event", G_CALLBACK (capture_cb), NULL);
actor = clutter_rectangle_new_with_color (&bcol); actor = clutter_rectangle_new_with_color (&bcol);
clutter_actor_set_name (actor, "Blue Box");
clutter_actor_set_size (actor, 100, 100); clutter_actor_set_size (actor, 100, 100);
clutter_actor_set_position (actor, 400, 100); clutter_actor_set_position (actor, 400, 100);
clutter_actor_set_reactive (actor, TRUE); clutter_actor_set_reactive (actor, TRUE);
clutter_container_add (CLUTTER_CONTAINER(stage), actor, NULL); clutter_container_add (CLUTTER_CONTAINER(stage), actor, NULL);
g_signal_connect (actor, "event", G_CALLBACK (input_cb), "blue box"); g_signal_connect (actor, "event", G_CALLBACK (input_cb), "blue box");
g_signal_connect (actor, "key-focus-in", G_CALLBACK (key_focus_in_cb), g_signal_connect (actor, "key-focus-in", G_CALLBACK (key_focus_in_cb),
focus_box); focus_box);
@ -299,20 +325,19 @@ test_events_main (int argc, char *argv[])
/* non reactive */ /* non reactive */
actor = clutter_rectangle_new_with_color (&ncol); actor = clutter_rectangle_new_with_color (&ncol);
clutter_actor_set_name (actor, "Black Box");
clutter_actor_set_size (actor, 400, 50); clutter_actor_set_size (actor, 400, 50);
clutter_actor_set_position (actor, 100, 250); clutter_actor_set_position (actor, 100, 250);
clutter_container_add (CLUTTER_CONTAINER(stage), actor, NULL); clutter_container_add (CLUTTER_CONTAINER(stage), actor, NULL);
g_signal_connect (actor, "event", G_CALLBACK (input_cb), "blue box"); g_signal_connect (actor, "event", G_CALLBACK (input_cb), "blue box");
g_signal_connect (actor, "key-focus-in", G_CALLBACK (key_focus_in_cb), g_signal_connect (actor, "key-focus-in", G_CALLBACK (key_focus_in_cb),
focus_box); focus_box);
g_signal_connect (stage, "key-focus-in", G_CALLBACK (key_focus_in_cb), g_signal_connect (stage, "key-focus-in", G_CALLBACK (key_focus_in_cb),
focus_box); focus_box);
/* non reactive group, with reactive child */ /* non reactive group, with reactive child */
actor = clutter_rectangle_new_with_color (&ycol); actor = clutter_rectangle_new_with_color (&ycol);
clutter_actor_set_name (actor, "Yellow Box");
clutter_actor_set_size (actor, 100, 100); clutter_actor_set_size (actor, 100, 100);
clutter_actor_set_reactive (actor, TRUE); clutter_actor_set_reactive (actor, TRUE);