Port the X11 backend to the Device Manager

Use the device manager to store the input devices. Also, provide
two fallback devices when initializing the X11 backend: device 0
for the pointer and device 1 for the keyboard.
This commit is contained in:
Emmanuele Bassi 2009-11-20 15:36:43 +00:00
parent d34f1aa775
commit 3027d4327a
5 changed files with 257 additions and 275 deletions

View File

@ -153,9 +153,6 @@ struct _ClutterMainContext
PangoContext *pango_context; /* Global Pango context */
CoglPangoFontMap *font_map; /* Global font map */
GSList *input_devices; /* For extra input devices, i.e
MultiTouch */
ClutterEvent *current_event;
guint32 last_event_time;

View File

@ -46,15 +46,17 @@
#include <X11/extensions/XInput.h>
#endif
#include "cogl/cogl.h"
#include "../clutter-debug.h"
#include "../clutter-device-manager.h"
#include "../clutter-event.h"
#include "../clutter-main.h"
#include "../clutter-debug.h"
#include "../clutter-private.h"
#include "cogl/cogl.h"
G_DEFINE_TYPE (ClutterBackendX11, clutter_backend_x11, CLUTTER_TYPE_BACKEND);
/* a specific X11 input device */
struct _ClutterX11XInputDevice
{
ClutterInputDevice device;
@ -66,11 +68,6 @@ struct _ClutterX11XInputDevice
#endif
};
#ifdef HAVE_XINPUT
void _clutter_x11_register_xinput ();
#endif
/* atoms; remember to add the code that assigns the atom value to
* the member of the ClutterBackendX11 structure if you add an
* atom name here. do not change the order!
@ -109,6 +106,222 @@ static gboolean clutter_synchronise = FALSE;
static int TrappedErrorCode = 0;
static int (* old_error_handler) (Display *, XErrorEvent *);
static void
clutter_x11_register_input_devices (ClutterBackendX11 *backend)
{
ClutterDeviceManager *manager;
gboolean have_an_xpointer = FALSE;
#ifdef HAVE_XINPUT
XDeviceInfo *x_devices = NULL;
int res, opcode, event, error;
int i, n_devices;
GSList *devices = NULL;
#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;
}
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)
{
ClutterInputDevice *device;
XDevice *x_device;
int n_events = 0;
int j;
clutter_x11_trap_x_errors ();
x_device = XOpenDevice (backend->xdpy, info->id);
if (clutter_x11_untrap_x_errors () || x_device == NULL)
{
CLUTTER_NOTE (BACKEND, "Unable to open device %li", info->id);
continue;
}
device = g_slice_new0 (ClutterX11XInputDevice);
device->device.id = info->id;
device->device.click_count = 0;
device->device.previous_time = CLUTTER_CURRENT_TIME;
device->device.previous_x = -1;
device->device.previous_y = -1;
device->device.previous_button_number = -1;
switch (info->use)
{
case IsXExtensionPointer:
device->device.device_type = CLUTTER_POINTER_DEVICE;
have_an_xpointer = TRUE;
break;
#if 0
/* XInput1 is broken for keyboards */
case IsXExtensionKeyboard:
device->device.device_type = CLUTTER_KEYBOARD_DEVICE;
break;
#endif
case IsXExtensionDevice:
device->device.device_type = CLUTTER_EXTENSION_DEVICE;
break;
}
device->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 (j = 0; j < xdevice->num_classes; j++)
{
XInputClassInfo *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]);
n_events++;
DeviceKeyRelease (xdevice,
x11b->event_types[CLUTTER_X11_XINPUT_KEY_RELEASE_EVENT],
device->xevent_list [num_events]);
n_events++;
break;
#endif
case ButtonClass:
DeviceButtonPress (xdevice,
x11b->event_types[CLUTTER_X11_XINPUT_BUTTON_PRESS_EVENT],
device->xevent_list [num_events]);
n_events++;
DeviceButtonRelease (xdevice,
x11b->event_types[CLUTTER_X11_XINPUT_BUTTON_RELEASE_EVENT],
device->xevent_list [num_events]);
n_events++;
break;
case ValuatorClass:
DeviceMotionNotify (xdevice,
x11b->event_types[CLUTTER_X11_XINPUT_MOTION_NOTIFY_EVENT],
device->xevent_list [num_events]);
n_events++;
break;
}
}
if (info->use == IsXExtensionPointer && num_events > 0)
have_an_xpointer = TRUE;
device->num_events = n_events;
/* add it to a temporary list; we don't add the device
* straight to the device manager because the XInput
* initialization might still fail
*/
devices = g_slist_prepend (devices, device);
}
}
XFree (x_devices);
devices = g_slist_reverse (devices);
if (have_an_xpointer)
{
GSList *l;
for (l = devices; l != NULL; l = l->next)
_clutter_device_manager_add_device (manager, l->data);
}
else
{
GSList *l;
g_warning ("No usable XInput pointer devices found");
for (l = devices; l != NULL; l = l->next)
g_slice_free (ClutterX11XInputDevice, l->data);
}
g_slist_free (devices);
backend->have_xinput = TRUE;
#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 do not have a XInput pointer device
*
* we register two default devices, one for the pointer
* and one for the keyboard
*/
if (!have_an_xpointer)
{
ClutterInputDevice *d;
d = g_slice_new0 (ClutterInputDevice);
d->id = 0;
d->device_type = CLUTTER_POINTER_DEVICE;
d->click_count = 0;
d->previous_time = CLUTTER_CURRENT_TIME;
d->previous_x = -1;
d->previous_y = -1;
d->previous_button_number = -1;
_clutter_device_manager_add_device (manager, d);
d = g_slice_new0 (ClutterInputDevice);
d->id = 1;
d->device_type = CLUTTER_KEYBOARD_DEVICE;
_clutter_device_manager_add_device (manager, d);
}
}
gboolean
clutter_backend_x11_pre_parse (ClutterBackend *backend,
GError **error)
@ -199,9 +412,8 @@ clutter_backend_x11_post_parse (ClutterBackend *backend,
clutter_backend_set_resolution (backend, dpi);
#ifdef HAVE_XINPUT
_clutter_x11_register_xinput ();
#endif
/* register input devices */
clutter_x11_register_input_devices (backend_x11);
if (clutter_synchronise)
XSynchronize (backend_x11->xdpy, True);
@ -644,234 +856,27 @@ 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
_clutter_x11_select_events (Window xwin)
{
#ifdef HAVE_XINPUT
GSList *list_it;
ClutterX11XInputDevice *device = NULL;
ClutterDeviceManager *manager;
const GSList *l;
ClutterMainContext *context;
context = _clutter_context_get_default ();
if (!backend_singleton)
if (G_UNLIKELY (backend_singleton == NULL))
{
g_critical ("X11 backend has not been initialised");
return;
}
for (list_it = context->input_devices;
list_it != NULL;
list_it = list_it->next)
manager = clutter_device_manager_get_default ();
for (l = clutter_device_manager_peek_devices (manager);
l != NULL;
l = l->next)
{
device = (ClutterX11XInputDevice *)list_it->data;
ClutterX11XInputDevice *device = l->data;
XSelectExtensionEvent (backend_singleton->xdpy,
xwin,
@ -884,49 +889,34 @@ _clutter_x11_select_events (Window xwin)
ClutterInputDevice *
_clutter_x11_get_device_for_xid (XID id)
{
#ifdef HAVE_XINPUT
ClutterMainContext *context;
GSList *l;
ClutterDeviceManager *manager;
context = _clutter_context_get_default ();
manager = clutter_device_manager_get_default ();
if (!backend_singleton)
{
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;
return clutter_device_manager_get_device (manager, (gint) id);
}
/* 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 *
clutter_x11_get_input_devices (void)
{
#ifdef HAVE_XINPUT
ClutterMainContext *context;
ClutterDeviceManager *manager;
if (!backend_singleton)
{
g_critical ("X11 backend has not been initialised");
return NULL;
}
manager = clutter_device_manager_get_default ();
context = _clutter_context_get_default ();
return context->input_devices;
#else /* !HAVE_XINPUT */
return NULL;
#endif /* HAVE_XINPUT */
return clutter_device_manager_peek_devices (manager);
}
/**
@ -943,7 +933,7 @@ gboolean
clutter_x11_has_xinput (void)
{
#ifdef HAVE_XINPUT
if (!backend_singleton)
if (backend_singleton == NULL)
{
g_critical ("X11 backend has not been initialised");
return FALSE;

View File

@ -125,12 +125,6 @@ clutter_backend_x11_get_features (ClutterBackend *backend);
XVisualInfo *
clutter_backend_x11_get_visual_info (ClutterBackendX11 *backend_x11);
void
_clutter_x11_register_xinput (void);
void
_clutter_x11_unregister_xinput (void);
ClutterInputDevice *
_clutter_x11_get_device_for_xid (XID id);

View File

@ -696,8 +696,8 @@ event_translate (ClutterBackend *backend,
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;
@ -705,10 +705,9 @@ event_translate (ClutterBackend *backend,
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;

View File

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