From b24148158680620dd59e73c827db547f248b1a7c Mon Sep 17 00:00:00 2001 From: Matthew Allum Date: Mon, 23 Jun 2008 09:55:42 +0000 Subject: [PATCH] 2008-06-23 Matthew Allum * clutter/clutter-actor.c: * clutter/clutter-actor.h: * clutter/clutter-event.c: * clutter/clutter-event.h: * clutter/clutter-main.c: * clutter/clutter-main.h: * clutter/clutter-private.h: * clutter/eglx/clutter-stage-egl.c: * clutter/fruity/clutter-backend-fruity.c: * clutter/fruity/clutter-backend-fruity.h: * clutter/fruity/clutter-fruity.c: * clutter/glx/clutter-stage-glx.c: * clutter/x11/clutter-backend-x11.c: * clutter/x11/clutter-backend-x11.h: * clutter/x11/clutter-event-x11.c: * clutter/x11/clutter-stage-x11.h: * clutter/x11/clutter-x11.h: * configure.ac: * tests/Makefile.am: * tests/test-devices.c: Merge of 'xinput' branch giving initial basic support of multiple input devices. --- ChangeLog | 25 ++ clutter/clutter-actor.c | 7 +- clutter/clutter-actor.h | 2 + clutter/clutter-event.c | 48 ++++ clutter/clutter-event.h | 1 + clutter/clutter-main.c | 175 +++++++++++-- clutter/clutter-main.h | 8 + clutter/clutter-private.h | 20 +- clutter/eglx/clutter-stage-egl.c | 33 ++- clutter/fruity/clutter-backend-fruity.c | 23 ++ clutter/fruity/clutter-backend-fruity.h | 10 + clutter/fruity/clutter-fruity.c | 246 ++++++++++++++++++ clutter/glx/clutter-stage-glx.c | 33 ++- clutter/x11/clutter-backend-x11.c | 277 ++++++++++++++++++++ clutter/x11/clutter-backend-x11.h | 23 ++ clutter/x11/clutter-event-x11.c | 323 ++++++++++++++++++------ clutter/x11/clutter-stage-x11.h | 7 + clutter/x11/clutter-x11.h | 27 ++ configure.ac | 19 ++ tests/Makefile.am | 2 + tests/test-devices.c | 76 ++++++ 21 files changed, 1257 insertions(+), 128 deletions(-) create mode 100644 tests/test-devices.c diff --git a/ChangeLog b/ChangeLog index ede014d60..6d4e868b9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,28 @@ +2008-06-23 Matthew Allum + + * clutter/clutter-actor.c: + * clutter/clutter-actor.h: + * clutter/clutter-event.c: + * clutter/clutter-event.h: + * clutter/clutter-main.c: + * clutter/clutter-main.h: + * clutter/clutter-private.h: + * clutter/eglx/clutter-stage-egl.c: + * clutter/fruity/clutter-backend-fruity.c: + * clutter/fruity/clutter-backend-fruity.h: + * clutter/fruity/clutter-fruity.c: + * clutter/glx/clutter-stage-glx.c: + * clutter/x11/clutter-backend-x11.c: + * clutter/x11/clutter-backend-x11.h: + * clutter/x11/clutter-event-x11.c: + * clutter/x11/clutter-stage-x11.h: + * clutter/x11/clutter-x11.h: + * configure.ac: + * tests/Makefile.am: + * tests/test-devices.c: + Merge of 'xinput' branch giving initial basic support of + multiple input devices. + 2008-06-23 Matthew Allum * clutter/clutter-actor.c: diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c index 6d6526201..a8f613a20 100644 --- a/clutter/clutter-actor.c +++ b/clutter/clutter-actor.c @@ -7293,9 +7293,10 @@ clutter_actor_get_stage (ClutterActor *actor) * * This function is a utility call for #ClutterActor implementations * that allocates the actor's preferred natural size. It can be used - * by fixed layout managers (like #ClutterGroup) inside the - * ClutterActor::allocate implementation to give each child exactly - * how much space it requires. + * by fixed layout managers (like #ClutterGroup or so called + * 'composite actors') inside the ClutterActor::allocate + * implementation to give each child exactly how much space it + * requires. * * This function is not meant to be used by applications. It is also * not meant to be used outside the implementation of the diff --git a/clutter/clutter-actor.h b/clutter/clutter-actor.h index 53b296d6a..82ad2a964 100644 --- a/clutter/clutter-actor.h +++ b/clutter/clutter-actor.h @@ -550,6 +550,8 @@ void clutter_actor_apply_relative_transform_to_point (ClutterActor *self, ClutterActor *ancestor, ClutterVertex *point, ClutterVertex *vertex); +void clutter_actor_allocate_preferred_size (ClutterActor *actor, + gboolean absolute_origin_changed); G_END_DECLS diff --git a/clutter/clutter-event.c b/clutter/clutter-event.c index 967718111..2ac3ea653 100644 --- a/clutter/clutter-event.c +++ b/clutter/clutter-event.c @@ -315,6 +315,54 @@ clutter_keysym_to_unicode (guint keyval) return 0; } +/** + * clutter_event_get_device_id: + * @event: a clutter event + * + * Retrieves the events device id if set. + * + * Return value: A unique identifier for the device or -1 if the event has + * no specific device set. + **/ +gint +clutter_event_get_device_id (ClutterEvent *event) +{ + g_return_val_if_fail (-1, event != NULL); + + ClutterInputDevice *device = NULL; + + switch (event->type) + { + case CLUTTER_NOTHING: + case CLUTTER_STAGE_STATE: + case CLUTTER_DESTROY_NOTIFY: + case CLUTTER_CLIENT_MESSAGE: + case CLUTTER_DELETE: + case CLUTTER_ENTER: + case CLUTTER_LEAVE: + break; + case CLUTTER_BUTTON_PRESS: + case CLUTTER_BUTTON_RELEASE: + device = event->button.device; + break; + case CLUTTER_MOTION: + device = event->motion.device; + break; + case CLUTTER_SCROLL: + device = event->scroll.device; + break; + case CLUTTER_KEY_PRESS: + case CLUTTER_KEY_RELEASE: + break; + } + + if (device) + return device->id; + else + return -1; +} + + GType clutter_event_get_type (void) { diff --git a/clutter/clutter-event.h b/clutter/clutter-event.h index 9cbdcb4b7..32675be1c 100644 --- a/clutter/clutter-event.h +++ b/clutter/clutter-event.h @@ -350,6 +350,7 @@ ClutterModifierType clutter_event_get_state (ClutterEvent *event); void clutter_event_get_coords (ClutterEvent *event, gint *x, gint *y); +gint clutter_event_get_device_id (ClutterEvent *event); ClutterActor* clutter_event_get_source (ClutterEvent *event); guint clutter_key_event_symbol (ClutterKeyEvent *keyev); diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index 6d668ba5c..bc3cbd461 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -1415,6 +1415,15 @@ event_click_count_generate (ClutterEvent *event) double_click_distance = clutter_backend_get_double_click_distance (backend); double_click_time = clutter_backend_get_double_click_time (backend); + if (event->button.device != NULL) + { + click_count = event->button.device->click_count; + previous_x = event->button.device->previous_x; + previous_y = event->button.device->previous_y; + previous_time = event->button.device->previous_time; + previous_button_number = event->button.device->previous_button_number; + } + switch (event->type) { case CLUTTER_BUTTON_PRESS: @@ -1449,6 +1458,15 @@ event_click_count_generate (ClutterEvent *event) default: g_assert (NULL); } + + if (event->button.device != NULL) + { + event->button.device->click_count = click_count; + event->button.device->previous_x = previous_x; + event->button.device->previous_y = previous_y; + event->button.device->previous_time = previous_time; + event->button.device->previous_button_number = previous_button_number; + } } @@ -1546,11 +1564,14 @@ emit_keyboard_event (ClutterEvent *event) } static void -unset_motion_last_actor (ClutterActor *actor) +unset_motion_last_actor (ClutterActor *actor, ClutterInputDevice *dev) { ClutterMainContext *context = ClutterCntx; - context->motion_last_actor = NULL; + if (dev == NULL) + context->motion_last_actor = NULL; + else + dev->motion_last_actor = NULL; } static inline void @@ -1558,8 +1579,12 @@ generate_enter_leave_events (ClutterEvent *event) { ClutterMainContext *context = ClutterCntx; ClutterActor *motion_current_actor = event->motion.source; + ClutterActor *last_actor = context->motion_last_actor; - if (context->motion_last_actor != motion_current_actor) + if (event->motion.device != NULL) + last_actor = event->motion.device->motion_last_actor; + + if (last_actor != motion_current_actor) { if (motion_current_actor) { @@ -1572,7 +1597,7 @@ generate_enter_leave_events (ClutterEvent *event) cev.crossing.flags = 0; cev.crossing.x = event->motion.x; cev.crossing.y = event->motion.y; - cev.crossing.source = context->motion_last_actor; + cev.crossing.source = last_actor; cev.crossing.stage = event->any.stage; /* unref in free */ cev.crossing.related = motion_current_actor; @@ -1590,7 +1615,7 @@ generate_enter_leave_events (ClutterEvent *event) cev.crossing.stage = event->any.stage; if (context->motion_last_actor) - cev.crossing.related = context->motion_last_actor; + cev.crossing.related = last_actor; else { /* the previous actor we were getting events from seems to have @@ -1604,23 +1629,25 @@ generate_enter_leave_events (ClutterEvent *event) } } - if (context->motion_last_actor && - context->motion_last_actor != motion_current_actor) + if (last_actor && last_actor != motion_current_actor) { - g_signal_handlers_disconnect_by_func (context->motion_last_actor, - G_CALLBACK (unset_motion_last_actor), - NULL); + g_signal_handlers_disconnect_by_func + (last_actor, + G_CALLBACK (unset_motion_last_actor), + event->motion.device); } - if (motion_current_actor && - context->motion_last_actor != motion_current_actor) + if (motion_current_actor && last_actor != motion_current_actor) { g_signal_connect (motion_current_actor, "destroy", G_CALLBACK (unset_motion_last_actor), - NULL); + event->motion.device); } - context->motion_last_actor = motion_current_actor; + if (event->motion.device != NULL) + event->motion.device->motion_last_actor = motion_current_actor; + else + context->motion_last_actor = motion_current_actor; } /** @@ -1641,7 +1668,9 @@ clutter_do_event (ClutterEvent *event) ClutterMainContext *context; ClutterBackend *backend; ClutterActor *stage; + ClutterInputDevice *device = NULL; static gint32 motion_last_time = 0L; + gint32 local_motion_time; context = clutter_context_get_default (); backend = context->backend; @@ -1699,6 +1728,12 @@ clutter_do_event (ClutterEvent *event) break; case CLUTTER_MOTION: + device = event->motion.device; + + if (device) + local_motion_time = device->motion_last_time; + else + local_motion_time = motion_last_time; /* avoid rate throttling for synthetic motion events or if * the per-actor events are disabled @@ -1716,17 +1751,22 @@ clutter_do_event (ClutterEvent *event) CLUTTER_NOTE (EVENT, "skip motion event: %s (last:%d, delta:%d, time:%d)", - (event->any.time < (motion_last_time + delta) ? "yes" : "no"), - motion_last_time, + (event->any.time < (local_motion_time + delta) ? "yes" : "no"), + local_motion_time, delta, event->any.time); - if (event->any.time < (motion_last_time + delta)) + if (event->any.time < (local_motion_time + delta)) break; else - motion_last_time = event->any.time; + local_motion_time = event->any.time; } + if (device) + device->motion_last_time = local_motion_time; + else + motion_last_time = local_motion_time; + /* Only stage gets motion events if clutter_set_motion_events is TRUE, * and the event is not a synthetic event with source set. */ @@ -1736,9 +1776,17 @@ clutter_do_event (ClutterEvent *event) /* Only stage gets motion events */ event->any.source = stage; + /* global grabs */ if (context->pointer_grab_actor != NULL) { - clutter_actor_event (context->pointer_grab_actor, event, FALSE); + clutter_actor_event (context->pointer_grab_actor, + event, FALSE); + break; + } + else if (device != NULL && device->pointer_grab_actor != NULL) + { + clutter_actor_event (device->pointer_grab_actor, + event, FALSE); break; } @@ -1774,7 +1822,8 @@ clutter_do_event (ClutterEvent *event) { if (event->type == CLUTTER_BUTTON_RELEASE) { - CLUTTER_NOTE (EVENT,"Release off stage received at %i, %i", + CLUTTER_NOTE (EVENT, + "Release off stage received at %i, %i", x, y); event->button.source = stage; @@ -1922,12 +1971,21 @@ static void on_pointer_grab_weak_notify (gpointer data, GObject *where_the_object_was) { + ClutterInputDevice *dev = (ClutterInputDevice *)data; ClutterMainContext *context; - + context = clutter_context_get_default (); - context->pointer_grab_actor = NULL; - clutter_ungrab_pointer (); + if (dev) + { + dev->pointer_grab_actor = NULL; + clutter_ungrab_pointer_for_device (dev->id); + } + else + { + context->pointer_grab_actor = NULL; + clutter_ungrab_pointer (); + } } /** @@ -1971,6 +2029,48 @@ clutter_grab_pointer (ClutterActor *actor) } } +void +clutter_grab_pointer_for_device (ClutterActor *actor, + gint id) +{ + ClutterInputDevice *dev; + + g_return_if_fail (actor == NULL || CLUTTER_IS_ACTOR (actor)); + + /* essentially a global grab */ + if (id == -1) + { + clutter_grab_pointer (actor); + return; + } + + dev = clutter_get_input_device_for_id (id); + + if (!dev) + return; + + if (dev->pointer_grab_actor == actor) + return; + + if (dev->pointer_grab_actor) + { + g_object_weak_unref (G_OBJECT (dev->pointer_grab_actor), + on_pointer_grab_weak_notify, + dev); + dev->pointer_grab_actor = NULL; + } + + if (actor) + { + dev->pointer_grab_actor = actor; + + g_object_weak_ref (G_OBJECT (actor), + on_pointer_grab_weak_notify, + dev); + } +} + + /** * clutter_ungrab_pointer: * @@ -1984,6 +2084,13 @@ clutter_ungrab_pointer (void) clutter_grab_pointer (NULL); } +void +clutter_ungrab_pointer_for_device (gint id) +{ + clutter_grab_pointer_for_device (NULL, id); +} + + /** * clutter_get_pointer_grab: * @@ -2200,3 +2307,25 @@ clutter_get_use_mipmapped_text (void) return FALSE; } + +ClutterInputDevice* +clutter_get_input_device_for_id (gint id) +{ + GSList *item; + ClutterInputDevice *device = NULL; + ClutterMainContext *context; + + context = clutter_context_get_default (); + + for (item = context->input_devices; + item != NULL; + item = item->next) + { + device = (ClutterInputDevice *)item->data; + + if (device->id == id) + return device; + } + + return NULL; +} diff --git a/clutter/clutter-main.h b/clutter/clutter-main.h index 149236369..5bead3305 100644 --- a/clutter/clutter-main.h +++ b/clutter/clutter-main.h @@ -131,6 +131,14 @@ void clutter_clear_glyph_cache (void); void clutter_set_use_mipmapped_text (gboolean value); gboolean clutter_get_use_mipmapped_text (void); +ClutterInputDevice* clutter_get_input_device_for_id (gint id); + +void clutter_grab_pointer_for_device (ClutterActor *actor, + gint id); + +void clutter_ungrab_pointer_for_device (gint id); + + G_END_DECLS #endif /* _HAVE_CLUTTER_MAIN_H */ diff --git a/clutter/clutter-private.h b/clutter/clutter-private.h index 03f50a363..2d20f80ef 100644 --- a/clutter/clutter-private.h +++ b/clutter/clutter-private.h @@ -69,6 +69,20 @@ typedef enum { CLUTTER_PICK_ALL } ClutterPickMode; +struct _ClutterInputDevice +{ + gint id; + gint32 motion_last_time; + ClutterActor *pointer_grab_actor; + ClutterActor *motion_last_actor; + + gint click_count; + gint previous_x; + gint previous_y; + guint32 previous_time; + gint previous_button_number; +}; + typedef struct _ClutterMainContext ClutterMainContext; struct _ClutterMainContext @@ -83,8 +97,7 @@ struct _ClutterMainContext ClutterPickMode pick_mode; /* Indicates pick render mode */ - guint motion_events_per_actor : 1;/* set f -or enter/leave events */ + guint motion_events_per_actor : 1;/* set for enter/leave events */ guint motion_frequency; /* Motion events per second */ gint num_reactives; /* Num of reactive actors */ @@ -110,6 +123,9 @@ or enter/leave events */ gint fb_r_mask_used, fb_g_mask_used, fb_b_mask_used; PangoClutterFontMap *font_map; /* Global font map */ + + GSList *input_devices; /* For extra input devices, i.e + MultiTouch */ }; #define CLUTTER_CONTEXT() (clutter_context_get_default ()) diff --git a/clutter/eglx/clutter-stage-egl.c b/clutter/eglx/clutter-stage-egl.c index e17551ce6..bf68fc7ed 100644 --- a/clutter/eglx/clutter-stage-egl.c +++ b/clutter/eglx/clutter-stage-egl.c @@ -76,6 +76,7 @@ clutter_stage_egl_realize (ClutterActor *actor) ClutterStageEGL *stage_egl = CLUTTER_STAGE_EGL (actor); ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (actor); ClutterBackendEGL *backend_egl; + ClutterBackendX11 *backend_x11; EGLConfig configs[2]; EGLint config_count; EGLBoolean status; @@ -86,6 +87,7 @@ clutter_stage_egl_realize (ClutterActor *actor) g_object_get (stage_x11->wrapper, "offscreen", &is_offscreen, NULL); backend_egl = CLUTTER_BACKEND_EGL (clutter_get_default_backend ()); + backend_x11 = CLUTTER_BACKEND_X11 (clutter_get_default_backend ()); if (G_LIKELY (!is_offscreen)) { @@ -168,16 +170,27 @@ clutter_stage_egl_realize (ClutterActor *actor) WhitePixel (stage_x11->xdpy, stage_x11->xscreen)); - XSelectInput (stage_x11->xdpy, stage_x11->xwin, - StructureNotifyMask - | ExposureMask - /* FIXME: we may want to eplicity enable MotionMask */ - | PointerMotionMask - | KeyPressMask - | KeyReleaseMask - | ButtonPressMask - | ButtonReleaseMask - | PropertyChangeMask); + if (clutter_x11_has_xinput()) + { + XSelectInput (stage_x11->xdpy, stage_x11->xwin, + StructureNotifyMask | + FocusChangeMask | + ExposureMask | + PropertyChangeMask); +#ifdef USE_XINPUT + _clutter_x11_select_events (stage_x11->xwin); +#endif + } + else + XSelectInput (stage_x11->xdpy, stage_x11->xwin, + StructureNotifyMask | + FocusChangeMask | + ExposureMask | + /* FIXME: we may want to eplicity enable MotionMask */ + PointerMotionMask | + KeyPressMask | KeyReleaseMask | + ButtonPressMask | ButtonReleaseMask | + PropertyChangeMask); /* FIXME, do these in a clutterstage_x11_realise? */ clutter_stage_x11_fix_window_size (stage_x11); diff --git a/clutter/fruity/clutter-backend-fruity.c b/clutter/fruity/clutter-backend-fruity.c index b182e7e77..93263b7d7 100644 --- a/clutter/fruity/clutter-backend-fruity.c +++ b/clutter/fruity/clutter-backend-fruity.c @@ -213,10 +213,33 @@ static void clutter_backend_egl_init (ClutterBackendEGL *backend_egl) { ClutterBackend *backend = CLUTTER_BACKEND (backend_egl); + ClutterMainContext *context; + int i; clutter_backend_set_resolution (backend, 96.0); clutter_backend_set_double_click_time (backend, 250); clutter_backend_set_double_click_distance (backend, 5); + + context = clutter_context_get_default (); + +#define MAX_FINGERS 5 + + for (i=0; iinput_devices = g_slist_append (context->input_devices, device); + + device->device.id = i; + 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->x = 0; + device->y = 0; + } } GType diff --git a/clutter/fruity/clutter-backend-fruity.h b/clutter/fruity/clutter-backend-fruity.h index 817f8d5eb..0336a45b5 100644 --- a/clutter/fruity/clutter-backend-fruity.h +++ b/clutter/fruity/clutter-backend-fruity.h @@ -33,6 +33,7 @@ #include #include +#include G_BEGIN_DECLS #define CLUTTER_TYPE_BACKEND_FRUITY (clutter_backend_egl_get_type ()) @@ -44,7 +45,14 @@ G_BEGIN_DECLS typedef struct _ClutterBackendEGL ClutterBackendEGL; typedef struct _ClutterBackendEGLClass ClutterBackendEGLClass; +typedef struct _ClutterFruityFingerDevice ClutterFruityFingerDevice; +struct _ClutterFruityFingerDevice +{ + ClutterInputDevice device; + int x, y; + gboolean is_down; +}; struct _ClutterBackendEGL { @@ -64,6 +72,8 @@ struct _ClutterBackendEGL /* event source */ GSource *event_source; + int num_fingers; + /*< private >*/ }; diff --git a/clutter/fruity/clutter-fruity.c b/clutter/fruity/clutter-fruity.c index 32be9b08d..953581666 100644 --- a/clutter/fruity/clutter-fruity.c +++ b/clutter/fruity/clutter-fruity.c @@ -50,6 +50,191 @@ static CoreSurfaceBufferRef CreateSurface(int w, int h) @implementation StageView +struct GSPathPoint { + char unk0; + char unk1; + short int status; + int unk2; + float x; + float y; +}; + +typedef struct { + int unk0; + int unk1; + int type; + int subtype; + float unk2; + float unk3; + float x; + float y; + int timestamp1; + int timestamp2; + int unk4; + int modifierFlags; + int unk5; + int unk6; + int mouseEvent; + short int dx; + short int fingerCount; + int unk7; + int unk8; + char unk9; + char numPoints; + short int unk10; + struct GSPathPoint points[10]; +} MEvent; + + +- (void)doEvent:(GSEvent*)gs_event +{ + ClutterBackendEGL *ba = CLUTTER_BACKEND_EGL (clutter_get_default_backend()); + int i, j; + ClutterMainContext *context; + ClutterStage *stage = CLUTTER_STAGE_EGL(ba->stage)->wrapper; + int n_fingers = 0; + MEvent *event = (MEvent*)gs_event; + + context = clutter_context_get_default (); + + bool mapped[5] = {false, false, false, false, false}; /* an event has been mapped to this device */ + int evs[5] = {0,0,0,0,0}; + + for (i = 0; i < event->fingerCount; i++) + { + bool found = false; + + /* NSLog(@"IncomingEvent: %d, pos: %f, %f", i, event->points[i].x, event->points[i].y);*/ + + /* check if this finger maps to one of the existing devices */ + for (j = 0; j < 5; j++) + { + ClutterFruityFingerDevice *dev; + + if (mapped[j]) + continue; /* we're already using device j */ + + dev = g_slist_nth_data (context->input_devices, j); + + if (!dev->is_down) + continue; /* device isn't down we cannot really match against it */ + + int dist = ABS(event->points[i].x - dev->x) + + ABS(event->points[i].y - dev->y); + if (dist < 20) + { + found = true; + mapped[j] = true; + + if (dist >= 1) + { + dev->x = event->points[i].x; + dev->y = event->points[i].y; + // MOUSEMOVE + /*NSLog(@"MouseMove: %d, pos: %d, %d", j, dev->x, dev->y);*/ + evs[j] = 3; + } + break; + } + } + + if (!found) + { + ClutterFruityFingerDevice *dev; + + for (j = 0; j < 5 /*n_fingers*/; j++) + { + dev = g_slist_nth_data (context->input_devices, j); + if (!dev->is_down) + break; + } + + dev->x = event->points[i].x; + dev->y = event->points[i].y; + dev->is_down = TRUE; + + mapped[j] = true; + + // MOUSEDOWN + /* NSLog(@"MouseDown: %d, pos: %d, %d", j, event->points[i].x, dev->x, dev->y); */ + evs[j] = 2; + } + } + + for (j = 0; j < 5; j++) + { + ClutterFruityFingerDevice *dev; + + dev = g_slist_nth_data (context->input_devices, j); + + if (dev->is_down && !mapped[j]) + { + // MOUSEUP + /* NSLog(@"MouseUp: %d, pos: %d, %d", j, dev->x, dev->y); */ + evs[j] = 1; + dev->is_down = FALSE; + } + } + + /* Now I guess go through device list and deliver an event for each + * if valid and devliver if so... + */ + { + i = 0; + GSList *list_it; + + for (list_it = context->input_devices; + list_it != NULL; + list_it = list_it->next) + { + ClutterFruityFingerDevice *dev = (ClutterFruityFingerDevice *)list_it->data; + + if (evs[i] > 0) + { + ClutterEvent *cev; + + if (evs[i] == 1) + { + cev = clutter_event_new (CLUTTER_BUTTON_RELEASE); + cev->button.device = (ClutterInputDevice *)dev; + cev->button.x = dev->x; + cev->button.y = dev->y; + cev->button.button = 1; + cev->button.time = clutter_get_timestamp () / 1000; + cev->any.stage = stage; + clutter_do_event (cev); + clutter_event_free (cev); + } + else if (evs[i] == 2) + { + cev = clutter_event_new (CLUTTER_BUTTON_PRESS); + cev->button.device = (ClutterInputDevice *)dev; + cev->button.x = dev->x; + cev->button.y = dev->y; + cev->button.button = 1; + cev->button.time = clutter_get_timestamp () / 1000; + cev->any.stage = stage; + clutter_do_event (cev); + clutter_event_free (cev); + } + else /* evs = 3, motion */ + { + cev = clutter_event_new (CLUTTER_MOTION); + cev->motion.device = (ClutterInputDevice *)dev; + cev->motion.x = dev->x; + cev->motion.y = dev->y; + cev->motion.time = clutter_get_timestamp () / 1000; + cev->any.stage = stage; + clutter_do_event (cev); + clutter_event_free (cev); + } + } + i++; + } + } +} + +#if 0 // old stylie - (void) mouseDown:(GSEvent*)event { CGPoint location= GSEventGetLocationInWindow(event); @@ -106,6 +291,67 @@ static CoreSurfaceBufferRef CreateSurface(int w, int h) clutter_do_event (cev); clutter_event_free (cev); } +#endif + +/* New... */ + +- (void)gestureChanged:(GSEvent*)event { + /*NSLog(@"gestureChanged:");*/ + [self doEvent: event]; +} + +- (void)gestureEnded:(GSEvent*)event { + /*NSLog(@"gestureEnded:");*/ + [self doEvent: event]; +} + +- (void)gestureStarted:(GSEvent*)event { + /*NSLog(@"gestureStarted:");*/ + [self doEvent: event]; +} + +- (void)mouseDown:(GSEvent*)event { + /*NSLog(@"mouseDown:");*/ + [self doEvent: event]; +} + +- (void)mouseDragged:(GSEvent*)event { + /*NSLog(@"mouseDragged:");*/ + [self doEvent: event]; +} + +- (void)mouseEntered:(GSEvent*)event { + /*NSLog(@"mouseEntered:");*/ + [self doEvent: event]; +} + +- (void)mouseExited:(GSEvent*)event { + /*NSLog(@"mouseExited:");*/ + [self doEvent: event]; +} + +- (void)mouseMoved:(GSEvent*)event { + /*NSLog(@"mouseMoved:");*/ + [self doEvent: event]; +} + +- (void)mouseUp:(GSEvent*)event { + /*NSLog(@"mouseUp:");*/ + [self doEvent: event]; +} + +- (void)view:(UIView *)view handleTapWithCount:(int)count event:(GSEvent *)event { + /*NSLog(@"handleTapWithCount: %d", count);*/ + [self doEvent: event]; +} + +- (double)viewTouchPauseThreshold:(UIView *)view { + return 0.5; +} + +- (BOOL)isFirstResponder { + return YES; +} @end diff --git a/clutter/glx/clutter-stage-glx.c b/clutter/glx/clutter-stage-glx.c index b7bb9a10a..4bfca77af 100644 --- a/clutter/glx/clutter-stage-glx.c +++ b/clutter/glx/clutter-stage-glx.c @@ -112,6 +112,7 @@ clutter_stage_glx_realize (ClutterActor *actor) ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (actor); ClutterStageGLX *stage_glx = CLUTTER_STAGE_GLX (actor); ClutterBackendGLX *backend_glx; + ClutterBackendX11 *backend_x11; gboolean is_offscreen; CLUTTER_NOTE (MISC, "Realizing main stage"); @@ -119,6 +120,7 @@ clutter_stage_glx_realize (ClutterActor *actor) g_object_get (stage_x11->wrapper, "offscreen", &is_offscreen, NULL); backend_glx = CLUTTER_BACKEND_GLX (clutter_get_default_backend ()); + backend_x11 = CLUTTER_BACKEND_X11 (clutter_get_default_backend ()); if (G_LIKELY (!is_offscreen)) { @@ -178,16 +180,27 @@ clutter_stage_glx_realize (ClutterActor *actor) mask, &xattr); } - CLUTTER_NOTE (MISC, "XSelectInput"); - XSelectInput (stage_x11->xdpy, stage_x11->xwin, - StructureNotifyMask | - FocusChangeMask | - ExposureMask | - /* FIXME: we may want to eplicity enable MotionMask */ - PointerMotionMask | - KeyPressMask | KeyReleaseMask | - ButtonPressMask | ButtonReleaseMask | - PropertyChangeMask); + if (clutter_x11_has_xinput()) + { + XSelectInput (stage_x11->xdpy, stage_x11->xwin, + StructureNotifyMask | + FocusChangeMask | + ExposureMask | + PropertyChangeMask); +#ifdef USE_XINPUT + _clutter_x11_select_events (stage_x11->xwin); +#endif + } + else + XSelectInput (stage_x11->xdpy, stage_x11->xwin, + StructureNotifyMask | + FocusChangeMask | + ExposureMask | + /* FIXME: we may want to eplicity enable MotionMask */ + PointerMotionMask | + KeyPressMask | KeyReleaseMask | + ButtonPressMask | ButtonReleaseMask | + PropertyChangeMask); /* no user resize.. */ clutter_stage_x11_fix_window_size (stage_x11); diff --git a/clutter/x11/clutter-backend-x11.c b/clutter/x11/clutter-backend-x11.c index 40eb4c086..1790222e2 100644 --- a/clutter/x11/clutter-backend-x11.c +++ b/clutter/x11/clutter-backend-x11.c @@ -38,6 +38,7 @@ #include "clutter-stage-x11.h" #include "clutter-x11.h" + #include "../clutter-event.h" #include "../clutter-main.h" #include "../clutter-debug.h" @@ -47,6 +48,22 @@ G_DEFINE_TYPE (ClutterBackendX11, clutter_backend_x11, CLUTTER_TYPE_BACKEND); +struct _ClutterX11XInputDevice +{ + ClutterInputDevice device; +#ifdef USE_XINPUT + XDevice *xdevice; + XEventClass xevent_list[5]; /* MAX 5 event types */ + int num_events; +#endif + ClutterX11InputDeviceType type; /* FIXME: generic to ClutterInputDevice? */ +}; + +#ifdef USE_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! @@ -151,6 +168,10 @@ clutter_backend_x11_post_parse (ClutterBackend *backend, clutter_backend_set_resolution (backend, dpi); +#ifdef USE_XINPUT + _clutter_x11_register_xinput (); +#endif + if (clutter_synchronise) XSynchronize (backend_x11->xdpy, True); @@ -530,3 +551,259 @@ clutter_x11_remove_filter (ClutterX11FilterFunc func, } } } + +#ifdef USE_XINPUT + +void +_clutter_x11_register_xinput () +{ + XDeviceInfo *xdevices = NULL; + XDeviceInfo *info = NULL; + + XDevice *xdevice = NULL; + + XInputClassInfo *xclass_info = NULL; + XExtensionVersion *ext; + + gint num_devices = 0; + gint num_events = 0; + gint i = 0, j = 0; + + ClutterBackendX11 *x11b; + ClutterX11XInputDevice *device = NULL; + + ClutterMainContext *context; + + if (!backend_singleton) + { + g_critical ("X11 backend has not been initialised"); + return; + } + + context = clutter_context_get_default (); + + backend_singleton->have_xinput = TRUE; + + ext = XGetExtensionVersion(backend_singleton->xdpy, INAME); + + if (!ext || (ext == (XExtensionVersion*) NoSuchExtension)) + { + backend_singleton->have_xinput = FALSE; + return; + } + + x11b = backend_singleton; + + xdevices = XListInputDevices (x11b->xdpy, &num_devices); + + CLUTTER_NOTE (BACKEND, "%d XINPUT devices found", num_devices); + + if (num_devices == 0) + { + backend_singleton->have_xinput = FALSE; + return; + } + + for (i = 0; i < num_devices; i++) + { + num_events = 0; + info = xdevices + i; + + 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 || + info->use == IsXExtensionDevice) + { + /* Create the appropriate Clutter device */ + device = g_new0 (ClutterX11XInputDevice, 1); + context->input_devices = g_slist_append (context->input_devices, device); + + xdevice = XOpenDevice (x11b->xdpy, info->id); + device->device.id = info->id; + + /* 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->xdevice = xdevice; + device->num_events = 0; + + switch (info->use) + { + case IsXExtensionPointer: + device->type = CLUTTER_X11_XINPUT_POINTER_DEVICE; + break; + case IsXExtensionKeyboard: + device->type = CLUTTER_X11_XINPUT_KEYBOARD_DEVICE; + break; + case IsXExtensionDevice: + device->type = CLUTTER_X11_XINPUT_EXTENSION_DEVICE; + break; + } + + 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) + { + 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; + + 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; + } + } + + device->num_events = num_events; + } + } + + XFree (xdevices); +} + +void +_clutter_x11_unregister_xinput () +{ + +} + +void +_clutter_x11_select_events (Window xwin) +{ + GSList *list_it; + ClutterX11XInputDevice *device = NULL; + + ClutterMainContext *context; + + context = clutter_context_get_default (); + + if (!backend_singleton) + { + g_critical ("X11 backend has not been initialised"); + return; + } + + for (list_it = context->input_devices; + list_it != NULL; + list_it = list_it->next) + { + device = (ClutterX11XInputDevice *)list_it->data; + + XSelectExtensionEvent (backend_singleton->xdpy, + xwin, + device->xevent_list, + device->num_events); + } +} + +ClutterX11XInputDevice* +_clutter_x11_get_device_for_xid (XID id) +{ + GSList *list_it; + ClutterX11XInputDevice *device = NULL; + ClutterMainContext *context; + + context = clutter_context_get_default (); + + if (!backend_singleton) + { + g_critical ("X11 backend has not been initialised"); + return NULL; + } + + for (list_it = context->input_devices; + list_it != NULL; + list_it = list_it->next) + { + device = (ClutterX11XInputDevice *)list_it->data; + + if (device->xdevice->device_id == id) + return device; + } + + return NULL; +} +#endif + +/* FIXME: This nasty little func needs moving elsewhere.. */ +GSList* +clutter_x11_get_input_devices (void) +{ + ClutterMainContext *context; + +#ifdef USE_XINPUT + if (!backend_singleton) + { + g_critical ("X11 backend has not been initialised"); + return NULL; + } + + context = clutter_context_get_default (); + + return context->input_devices; +#else + return NULL; +#endif +} + +ClutterX11InputDeviceType +clutter_x11_get_input_device_type (ClutterX11XInputDevice *device) +{ + return device->type; +} + +gboolean +clutter_x11_has_xinput (void) +{ +#ifdef USE_XINPUT + if (!backend_singleton) + { + g_critical ("X11 backend has not been initialised"); + return FALSE; + } + + return backend_singleton->have_xinput; +#else + return FALSE; +#endif +} diff --git a/clutter/x11/clutter-backend-x11.h b/clutter/x11/clutter-backend-x11.h index 48b4325ff..2642c010b 100644 --- a/clutter/x11/clutter-backend-x11.h +++ b/clutter/x11/clutter-backend-x11.h @@ -28,6 +28,10 @@ #include #include +#ifdef USE_XINPUT +#include +#endif + #include "clutter-x11.h" G_BEGIN_DECLS @@ -76,6 +80,12 @@ struct _ClutterBackendX11 Atom atom_XEMBED_INFO; Atom atom_NET_WM_NAME; Atom atom_UTF8_STRING; + +#ifdef USE_XINPUT + int event_types[CLUTTER_X11_XINPUT_LAST_EVENT]; + gboolean have_xinput; +#endif + }; struct _ClutterBackendX11Class @@ -111,6 +121,19 @@ clutter_backend_x11_add_options (ClutterBackend *backend, ClutterFeatureFlags clutter_backend_x11_get_features (ClutterBackend *backend); +#ifdef USE_XINPUT +void +_clutter_x11_register_xinput (void); + +void +_clutter_x11_unregister_xinput (void); + +ClutterX11XInputDevice * +_clutter_x11_get_device_for_xid (XID id); +#endif + +void +_clutter_x11_select_events (Window xwin); G_END_DECLS diff --git a/clutter/x11/clutter-event-x11.c b/clutter/x11/clutter-event-x11.c index 0a7031803..afd079f51 100644 --- a/clutter/x11/clutter-event-x11.c +++ b/clutter/x11/clutter-event-x11.c @@ -41,6 +41,10 @@ #include +#ifdef USE_XINPUT +#include +#endif + /* XEMBED protocol support for toolkit embedding */ #define XEMBED_MAPPED (1 << 0) #define MAX_SUPPORTED_XEMBED_VERSION 1 @@ -215,6 +219,7 @@ set_user_time (ClutterBackendX11 *backend_x11, } } + static void translate_key_event (ClutterBackend *backend, ClutterEvent *event, @@ -347,7 +352,7 @@ event_translate (ClutterBackend *backend, ClutterStageX11 *stage_x11; ClutterStage *stage; ClutterStageWindow *impl; - gboolean res; + gboolean res, not_yet_handled = FALSE; Window xwindow, stage_xwindow; backend_x11 = CLUTTER_BACKEND_X11 (backend); @@ -389,7 +394,7 @@ event_translate (ClutterBackend *backend, stage = clutter_x11_get_stage_from_window (xwindow); if (stage == NULL) - return FALSE; + return FALSE; impl = _clutter_stage_get_window (stage); stage_x11 = CLUTTER_STAGE_X11 (impl); @@ -397,6 +402,7 @@ event_translate (ClutterBackend *backend, event->any.stage = stage; + res = TRUE; switch (xevent->type) @@ -522,83 +528,6 @@ event_translate (ClutterBackend *backend, res = FALSE; } break; - - case KeyPress: - event->type = CLUTTER_KEY_PRESS; - translate_key_event (backend, event, xevent); - set_user_time (backend_x11, &xwindow, xevent->xkey.time); - break; - - case KeyRelease: - event->type = CLUTTER_KEY_RELEASE; - translate_key_event (backend, event, xevent); - break; - - case ButtonPress: - switch (xevent->xbutton.button) - { - case 4: /* up */ - case 5: /* down */ - case 6: /* left */ - case 7: /* right */ - event->scroll.type = event->type = CLUTTER_SCROLL; - - if (xevent->xbutton.button == 4) - event->scroll.direction = CLUTTER_SCROLL_UP; - else if (xevent->xbutton.button == 5) - event->scroll.direction = CLUTTER_SCROLL_DOWN; - else if (xevent->xbutton.button == 6) - event->scroll.direction = CLUTTER_SCROLL_LEFT; - else - event->scroll.direction = CLUTTER_SCROLL_RIGHT; - - event->scroll.time = xevent->xbutton.time; - event->scroll.x = xevent->xbutton.x; - event->scroll.y = xevent->xbutton.y; - event->scroll.modifier_state = xevent->xbutton.state; - - 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; - - case ButtonRelease: - /* 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; - break; - } - - event->button.type = event->type = CLUTTER_BUTTON_RELEASE; - event->button.time = xevent->xbutton.time; - event->button.x = xevent->xbutton.x; - event->button.y = xevent->xbutton.y; - event->button.modifier_state = xevent->xbutton.state; - event->button.button = xevent->xbutton.button; - 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 DestroyNotify: CLUTTER_NOTE (EVENT, "destroy notify:\txid: %ld", xevent->xdestroywindow.window); @@ -625,10 +554,244 @@ event_translate (ClutterBackend *backend, default: /* ignore every other event */ - res = FALSE; + not_yet_handled = TRUE; break; } + /* Input device event handling.. */ + if (not_yet_handled) + { + if (!clutter_x11_has_xinput ()) + { + /* Regular X event */ + switch (xevent->type) + { + case KeyPress: + event->type = CLUTTER_KEY_PRESS; + translate_key_event (backend, event, xevent); + set_user_time (backend_x11, &xwindow, xevent->xkey.time); + break; + + case KeyRelease: + event->type = CLUTTER_KEY_RELEASE; + translate_key_event (backend, event, xevent); + break; + + case ButtonPress: + switch (xevent->xbutton.button) + { + case 4: /* up */ + case 5: /* down */ + case 6: /* left */ + case 7: /* right */ + event->scroll.type = event->type = CLUTTER_SCROLL; + + if (xevent->xbutton.button == 4) + event->scroll.direction = CLUTTER_SCROLL_UP; + else if (xevent->xbutton.button == 5) + event->scroll.direction = CLUTTER_SCROLL_DOWN; + else if (xevent->xbutton.button == 6) + event->scroll.direction = CLUTTER_SCROLL_LEFT; + else + event->scroll.direction = CLUTTER_SCROLL_RIGHT; + + event->scroll.time = xevent->xbutton.time; + event->scroll.x = xevent->xbutton.x; + event->scroll.y = xevent->xbutton.y; + event->scroll.modifier_state = xevent->xbutton.state; + + 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; + + case ButtonRelease: + /* 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; + break; + } + + event->button.type = event->type = CLUTTER_BUTTON_RELEASE; + event->button.time = xevent->xbutton.time; + event->button.x = xevent->xbutton.x; + event->button.y = xevent->xbutton.y; + event->button.modifier_state = xevent->xbutton.state; + event->button.button = xevent->xbutton.button; + 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; + default: + /* ignore every other event */ + res = FALSE; + break; + } + } + else + { /* XInput fun.. Needs clean up. */ +#ifdef USE_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; + + 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 + = (ClutterInputDevice *)_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 + = (ClutterInputDevice *)_clutter_x11_get_device_for_xid (xbev->deviceid); + break; + } + + set_user_time (backend_x11, &xwindow, xbev->time); + + CLUTTER_NOTE(EVENT, "XINPUT Button press event for %li %d %d", + xbev->deviceid, xbev->x, xbev->y); + } + else if (xevent->type + == ev_types[CLUTTER_X11_XINPUT_BUTTON_RELEASE_EVENT]) + { + XDeviceButtonEvent *xbev = (XDeviceButtonEvent *)xevent; + + /* scroll events don't have a corresponding release */ + if (xbev->button == 4 || + xbev->button == 5 || + xbev->button == 6 || + xbev->button == 7) + { + 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 + = (ClutterInputDevice *)_clutter_x11_get_device_for_xid (xbev->deviceid); + CLUTTER_NOTE(EVENT, "XINPUT Button release event for %li %d %d", + xbev->deviceid, xbev->x, xbev->y); + } + else if (xevent->type + == ev_types [CLUTTER_X11_XINPUT_MOTION_NOTIFY_EVENT]) + { + XDeviceMotionEvent *xmev = (XDeviceMotionEvent *)xevent; + + 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 + = (ClutterInputDevice *) _clutter_x11_get_device_for_xid (xmev->deviceid); + CLUTTER_NOTE(EVENT, "XINPUT Motion event for %li %d %d", + xmev->deviceid, + xmev->x, + xmev->y); + } + else if (xevent->type + == ev_types [CLUTTER_X11_XINPUT_KEY_PRESS_EVENT]) + { + XDeviceKeyEvent *xkev = (XDeviceKeyEvent *)xevent; + + event->key.type = CLUTTER_KEY_PRESS; + event->key.time = xkev->time; + event->key.modifier_state = (ClutterModifierType) xkev->state; + event->key.hardware_keycode = xkev->keycode; + + /* Note key events have no device field */ + + /* FIXME: We need to handle other modifiers rather than + just shift */ + event->key.keyval = + XKeycodeToKeysym (xevent->xany.display, + xkev->keycode, + (event->key.modifier_state + & CLUTTER_SHIFT_MASK) ? 1 : 0); + + set_user_time (backend_x11, &xwindow, xkev->time); + } + else if (xevent->type + == ev_types [CLUTTER_X11_XINPUT_KEY_RELEASE_EVENT]) + { + XDeviceKeyEvent *xkev = (XDeviceKeyEvent *)xevent; + + event->key.type = CLUTTER_KEY_RELEASE; + event->key.time = xkev->time; + event->key.modifier_state = (ClutterModifierType) xkev->state; + event->key.hardware_keycode = xkev->keycode; + + /* FIXME: We need to handle other modifiers rather than + just shift */ + event->key.keyval = + XKeycodeToKeysym (xevent->xany.display, + xkev->keycode, + (event->key.modifier_state + & CLUTTER_SHIFT_MASK) ? 1 : 0); + } + else +#endif + { + CLUTTER_NOTE (EVENT, "Uknown Event"); + res = FALSE; + } + } + } + + out: return res; } diff --git a/clutter/x11/clutter-stage-x11.h b/clutter/x11/clutter-stage-x11.h index bb2f9469c..229f058e1 100644 --- a/clutter/x11/clutter-stage-x11.h +++ b/clutter/x11/clutter-stage-x11.h @@ -63,6 +63,11 @@ struct _ClutterStageX11 ClutterBackendX11 *backend; ClutterStageState state; +#ifdef USE_XINPUT + int event_types[CLUTTER_X11_XINPUT_LAST_EVENT]; + GList *devices; +#endif + ClutterStage *wrapper; }; @@ -79,6 +84,8 @@ void clutter_stage_x11_set_wm_protocols (ClutterStageX11 *stage_x11); void clutter_stage_x11_map (ClutterStageX11 *stage_x11); void clutter_stage_x11_unmap (ClutterStageX11 *stage_x11); +GList *clutter_stage_x11_get_input_devices (ClutterStageX11 *stage_x11); + G_END_DECLS #endif /* __CLUTTER_STAGE_H__ */ diff --git a/clutter/x11/clutter-x11.h b/clutter/x11/clutter-x11.h index bb0a82324..ee2f287f3 100644 --- a/clutter/x11/clutter-x11.h +++ b/clutter/x11/clutter-x11.h @@ -64,6 +64,23 @@ typedef enum { CLUTTER_X11_FILTER_REMOVE } ClutterX11FilterReturn; +typedef enum { + CLUTTER_X11_XINPUT_KEY_PRESS_EVENT = 0, + CLUTTER_X11_XINPUT_KEY_RELEASE_EVENT, + CLUTTER_X11_XINPUT_BUTTON_PRESS_EVENT, + CLUTTER_X11_XINPUT_BUTTON_RELEASE_EVENT, + CLUTTER_X11_XINPUT_MOTION_NOTIFY_EVENT, + CLUTTER_X11_XINPUT_LAST_EVENT +} ClutterX11XInputEventTypes; + +typedef enum { + CLUTTER_X11_XINPUT_POINTER_DEVICE, + CLUTTER_X11_XINPUT_KEYBOARD_DEVICE, + CLUTTER_X11_XINPUT_EXTENSION_DEVICE +} ClutterX11InputDeviceType; + +typedef struct _ClutterX11XInputDevice ClutterX11XInputDevice; + /** * ClutterX11FilterFunc: * @xev: Native X11 event structure @@ -104,6 +121,16 @@ void clutter_x11_disable_event_retrieval (void); ClutterStage *clutter_x11_get_stage_from_window (Window win); +GSList* +clutter_x11_get_input_devices (void); + +ClutterX11InputDeviceType +clutter_x11_get_input_device_type (ClutterX11XInputDevice *device); + +gboolean +clutter_x11_has_xinput (void); + + G_END_DECLS #endif /* __CLUTTER_X11_H__ */ diff --git a/configure.ac b/configure.ac index 31a88b9de..40f9ccd28 100644 --- a/configure.ac +++ b/configure.ac @@ -225,6 +225,22 @@ fi AM_CONDITIONAL(X11_TESTS, test "x$x11_tests" != "xno") +xinput=no +AC_ARG_ENABLE(xinput, + AS_HELP_STRING([--enable-xinput], + ["Use the XINPUT X extension"]),[ + if test "x$enableval" = "xyes" ; then + PKG_CHECK_MODULES(XINPUT,[xi], + xinput=yes, + xinput=no) + fi], + [xinput=yes]) + +if test "x$xinput" = "xyes"; then + AC_DEFINE(USE_XINPUT, 1, Use the XINPUT X extension) + X11_LIBS="$X11_LIBS -lXi" +fi + clutter_gl_header="" use_gles2_wrapper="no" @@ -657,6 +673,9 @@ echo "" echo " prefix: ${prefix}" echo "" echo " Flavour: ${clutterbackend}/${CLUTTER_COGL}" +if test "x$clutterbackend" = "xeglx" || test "x$clutterbackend" = "xglx"; then + echo " XInput: ${xinput}" +fi echo " GL Headers: ${CLUTTER_GL_HEADER}" echo " Image backend: ${imagebackend}" echo " Target library: ${clutterbackendlib}" diff --git a/tests/Makefile.am b/tests/Makefile.am index 829d8bcbd..a8ca07444 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -18,6 +18,7 @@ noinst_PROGRAMS = test-textures test-events test-offscreen test-scale \ if X11_TESTS noinst_PROGRAMS += test-pixmap +noinst_PROGRAMS += test-devices endif INCLUDES = -I$(top_srcdir)/ -I$(top_srcdir)/clutter -I$(top_builddir)/clutter @@ -70,5 +71,6 @@ test_texture_quality_SOURCES = test-texture-quality.c test_entry_auto_SOURCES = test-entry-auto.c test_layout_SOURCES = test-layout.c test_invariants_SOURCES = test-invariants.c +test_devices_SOURCES = test-devices.c EXTRA_DIST = redhand.png test-script.json diff --git a/tests/test-devices.c b/tests/test-devices.c new file mode 100644 index 000000000..fab9a02b0 --- /dev/null +++ b/tests/test-devices.c @@ -0,0 +1,76 @@ +#include +#include + +typedef struct { + + GHashTable *devices; + +} TestDevicesApp; + + + +static gboolean +stage_motion_event_cb (ClutterActor *actor, + ClutterEvent *event, + gpointer userdata) +{ + TestDevicesApp *app = (TestDevicesApp *)userdata; + ClutterActor *hand = NULL; + ClutterMotionEvent *mev = (ClutterMotionEvent *)event; + + hand = g_hash_table_lookup (app->devices, mev->device); + clutter_actor_set_position (hand, mev->x, mev->y); +} + +int +main (int argc, char **argv) +{ + ClutterActor *stage = NULL; + GSList *stage_devices = NULL; + TestDevicesApp *app = NULL; + ClutterColor stage_color = { 0x61, 0x64, 0x8c, 0xff }; + + clutter_init (&argc, &argv); + + app = g_new0 (TestDevicesApp, 1); + app->devices = g_hash_table_new (g_direct_hash, g_direct_equal) ; + + stage = clutter_stage_get_default (); + clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color); + //clutter_stage_fullscreen (CLUTTER_STAGE (stage)); + + g_signal_connect (stage, + "motion-event", + G_CALLBACK(stage_motion_event_cb), + app); + clutter_actor_show_all (stage); + + stage_devices = clutter_x11_get_input_devices (); + + do + { + if (stage_devices) + { + ClutterX11XInputDevice *device = NULL; + ClutterActor *hand = NULL; + + device = (ClutterX11XInputDevice *)stage_devices->data; + + if (clutter_x11_get_input_device_type (device) + == CLUTTER_X11_XINPUT_POINTER_DEVICE) + { + + g_debug("got a pointer device...\n"); + + hand = clutter_texture_new_from_file ("redhand.png", NULL); + g_hash_table_insert (app->devices, device, hand); + clutter_container_add_actor (CLUTTER_CONTAINER (stage), hand); + } + + } + } while ((stage_devices = stage_devices->next) != NULL); + + clutter_main (); + + return 0; +}