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:
parent
d34f1aa775
commit
3027d4327a
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
return clutter_device_manager_get_device (manager, (gint) id);
|
||||
}
|
||||
|
||||
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 *
|
||||
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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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,7 +705,6 @@ event_translate (ClutterBackend *backend,
|
||||
event->button.y = xevent->xbutton.y;
|
||||
event->button.modifier_state = xevent->xbutton.state;
|
||||
event->button.button = xevent->xbutton.button;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user