/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By Matthew Allum * * Copyright (C) 2006 OpenedHand * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * */ #include "clutter/clutter-build-config.h" #include "clutter/clutter-backend-private.h" #include "clutter/clutter-debug.h" #include "clutter/clutter-event-private.h" #include "clutter/clutter-keysyms.h" #include "clutter/clutter-input-device-tool.h" #include "clutter/clutter-private.h" #include struct _ClutterAnyEvent { ClutterEventType type; int64_t time_us; ClutterEventFlags flags; ClutterInputDevice *device; ClutterInputDevice *source_device; }; struct _ClutterKeyEvent { ClutterEventType type; int64_t time_us; ClutterEventFlags flags; ClutterInputDevice *device; ClutterInputDevice *source_device; ClutterModifierSet raw_modifiers; ClutterModifierType modifier_state; uint32_t keyval; uint16_t hardware_keycode; gunichar unicode_value; uint32_t evdev_code; }; struct _ClutterButtonEvent { ClutterEventType type; int64_t time_us; ClutterEventFlags flags; ClutterInputDevice *device; ClutterInputDevice *source_device; float x; float y; ClutterModifierType modifier_state; uint32_t button; double *axes; ClutterInputDeviceTool *tool; uint32_t evdev_code; }; struct _ClutterProximityEvent { ClutterEventType type; int64_t time_us; ClutterEventFlags flags; ClutterInputDevice *device; ClutterInputDevice *source_device; ClutterInputDeviceTool *tool; }; struct _ClutterCrossingEvent { ClutterEventType type; int64_t time_us; ClutterEventFlags flags; ClutterInputDevice *device; ClutterInputDevice *source_device; float x; float y; ClutterEventSequence *sequence; ClutterActor *source; ClutterActor *related; }; struct _ClutterMotionEvent { ClutterEventType type; int64_t time_us; ClutterEventFlags flags; ClutterInputDevice *device; ClutterInputDevice *source_device; float x; float y; ClutterModifierType modifier_state; double *axes; ClutterInputDeviceTool *tool; double dx; double dy; double dx_unaccel; double dy_unaccel; double dx_constrained; double dy_constrained; }; struct _ClutterScrollEvent { ClutterEventType type; int64_t time_us; ClutterEventFlags flags; ClutterInputDevice *device; ClutterInputDevice *source_device; float x; float y; double delta_x; double delta_y; ClutterScrollDirection direction; ClutterModifierType modifier_state; double *axes; ClutterInputDeviceTool *tool; ClutterScrollSource scroll_source; ClutterScrollFinishFlags finish_flags; }; struct _ClutterTouchEvent { ClutterEventType type; int64_t time_us; ClutterEventFlags flags; ClutterInputDevice *device; ClutterInputDevice *source_device; float x; float y; ClutterEventSequence *sequence; ClutterModifierType modifier_state; double *axes; /* reserved */ }; struct _ClutterTouchpadPinchEvent { ClutterEventType type; int64_t time_us; ClutterEventFlags flags; ClutterInputDevice *device; ClutterInputDevice *source_device; ClutterTouchpadGesturePhase phase; float x; float y; float dx; float dy; float dx_unaccel; float dy_unaccel; float angle_delta; float scale; uint32_t n_fingers; }; struct _ClutterTouchpadSwipeEvent { ClutterEventType type; int64_t time_us; ClutterEventFlags flags; ClutterInputDevice *device; ClutterInputDevice *source_device; ClutterTouchpadGesturePhase phase; uint32_t n_fingers; float x; float y; float dx; float dy; float dx_unaccel; float dy_unaccel; }; struct _ClutterTouchpadHoldEvent { ClutterEventType type; int64_t time_us; ClutterEventFlags flags; ClutterInputDevice *device; ClutterInputDevice *source_device; ClutterTouchpadGesturePhase phase; uint32_t n_fingers; float x; float y; }; struct _ClutterPadButtonEvent { ClutterEventType type; int64_t time_us; ClutterEventFlags flags; ClutterInputDevice *device; ClutterInputDevice *source_device; uint32_t button; uint32_t group; uint32_t mode; }; struct _ClutterPadStripEvent { ClutterEventType type; int64_t time_us; ClutterEventFlags flags; ClutterInputDevice *device; ClutterInputDevice *source_device; ClutterInputDevicePadSource strip_source; uint32_t strip_number; uint32_t group; double value; uint32_t mode; }; struct _ClutterPadRingEvent { ClutterEventType type; int64_t time_us; ClutterEventFlags flags; ClutterInputDevice *device; ClutterInputDevice *source_device; ClutterInputDevicePadSource ring_source; uint32_t ring_number; uint32_t group; double angle; uint32_t mode; }; struct _ClutterDeviceEvent { ClutterEventType type; int64_t time_us; ClutterEventFlags flags; ClutterInputDevice *device; ClutterInputDevice *source_device; }; struct _ClutterIMEvent { ClutterEventType type; int64_t time_us; ClutterEventFlags flags; ClutterInputDevice *device; ClutterInputDevice *source_device; char *text; int32_t offset; int32_t anchor; uint32_t len; ClutterPreeditResetMode mode; }; union _ClutterEvent { /*< private >*/ ClutterEventType type; ClutterAnyEvent any; ClutterButtonEvent button; ClutterKeyEvent key; ClutterMotionEvent motion; ClutterScrollEvent scroll; ClutterCrossingEvent crossing; ClutterTouchEvent touch; ClutterTouchpadPinchEvent touchpad_pinch; ClutterTouchpadSwipeEvent touchpad_swipe; ClutterTouchpadHoldEvent touchpad_hold; ClutterProximityEvent proximity; ClutterPadButtonEvent pad_button; ClutterPadStripEvent pad_strip; ClutterPadRingEvent pad_ring; ClutterDeviceEvent device; ClutterIMEvent im; }; typedef struct _ClutterEventFilter { int id; ClutterStage *stage; ClutterEventFilterFunc func; GDestroyNotify notify; gpointer user_data; } ClutterEventFilter; G_DEFINE_BOXED_TYPE (ClutterEvent, clutter_event, clutter_event_copy, clutter_event_free); static ClutterEventSequence * clutter_event_sequence_copy (ClutterEventSequence *sequence) { /* Nothing to copy here */ return sequence; } static void clutter_event_sequence_free (ClutterEventSequence *sequence) { /* Nothing to free here */ } G_DEFINE_BOXED_TYPE (ClutterEventSequence, clutter_event_sequence, clutter_event_sequence_copy, clutter_event_sequence_free); /** * clutter_event_type: * @event: a #ClutterEvent * * Retrieves the type of the event. * * Return value: a #ClutterEventType */ ClutterEventType clutter_event_type (const ClutterEvent *event) { g_return_val_if_fail (event != NULL, CLUTTER_NOTHING); return event->type; } /** * clutter_event_get_time: * @event: a #ClutterEvent * * Retrieves the time of the event. * * Return value: the time of the event, or %CLUTTER_CURRENT_TIME */ guint32 clutter_event_get_time (const ClutterEvent *event) { g_return_val_if_fail (event != NULL, CLUTTER_CURRENT_TIME); return us2ms (event->any.time_us); } /** * clutter_event_get_state: * @event: a #ClutterEvent * * Retrieves the modifier state of the event. In case the window system * supports reporting latched and locked modifiers, this function returns * the effective state. * * Return value: the modifier state parameter, or 0 */ ClutterModifierType clutter_event_get_state (const ClutterEvent *event) { g_return_val_if_fail (event != NULL, 0); switch (event->type) { case CLUTTER_KEY_PRESS: case CLUTTER_KEY_RELEASE: return event->key.modifier_state; case CLUTTER_BUTTON_PRESS: case CLUTTER_BUTTON_RELEASE: return event->button.modifier_state; case CLUTTER_TOUCH_BEGIN: case CLUTTER_TOUCH_UPDATE: case CLUTTER_TOUCH_END: case CLUTTER_TOUCH_CANCEL: return event->touch.modifier_state; case CLUTTER_MOTION: return event->motion.modifier_state; case CLUTTER_SCROLL: return event->scroll.modifier_state; default: break; } return 0; } /** * clutter_event_get_coords: * @event: a #ClutterEvent * @x: (out): return location for the X coordinate, or %NULL * @y: (out): return location for the Y coordinate, or %NULL * * Retrieves the coordinates of @event and puts them into @x and @y. */ void clutter_event_get_coords (const ClutterEvent *event, gfloat *x, gfloat *y) { graphene_point_t coords; g_return_if_fail (event != NULL); clutter_event_get_position (event, &coords); if (x != NULL) *x = coords.x; if (y != NULL) *y = coords.y; } /** * clutter_event_get_position: * @event: a #ClutterEvent * @position: a #graphene_point_t * * Retrieves the event coordinates as a #graphene_point_t. */ void clutter_event_get_position (const ClutterEvent *event, graphene_point_t *position) { g_return_if_fail (event != NULL); g_return_if_fail (position != NULL); switch (event->type) { case CLUTTER_NOTHING: case CLUTTER_KEY_PRESS: case CLUTTER_KEY_RELEASE: case CLUTTER_EVENT_LAST: case CLUTTER_PROXIMITY_IN: case CLUTTER_PROXIMITY_OUT: case CLUTTER_PAD_BUTTON_PRESS: case CLUTTER_PAD_BUTTON_RELEASE: case CLUTTER_PAD_STRIP: case CLUTTER_PAD_RING: case CLUTTER_DEVICE_ADDED: case CLUTTER_DEVICE_REMOVED: case CLUTTER_IM_COMMIT: case CLUTTER_IM_DELETE: case CLUTTER_IM_PREEDIT: graphene_point_init (position, 0.f, 0.f); break; case CLUTTER_ENTER: case CLUTTER_LEAVE: graphene_point_init (position, event->crossing.x, event->crossing.y); break; case CLUTTER_BUTTON_PRESS: case CLUTTER_BUTTON_RELEASE: graphene_point_init (position, event->button.x, event->button.y); break; case CLUTTER_MOTION: graphene_point_init (position, event->motion.x, event->motion.y); break; case CLUTTER_TOUCH_BEGIN: case CLUTTER_TOUCH_UPDATE: case CLUTTER_TOUCH_END: case CLUTTER_TOUCH_CANCEL: graphene_point_init (position, event->touch.x, event->touch.y); break; case CLUTTER_SCROLL: graphene_point_init (position, event->scroll.x, event->scroll.y); break; case CLUTTER_TOUCHPAD_PINCH: graphene_point_init (position, event->touchpad_pinch.x, event->touchpad_pinch.y); break; case CLUTTER_TOUCHPAD_SWIPE: graphene_point_init (position, event->touchpad_swipe.x, event->touchpad_swipe.y); break; case CLUTTER_TOUCHPAD_HOLD: graphene_point_init (position, event->touchpad_hold.x, event->touchpad_hold.y); break; } } /** * clutter_event_get_flags: * @event: a #ClutterEvent * * Retrieves the #ClutterEventFlags of @event * * Return value: the event flags */ ClutterEventFlags clutter_event_get_flags (const ClutterEvent *event) { g_return_val_if_fail (event != NULL, CLUTTER_EVENT_NONE); return event->any.flags; } /** * clutter_event_get_related: * @event: a #ClutterEvent of type %CLUTTER_ENTER or of * type %CLUTTER_LEAVE * * Retrieves the related actor of a crossing event. * * Return value: (transfer none): the related #ClutterActor, or %NULL */ ClutterActor * clutter_event_get_related (const ClutterEvent *event) { g_return_val_if_fail (event != NULL, NULL); g_return_val_if_fail (event->type == CLUTTER_ENTER || event->type == CLUTTER_LEAVE, NULL); return event->crossing.related; } /** * clutter_event_get_scroll_delta: * @event: a #ClutterEvent of type %CLUTTER_SCROLL * @dx: (out): return location for the delta on the horizontal axis * @dy: (out): return location for the delta on the vertical axis * * Retrieves the precise scrolling information of @event. * * The @event has to have a #ClutterScrollEvent.direction value * of %CLUTTER_SCROLL_SMOOTH. */ void clutter_event_get_scroll_delta (const ClutterEvent *event, gdouble *dx, gdouble *dy) { g_return_if_fail (event != NULL); g_return_if_fail (event->type == CLUTTER_SCROLL); g_return_if_fail (event->scroll.direction == CLUTTER_SCROLL_SMOOTH); if (dx != NULL) *dx = event->scroll.delta_x; if (dy != NULL) *dy = event->scroll.delta_y; } /** * clutter_event_get_scroll_direction: * @event: a #ClutterEvent of type %CLUTTER_SCROLL * * Retrieves the direction of the scrolling of @event * * Return value: the scrolling direction */ ClutterScrollDirection clutter_event_get_scroll_direction (const ClutterEvent *event) { g_return_val_if_fail (event != NULL, CLUTTER_SCROLL_UP); g_return_val_if_fail (event->type == CLUTTER_SCROLL, CLUTTER_SCROLL_UP); return event->scroll.direction; } /** * clutter_event_get_button: * @event: a #ClutterEvent of type %CLUTTER_BUTTON_PRESS or * of type %CLUTTER_BUTTON_RELEASE * * Retrieves the button number of @event * * Return value: the button number */ guint32 clutter_event_get_button (const ClutterEvent *event) { g_return_val_if_fail (event != NULL, 0); g_return_val_if_fail (event->type == CLUTTER_BUTTON_PRESS || event->type == CLUTTER_BUTTON_RELEASE || event->type == CLUTTER_PAD_BUTTON_PRESS || event->type == CLUTTER_PAD_BUTTON_RELEASE, 0); if (event->type == CLUTTER_BUTTON_PRESS || event->type == CLUTTER_BUTTON_RELEASE) return event->button.button; else return event->pad_button.button; } /* keys */ /** * clutter_event_get_key_symbol: * @event: a #ClutterEvent of type %CLUTTER_KEY_PRESS or * of type %CLUTTER_KEY_RELEASE * * Retrieves the key symbol of @event * * Return value: the key symbol representing the key */ guint clutter_event_get_key_symbol (const ClutterEvent *event) { g_return_val_if_fail (event != NULL, 0); g_return_val_if_fail (event->type == CLUTTER_KEY_PRESS || event->type == CLUTTER_KEY_RELEASE, 0); return event->key.keyval; } /** * clutter_event_get_key_code: * @event: a #ClutterEvent of type %CLUTTER_KEY_PRESS or * of type %CLUTTER_KEY_RELEASE * * Retrieves the keycode of the key that caused @event * * Return value: The keycode representing the key */ guint16 clutter_event_get_key_code (const ClutterEvent *event) { g_return_val_if_fail (event != NULL, 0); g_return_val_if_fail (event->type == CLUTTER_KEY_PRESS || event->type == CLUTTER_KEY_RELEASE, 0); return event->key.hardware_keycode; } /** * clutter_event_get_key_unicode: * @event: a #ClutterEvent of type %CLUTTER_KEY_PRESS * or %CLUTTER_KEY_RELEASE * * Retrieves the unicode value for the key that caused @keyev. * * Return value: The unicode value representing the key */ gunichar clutter_event_get_key_unicode (const ClutterEvent *event) { g_return_val_if_fail (event != NULL, 0); g_return_val_if_fail (event->type == CLUTTER_KEY_PRESS || event->type == CLUTTER_KEY_RELEASE, 0); if (event->key.unicode_value) return event->key.unicode_value; else return clutter_keysym_to_unicode (event->key.keyval); } /** * clutter_event_get_key_state: * @event: a #ClutterEvent of type %CLUTTER_KEY_PRESS * or %CLUTTER_KEY_RELEASE * @pressed: (out): Return location for pressed modifiers * @latched: (out): Return location for latched modifiers * @locked: (out): Return location for locked modifiers * * Returns the modifier state decomposed into independent * pressed/latched/locked states. The effective state is a * composition of these 3 states, see clutter_event_get_state(). **/ void clutter_event_get_key_state (const ClutterEvent *event, ClutterModifierType *pressed, ClutterModifierType *latched, ClutterModifierType *locked) { g_return_if_fail (event != NULL); g_return_if_fail (event->type == CLUTTER_KEY_PRESS || event->type == CLUTTER_KEY_RELEASE); if (pressed) *pressed = event->key.raw_modifiers.pressed; if (latched) *latched = event->key.raw_modifiers.latched; if (locked) *locked = event->key.raw_modifiers.locked; } /** * clutter_event_get_event_sequence: * @event: a #ClutterEvent of type %CLUTTER_TOUCH_BEGIN, * %CLUTTER_TOUCH_UPDATE, %CLUTTER_TOUCH_END, or * %CLUTTER_TOUCH_CANCEL * * Retrieves the #ClutterEventSequence of @event. * * Return value: (transfer none): the event sequence, or %NULL */ ClutterEventSequence * clutter_event_get_event_sequence (const ClutterEvent *event) { g_return_val_if_fail (event != NULL, NULL); if (event->type == CLUTTER_TOUCH_BEGIN || event->type == CLUTTER_TOUCH_UPDATE || event->type == CLUTTER_TOUCH_END || event->type == CLUTTER_TOUCH_CANCEL) return event->touch.sequence; else if (event->type == CLUTTER_ENTER || event->type == CLUTTER_LEAVE) return event->crossing.sequence; return NULL; } /** * clutter_event_get_device_type: * @event: a #ClutterEvent * * Retrieves the type of the device for @event * * Return value: the #ClutterInputDeviceType for the device, if * any is set */ ClutterInputDeviceType clutter_event_get_device_type (const ClutterEvent *event) { ClutterInputDevice *device = NULL; g_return_val_if_fail (event != NULL, CLUTTER_POINTER_DEVICE); device = clutter_event_get_device (event); if (device != NULL) return clutter_input_device_get_device_type (device); return CLUTTER_POINTER_DEVICE; } /** * clutter_event_get_device: * @event: a #ClutterEvent * * Retrieves the #ClutterInputDevice for the event. * If you want the physical device the event originated from, use * clutter_event_get_source_device(). * * The #ClutterInputDevice structure is completely opaque and should * be cast to the platform-specific implementation. * * Return value: (transfer none): the #ClutterInputDevice or %NULL. The * returned device is owned by the #ClutterEvent and it should not * be unreferenced */ ClutterInputDevice * clutter_event_get_device (const ClutterEvent *event) { g_return_val_if_fail (event != NULL, NULL); return event->any.device; } /** * clutter_event_get_device_tool: * @event: a #ClutterEvent * * Returns the device tool that originated this event * * Returns: (transfer none): The tool of this event8 **/ ClutterInputDeviceTool * clutter_event_get_device_tool (const ClutterEvent *event) { g_return_val_if_fail (event != NULL, NULL); switch (event->any.type) { case CLUTTER_BUTTON_PRESS: case CLUTTER_BUTTON_RELEASE: return event->button.tool; case CLUTTER_MOTION: return event->motion.tool; case CLUTTER_SCROLL: return event->scroll.tool; case CLUTTER_PROXIMITY_IN: case CLUTTER_PROXIMITY_OUT: return event->proximity.tool; default: return NULL; } } /** * clutter_event_new: * @type: The type of event. * * Creates a new #ClutterEvent of the specified type. * * Return value: (transfer full): A newly allocated #ClutterEvent. */ ClutterEvent * clutter_event_new (ClutterEventType type) { ClutterEvent *new_event; new_event = g_new0 (ClutterEvent, 1); new_event->any.type = type; return new_event; } /** * clutter_event_copy: * @event: A #ClutterEvent. * * Copies @event. * * Return value: (transfer full): A newly allocated #ClutterEvent */ ClutterEvent * clutter_event_copy (const ClutterEvent *event) { ClutterEvent *new_event; g_return_val_if_fail (event != NULL, NULL); new_event = clutter_event_new (CLUTTER_NOTHING); g_set_object (&new_event->any.device, event->any.device); g_set_object (&new_event->any.source_device, event->any.source_device); *new_event = *event; switch (event->type) { case CLUTTER_BUTTON_PRESS: case CLUTTER_BUTTON_RELEASE: if (event->button.axes != NULL) { new_event->button.axes = g_memdup2 (event->button.axes, sizeof (double) * CLUTTER_INPUT_AXIS_LAST); } break; case CLUTTER_SCROLL: if (event->scroll.axes != NULL) { new_event->scroll.axes = g_memdup2 (event->scroll.axes, sizeof (double) * CLUTTER_INPUT_AXIS_LAST); } break; case CLUTTER_MOTION: if (event->motion.axes != NULL) { new_event->motion.axes = g_memdup2 (event->motion.axes, sizeof (double) * CLUTTER_INPUT_AXIS_LAST); } break; case CLUTTER_TOUCH_BEGIN: case CLUTTER_TOUCH_UPDATE: case CLUTTER_TOUCH_END: case CLUTTER_TOUCH_CANCEL: if (event->touch.axes != NULL) { new_event->touch.axes = g_memdup2 (event->touch.axes, sizeof (double) * CLUTTER_INPUT_AXIS_LAST); } break; case CLUTTER_IM_COMMIT: case CLUTTER_IM_PREEDIT: new_event->im.text = g_strdup (event->im.text); break; default: break; } return new_event; } /** * clutter_event_free: * @event: A #ClutterEvent. * * Frees all resources used by @event. */ void clutter_event_free (ClutterEvent *event) { if (G_LIKELY (event != NULL)) { g_clear_object (&event->any.device); g_clear_object (&event->any.source_device); switch (event->type) { case CLUTTER_BUTTON_PRESS: case CLUTTER_BUTTON_RELEASE: g_free (event->button.axes); break; case CLUTTER_MOTION: g_free (event->motion.axes); break; case CLUTTER_SCROLL: g_free (event->scroll.axes); break; case CLUTTER_TOUCH_BEGIN: case CLUTTER_TOUCH_UPDATE: case CLUTTER_TOUCH_END: case CLUTTER_TOUCH_CANCEL: g_free (event->touch.axes); break; case CLUTTER_IM_COMMIT: case CLUTTER_IM_PREEDIT: g_free (event->im.text); break; default: break; } g_free (event); } } /** * clutter_event_get: * * Pops an event off the event queue. Applications should not need to call * this. * * Return value: A #ClutterEvent or NULL if queue empty */ ClutterEvent * clutter_event_get (void) { ClutterMainContext *context = _clutter_context_get_default (); ClutterEvent *event; event = g_async_queue_try_pop (context->events_queue); return event; } void _clutter_event_push (const ClutterEvent *event, gboolean do_copy) { ClutterMainContext *context = _clutter_context_get_default (); g_assert (context != NULL); if (do_copy) { ClutterEvent *copy; copy = clutter_event_copy (event); event = copy; } g_async_queue_push (context->events_queue, (gpointer) event); g_main_context_wakeup (NULL); } /** * clutter_event_put: * @event: a #ClutterEvent * * Puts a copy of the event on the back of the event queue. The event will * have the %CLUTTER_EVENT_FLAG_SYNTHETIC flag set. If the source is set * event signals will be emitted for this source and capture/bubbling for * its ancestors. If the source is not set it will be generated by picking * or use the actor that currently has keyboard focus */ void clutter_event_put (const ClutterEvent *event) { _clutter_event_push (event, TRUE); } /** * clutter_events_pending: * * Checks if events are pending in the event queue. * * Return value: TRUE if there are pending events, FALSE otherwise. */ gboolean clutter_events_pending (void) { ClutterMainContext *context = _clutter_context_get_default (); g_return_val_if_fail (context != NULL, FALSE); return g_async_queue_length (context->events_queue) > 0; } /** * clutter_get_current_event_time: * * Retrieves the timestamp of the last event, if there is an * event or if the event has a timestamp. * * Return value: the event timestamp, or %CLUTTER_CURRENT_TIME */ guint32 clutter_get_current_event_time (void) { const ClutterEvent* event; event = clutter_get_current_event (); if (event != NULL) return clutter_event_get_time (event); return CLUTTER_CURRENT_TIME; } /** * clutter_get_current_event: * * If an event is currently being processed, return that event. * This function is intended to be used to access event state * that might not be exposed by higher-level widgets. For * example, to get the key modifier state from a Button 'clicked' * event. * * Return value: (transfer none): The current ClutterEvent, or %NULL if none */ const ClutterEvent * clutter_get_current_event (void) { ClutterMainContext *context = _clutter_context_get_default (); g_return_val_if_fail (context != NULL, NULL); return context->current_event != NULL ? context->current_event->data : NULL; } /** * clutter_event_get_source_device: * @event: a #ClutterEvent * * Retrieves the hardware device that originated the event. * * If you need the virtual device, use clutter_event_get_device(). * * If no hardware device originated this event, this function will * return the same device as clutter_event_get_device(). * * Return value: (transfer none): a pointer to a #ClutterInputDevice * or %NULL */ ClutterInputDevice * clutter_event_get_source_device (const ClutterEvent *event) { g_return_val_if_fail (event != NULL, NULL); if (event->any.source_device) return event->any.source_device; else return clutter_event_get_device (event); } /** * clutter_event_get_axes: * @event: a #ClutterEvent * @n_axes: (out): return location for the number of axes returned * * Retrieves the array of axes values attached to the event. * * Return value: (transfer none): an array of axis values */ gdouble * clutter_event_get_axes (const ClutterEvent *event, guint *n_axes) { gdouble *retval = NULL; switch (event->type) { case CLUTTER_NOTHING: case CLUTTER_ENTER: case CLUTTER_LEAVE: case CLUTTER_KEY_PRESS: case CLUTTER_KEY_RELEASE: case CLUTTER_EVENT_LAST: case CLUTTER_PROXIMITY_IN: case CLUTTER_PROXIMITY_OUT: case CLUTTER_DEVICE_ADDED: case CLUTTER_DEVICE_REMOVED: break; case CLUTTER_SCROLL: retval = event->scroll.axes; break; case CLUTTER_BUTTON_PRESS: case CLUTTER_BUTTON_RELEASE: retval = event->button.axes; break; case CLUTTER_TOUCH_BEGIN: case CLUTTER_TOUCH_UPDATE: case CLUTTER_TOUCH_END: case CLUTTER_TOUCH_CANCEL: retval = event->touch.axes; break; case CLUTTER_MOTION: retval = event->motion.axes; break; case CLUTTER_TOUCHPAD_PINCH: case CLUTTER_TOUCHPAD_SWIPE: case CLUTTER_TOUCHPAD_HOLD: case CLUTTER_PAD_BUTTON_PRESS: case CLUTTER_PAD_BUTTON_RELEASE: case CLUTTER_PAD_STRIP: case CLUTTER_PAD_RING: case CLUTTER_IM_COMMIT: case CLUTTER_IM_DELETE: case CLUTTER_IM_PREEDIT: break; } if (n_axes) *n_axes = CLUTTER_INPUT_AXIS_LAST; return retval; } /** * clutter_event_get_distance: * @source: a #ClutterEvent * @target: a #ClutterEvent * * Retrieves the distance between two events, a @source and a @target. * * Return value: the distance between two #ClutterEvent */ float clutter_event_get_distance (const ClutterEvent *source, const ClutterEvent *target) { graphene_point_t p0, p1; clutter_event_get_position (source, &p0); clutter_event_get_position (source, &p1); return graphene_point_distance (&p0, &p1, NULL, NULL); } /** * clutter_event_get_angle: * @source: a #ClutterEvent * @target: a #ClutterEvent * * Retrieves the angle relative from @source to @target. * * The direction of the angle is from the position X axis towards * the positive Y axis. * * Return value: the angle between two #ClutterEvent */ double clutter_event_get_angle (const ClutterEvent *source, const ClutterEvent *target) { graphene_point_t p0, p1; float x_distance, y_distance; double angle; clutter_event_get_position (source, &p0); clutter_event_get_position (target, &p1); if (graphene_point_equal (&p0, &p1)) return 0; graphene_point_distance (&p0, &p1, &x_distance, &y_distance); angle = atan2 (x_distance, y_distance); /* invert the angle, and shift it by 90 degrees */ angle = (2.0 * G_PI) - angle; angle += G_PI / 2.0; /* keep the angle within the [ 0, 360 ] interval */ angle = fmod (angle, 2.0 * G_PI); return angle; } /** * clutter_event_has_shift_modifier: * @event: a #ClutterEvent * * Checks whether @event has the Shift modifier mask set. * * Return value: %TRUE if the event has the Shift modifier mask set */ gboolean clutter_event_has_shift_modifier (const ClutterEvent *event) { return (clutter_event_get_state (event) & CLUTTER_SHIFT_MASK) != FALSE; } /** * clutter_event_has_control_modifier: * @event: a #ClutterEvent * * Checks whether @event has the Control modifier mask set. * * Return value: %TRUE if the event has the Control modifier mask set */ gboolean clutter_event_has_control_modifier (const ClutterEvent *event) { return (clutter_event_get_state (event) & CLUTTER_CONTROL_MASK) != FALSE; } /** * clutter_event_is_pointer_emulated: * @event: a #ClutterEvent * * Checks whether a pointer @event has been generated by the windowing * system. The returned value can be used to distinguish between events * synthesized by the windowing system itself (as opposed by Clutter). * * Return value: %TRUE if the event is pointer emulated */ gboolean clutter_event_is_pointer_emulated (const ClutterEvent *event) { g_return_val_if_fail (event != NULL, FALSE); return !!(event->any.flags & CLUTTER_EVENT_FLAG_POINTER_EMULATED); } gboolean _clutter_event_process_filters (ClutterEvent *event, ClutterActor *event_actor) { ClutterMainContext *context = _clutter_context_get_default (); GList *l, *next; /* Event filters are handled in order from least recently added to * most recently added */ for (l = context->event_filters; l; l = next) { ClutterEventFilter *event_filter = l->data; next = l->next; if (event_filter->stage && event_filter->stage != CLUTTER_STAGE (clutter_actor_get_stage (event_actor))) continue; if (event_filter->func (event, event_actor, event_filter->user_data) == CLUTTER_EVENT_STOP) return CLUTTER_EVENT_STOP; } return CLUTTER_EVENT_PROPAGATE; } /** * clutter_event_add_filter: * @stage: (allow-none): The #ClutterStage to capture events for * @func: The callback function which will be passed all events. * @notify: A #GDestroyNotify * @user_data: A data pointer to pass to the function. * * Adds a function which will be called for all events that Clutter * processes. The function will be called before any signals are * emitted for the event and it will take precedence over any grabs. * * Return value: an identifier for the event filter, to be used * with clutter_event_remove_filter(). */ guint clutter_event_add_filter (ClutterStage *stage, ClutterEventFilterFunc func, GDestroyNotify notify, gpointer user_data) { ClutterMainContext *context = _clutter_context_get_default (); ClutterEventFilter *event_filter = g_new0 (ClutterEventFilter, 1); static guint event_filter_id = 0; event_filter->stage = stage; event_filter->id = ++event_filter_id; event_filter->func = func; event_filter->notify = notify; event_filter->user_data = user_data; /* The event filters are kept in order from least recently added to * most recently added so we must add it to the end */ context->event_filters = g_list_append (context->event_filters, event_filter); return event_filter->id; } /** * clutter_event_remove_filter: * @id: The ID of the event filter, as returned from clutter_event_add_filter() * * Removes an event filter that was previously added with * clutter_event_add_filter(). */ void clutter_event_remove_filter (guint id) { ClutterMainContext *context = _clutter_context_get_default (); GList *l; for (l = context->event_filters; l; l = l->next) { ClutterEventFilter *event_filter = l->data; if (event_filter->id == id) { if (event_filter->notify) event_filter->notify (event_filter->user_data); context->event_filters = g_list_delete_link (context->event_filters, l); g_free (event_filter); return; } } g_warning ("No event filter found for id: %d\n", id); } /** * clutter_event_get_touchpad_gesture_finger_count: * @event: a touchpad swipe/pinch event * * Returns the number of fingers that is triggering the touchpad gesture. * * Returns: the number of fingers in the gesture.4 **/ guint clutter_event_get_touchpad_gesture_finger_count (const ClutterEvent *event) { g_return_val_if_fail (event != NULL, 0); g_return_val_if_fail (event->type == CLUTTER_TOUCHPAD_SWIPE || event->type == CLUTTER_TOUCHPAD_PINCH || event->type == CLUTTER_TOUCHPAD_HOLD, 0); if (event->type == CLUTTER_TOUCHPAD_SWIPE) return event->touchpad_swipe.n_fingers; else if (event->type == CLUTTER_TOUCHPAD_PINCH) return event->touchpad_pinch.n_fingers; else if (event->type == CLUTTER_TOUCHPAD_HOLD) return event->touchpad_hold.n_fingers; return 0; } /** * clutter_event_get_gesture_pinch_angle_delta: * @event: a touchpad pinch event * * Returns the angle delta reported by this specific event. * * Returns: The angle delta relative to the previous event.4 **/ gdouble clutter_event_get_gesture_pinch_angle_delta (const ClutterEvent *event) { g_return_val_if_fail (event != NULL, 0); g_return_val_if_fail (event->type == CLUTTER_TOUCHPAD_PINCH, 0); return event->touchpad_pinch.angle_delta; } /** * clutter_event_get_gesture_pinch_scale: * @event: a touchpad pinch event * * Returns the current scale as reported by @event, 1.0 being the original * distance at the time the corresponding event with phase * %CLUTTER_TOUCHPAD_GESTURE_PHASE_BEGIN is received. * is received. * * Returns: the current pinch gesture scale4 **/ gdouble clutter_event_get_gesture_pinch_scale (const ClutterEvent *event) { g_return_val_if_fail (event != NULL, 0); g_return_val_if_fail (event->type == CLUTTER_TOUCHPAD_PINCH, 0); return event->touchpad_pinch.scale; } /** * clutter_event_get_gesture_phase: * @event: a touchpad gesture event * * Returns the phase of the event, See #ClutterTouchpadGesturePhase. * * Returns: the phase of the gesture event. **/ ClutterTouchpadGesturePhase clutter_event_get_gesture_phase (const ClutterEvent *event) { g_return_val_if_fail (event != NULL, 0); g_return_val_if_fail (event->type == CLUTTER_TOUCHPAD_PINCH || event->type == CLUTTER_TOUCHPAD_SWIPE || event->type == CLUTTER_TOUCHPAD_HOLD, 0); if (event->type == CLUTTER_TOUCHPAD_PINCH) return event->touchpad_pinch.phase; else if (event->type == CLUTTER_TOUCHPAD_SWIPE) return event->touchpad_swipe.phase; else if (event->type == CLUTTER_TOUCHPAD_HOLD) return event->touchpad_hold.phase; /* Shouldn't ever happen */ return CLUTTER_TOUCHPAD_GESTURE_PHASE_BEGIN; }; /** * clutter_event_get_gesture_motion_delta: * @event: A clutter touchpad gesture event * @dx: (out) (allow-none): the displacement relative to the pointer * position in the X axis, or %NULL * @dy: (out) (allow-none): the displacement relative to the pointer * position in the Y axis, or %NULL * * Returns the gesture motion deltas relative to the current pointer * position.4 **/ void clutter_event_get_gesture_motion_delta (const ClutterEvent *event, gdouble *dx, gdouble *dy) { g_return_if_fail (event != NULL); g_return_if_fail (event->type == CLUTTER_TOUCHPAD_PINCH || event->type == CLUTTER_TOUCHPAD_SWIPE || event->type == CLUTTER_TOUCHPAD_HOLD); if (event->type == CLUTTER_TOUCHPAD_PINCH) { if (dx) *dx = event->touchpad_pinch.dx; if (dy) *dy = event->touchpad_pinch.dy; } else if (event->type == CLUTTER_TOUCHPAD_SWIPE) { if (dx) *dx = event->touchpad_swipe.dx; if (dy) *dy = event->touchpad_swipe.dy; } else if (event->type == CLUTTER_TOUCHPAD_HOLD) { if (dx) *dx = 0; if (dy) *dy = 0; } } /** * clutter_event_get_gesture_motion_delta_unaccelerated: * @event: A clutter touchpad gesture event * @dx: (out) (allow-none): the displacement relative to the pointer * position in the X axis, or %NULL * @dy: (out) (allow-none): the displacement relative to the pointer * position in the Y axis, or %NULL * * Returns the unaccelerated gesture motion deltas relative to the current * pointer position. Unlike clutter_event_get_gesture_motion_delta(), * pointer acceleration is ignored. **/ void clutter_event_get_gesture_motion_delta_unaccelerated (const ClutterEvent *event, gdouble *dx, gdouble *dy) { g_return_if_fail (event != NULL); g_return_if_fail (event->type == CLUTTER_TOUCHPAD_PINCH || event->type == CLUTTER_TOUCHPAD_SWIPE || event->type == CLUTTER_TOUCHPAD_HOLD); if (event->type == CLUTTER_TOUCHPAD_PINCH) { if (dx) *dx = event->touchpad_pinch.dx_unaccel; if (dy) *dy = event->touchpad_pinch.dy_unaccel; } else if (event->type == CLUTTER_TOUCHPAD_SWIPE) { if (dx) *dx = event->touchpad_swipe.dx_unaccel; if (dy) *dy = event->touchpad_swipe.dy_unaccel; } else if (event->type == CLUTTER_TOUCHPAD_HOLD) { if (dx) *dx = 0; if (dy) *dy = 0; } } /** * clutter_event_get_scroll_source: * @event: an scroll event * * Returns the #ClutterScrollSource that applies to an scroll event. * * Returns: The source of scroll events6 **/ ClutterScrollSource clutter_event_get_scroll_source (const ClutterEvent *event) { g_return_val_if_fail (event != NULL, CLUTTER_SCROLL_SOURCE_UNKNOWN); g_return_val_if_fail (event->type == CLUTTER_SCROLL, CLUTTER_SCROLL_SOURCE_UNKNOWN); return event->scroll.scroll_source; } /** * clutter_event_get_scroll_finish_flags: * @event: an scroll event * * Returns the #ClutterScrollFinishFlags of an scroll event. Those * can be used to determine whether post-scroll effects like kinetic * scrolling should be applied. * * Returns: The scroll finish flags6 **/ ClutterScrollFinishFlags clutter_event_get_scroll_finish_flags (const ClutterEvent *event) { g_return_val_if_fail (event != NULL, CLUTTER_SCROLL_FINISHED_NONE); g_return_val_if_fail (event->type == CLUTTER_SCROLL, CLUTTER_SCROLL_FINISHED_NONE); return event->scroll.finish_flags; } guint clutter_event_get_mode_group (const ClutterEvent *event) { g_return_val_if_fail (event->type == CLUTTER_PAD_BUTTON_PRESS || event->type == CLUTTER_PAD_BUTTON_RELEASE || event->type == CLUTTER_PAD_RING || event->type == CLUTTER_PAD_STRIP, 0); switch (event->type) { case CLUTTER_PAD_BUTTON_PRESS: case CLUTTER_PAD_BUTTON_RELEASE: return event->pad_button.group; case CLUTTER_PAD_RING: return event->pad_ring.group; case CLUTTER_PAD_STRIP: return event->pad_strip.group; default: return 0; } } /** * clutter_event_get_pad_details: * @event: a pad event * @number: (out) (optional): ring/strip/button number * @mode: (out) (optional): pad mode as per the event * @source: (out) (optional): source of the event * @value: (out) (optional): event axis value * * Returns the details of a pad event. * * Returns: #TRUE if event details could be obtained **/ gboolean clutter_event_get_pad_details (const ClutterEvent *event, guint *number, guint *mode, ClutterInputDevicePadSource *source, gdouble *value) { ClutterInputDevicePadSource s; guint n, m; gdouble v; g_return_val_if_fail (event != NULL, FALSE); g_return_val_if_fail (event->type == CLUTTER_PAD_BUTTON_PRESS || event->type == CLUTTER_PAD_BUTTON_RELEASE || event->type == CLUTTER_PAD_RING || event->type == CLUTTER_PAD_STRIP, FALSE); switch (event->type) { case CLUTTER_PAD_BUTTON_PRESS: case CLUTTER_PAD_BUTTON_RELEASE: n = event->pad_button.button; m = event->pad_button.mode; s = CLUTTER_INPUT_DEVICE_PAD_SOURCE_UNKNOWN; v = 0.0; break; case CLUTTER_PAD_RING: n = event->pad_ring.ring_number; m = event->pad_ring.mode; s = event->pad_ring.ring_source; v = event->pad_ring.angle; break; case CLUTTER_PAD_STRIP: n = event->pad_strip.strip_number; m = event->pad_strip.mode; s = event->pad_strip.strip_source; v = event->pad_strip.value; break; default: return FALSE; } if (number) *number = n; if (mode) *mode = m; if (source) *source = s; if (value) *value = v; return TRUE; } uint32_t clutter_event_get_event_code (const ClutterEvent *event) { if (event->type == CLUTTER_KEY_PRESS || event->type == CLUTTER_KEY_RELEASE) return event->key.evdev_code; else if (event->type == CLUTTER_BUTTON_PRESS || event->type == CLUTTER_BUTTON_RELEASE) return event->button.evdev_code; return 0; } int32_t clutter_event_sequence_get_slot (const ClutterEventSequence *sequence) { g_return_val_if_fail (sequence != NULL, -1); return GPOINTER_TO_INT (sequence) - 1; } int64_t clutter_event_get_time_us (const ClutterEvent *event) { return event->any.time_us; } gboolean clutter_event_get_relative_motion (const ClutterEvent *event, double *dx, double *dy, double *dx_unaccel, double *dy_unaccel, double *dx_constrained, double *dy_constrained) { if (event->type == CLUTTER_MOTION && event->motion.flags & CLUTTER_EVENT_FLAG_RELATIVE_MOTION) { if (dx) *dx = event->motion.dx; if (dy) *dy = event->motion.dy; if (dx_unaccel) *dx_unaccel = event->motion.dx_unaccel; if (dy_unaccel) *dy_unaccel = event->motion.dy_unaccel; if (dx_constrained) *dx_constrained = event->motion.dx_constrained; if (dy_constrained) *dy_constrained = event->motion.dy_constrained; return TRUE; } else return FALSE; } const char * clutter_event_get_im_text (const ClutterEvent *event) { g_return_val_if_fail (event != NULL, NULL); g_return_val_if_fail (event->type == CLUTTER_IM_COMMIT || event->type == CLUTTER_IM_PREEDIT, NULL); return event->im.text; } gboolean clutter_event_get_im_location (const ClutterEvent *event, int32_t *offset, int32_t *anchor) { g_return_val_if_fail (event != NULL, FALSE); g_return_val_if_fail (event->type == CLUTTER_IM_DELETE || event->type == CLUTTER_IM_PREEDIT, FALSE); if (offset) *offset = event->im.offset; if (anchor) *anchor = event->im.anchor; return TRUE; } uint32_t clutter_event_get_im_delete_length (const ClutterEvent *event) { g_return_val_if_fail (event != NULL, 0); g_return_val_if_fail (event->type == CLUTTER_IM_DELETE, 0); return event->im.len; } ClutterPreeditResetMode clutter_event_get_im_preedit_reset_mode (const ClutterEvent *event) { g_return_val_if_fail (event != NULL, CLUTTER_PREEDIT_RESET_CLEAR); g_return_val_if_fail (event->type == CLUTTER_IM_COMMIT || event->type == CLUTTER_IM_PREEDIT, CLUTTER_PREEDIT_RESET_CLEAR); return event->im.mode; } const char * clutter_event_get_name (const ClutterEvent *event) { switch (event->type) { case CLUTTER_KEY_PRESS: return "key-press"; case CLUTTER_KEY_RELEASE: return "key-release"; case CLUTTER_MOTION: return "motion"; case CLUTTER_ENTER: return "enter"; case CLUTTER_LEAVE: return "leave"; case CLUTTER_BUTTON_PRESS: return "button-press"; case CLUTTER_BUTTON_RELEASE: return "button-release"; case CLUTTER_SCROLL: return "scroll"; case CLUTTER_TOUCH_BEGIN: return "touch-begin"; case CLUTTER_TOUCH_UPDATE: return "touch-update"; case CLUTTER_TOUCH_END: return "touch-end"; case CLUTTER_TOUCH_CANCEL: return "touch-cancel"; case CLUTTER_TOUCHPAD_PINCH: return "touchpad-pinch"; case CLUTTER_TOUCHPAD_SWIPE: return "touchpad-swipe"; case CLUTTER_TOUCHPAD_HOLD: return "touchpad-hold"; case CLUTTER_PROXIMITY_IN: return "proximity-in"; case CLUTTER_PROXIMITY_OUT: return "proximity-out"; case CLUTTER_PAD_BUTTON_PRESS: return "pad-button-press"; case CLUTTER_PAD_BUTTON_RELEASE: return "pad-button-release"; case CLUTTER_PAD_STRIP: return "pad-strip"; case CLUTTER_PAD_RING: return "pad-ring"; case CLUTTER_DEVICE_ADDED: return "device-added"; case CLUTTER_DEVICE_REMOVED: return "device-removed"; case CLUTTER_IM_COMMIT: return "im-commit"; case CLUTTER_IM_DELETE: return "im-delete"; case CLUTTER_IM_PREEDIT: return "im-preedit"; case CLUTTER_NOTHING: case CLUTTER_EVENT_LAST: break; } g_assert_not_reached (); } ClutterEvent * clutter_event_key_new (ClutterEventType type, ClutterEventFlags flags, int64_t timestamp_us, ClutterInputDevice *source_device, ClutterModifierSet raw_modifiers, ClutterModifierType modifiers, uint32_t keyval, uint32_t evcode, uint32_t keycode, gunichar unicode_value) { ClutterEvent *event; ClutterSeat *seat; g_return_val_if_fail (type == CLUTTER_KEY_PRESS || type == CLUTTER_KEY_RELEASE, NULL); g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (source_device), NULL); seat = clutter_input_device_get_seat (source_device); event = clutter_event_new (type); event->key.time_us = timestamp_us; event->key.flags = flags; event->key.raw_modifiers = raw_modifiers; event->key.modifier_state = modifiers; event->key.keyval = keyval; event->key.hardware_keycode = keycode; event->key.unicode_value = unicode_value; event->key.evdev_code = evcode; g_set_object (&event->key.device, clutter_seat_get_keyboard (seat)); g_set_object (&event->key.source_device, source_device); return event; } ClutterEvent * clutter_event_button_new (ClutterEventType type, ClutterEventFlags flags, int64_t timestamp_us, ClutterInputDevice *source_device, ClutterInputDeviceTool *tool, ClutterModifierType modifiers, graphene_point_t coords, int button, uint32_t evcode, double *axes) { ClutterEvent *event; g_return_val_if_fail (type == CLUTTER_BUTTON_PRESS || type == CLUTTER_BUTTON_RELEASE, NULL); g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (source_device), NULL); g_return_val_if_fail (!tool || CLUTTER_IS_INPUT_DEVICE_TOOL (tool), NULL); event = clutter_event_new (type); event->button.time_us = timestamp_us; event->button.flags = flags; event->button.x = coords.x; event->button.y = coords.y; event->button.modifier_state = modifiers; event->button.button = button; event->button.axes = axes; event->button.evdev_code = evcode; event->button.tool = tool; g_set_object (&event->button.source_device, source_device); if (tool) { g_set_object (&event->button.device, source_device); } else { ClutterSeat *seat; seat = clutter_input_device_get_seat (source_device); g_set_object (&event->button.device, clutter_seat_get_pointer (seat)); } return event; } ClutterEvent * clutter_event_motion_new (ClutterEventFlags flags, int64_t timestamp_us, ClutterInputDevice *source_device, ClutterInputDeviceTool *tool, ClutterModifierType modifiers, graphene_point_t coords, graphene_point_t delta, graphene_point_t delta_unaccel, graphene_point_t delta_constrained, double *axes) { ClutterEvent *event; g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (source_device), NULL); g_return_val_if_fail (!tool || CLUTTER_IS_INPUT_DEVICE_TOOL (tool), NULL); event = clutter_event_new (CLUTTER_MOTION); event->motion.time_us = timestamp_us; event->motion.flags = flags; event->motion.x = coords.x; event->motion.y = coords.y; event->motion.modifier_state = modifiers; event->motion.axes = axes; event->motion.dx = delta.x; event->motion.dy = delta.y; event->motion.dx_unaccel = delta_unaccel.x; event->motion.dy_unaccel = delta_unaccel.y; event->motion.dx_constrained = delta_constrained.x; event->motion.dy_constrained = delta_constrained.y; event->motion.tool = tool; g_set_object (&event->motion.source_device, source_device); if (tool) { g_set_object (&event->motion.device, source_device); } else { ClutterSeat *seat; seat = clutter_input_device_get_seat (source_device); g_set_object (&event->motion.device, clutter_seat_get_pointer (seat)); } return event; } ClutterEvent * clutter_event_scroll_smooth_new (ClutterEventFlags flags, int64_t timestamp_us, ClutterInputDevice *source_device, ClutterInputDeviceTool *tool, ClutterModifierType modifiers, graphene_point_t coords, graphene_point_t delta, ClutterScrollSource scroll_source, ClutterScrollFinishFlags finish_flags) { ClutterEvent *event; g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (source_device), NULL); g_return_val_if_fail (!tool || CLUTTER_IS_INPUT_DEVICE_TOOL (tool), NULL); event = clutter_event_new (CLUTTER_SCROLL); event->scroll.time_us = timestamp_us; event->scroll.flags = flags; event->scroll.x = coords.x; event->scroll.y = coords.y; event->scroll.delta_x = delta.x; event->scroll.delta_y = delta.y; event->scroll.direction = CLUTTER_SCROLL_SMOOTH; event->scroll.modifier_state = modifiers; event->scroll.scroll_source = scroll_source; event->scroll.finish_flags = finish_flags; event->scroll.tool = tool; g_set_object (&event->scroll.source_device, source_device); if (tool) { g_set_object (&event->scroll.device, source_device); } else { ClutterSeat *seat; seat = clutter_input_device_get_seat (source_device); g_set_object (&event->scroll.device, clutter_seat_get_pointer (seat)); } return event; } ClutterEvent * clutter_event_scroll_discrete_new (ClutterEventFlags flags, int64_t timestamp_us, ClutterInputDevice *source_device, ClutterInputDeviceTool *tool, ClutterModifierType modifiers, graphene_point_t coords, ClutterScrollDirection direction) { ClutterEvent *event; g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (source_device), NULL); g_return_val_if_fail (!tool || CLUTTER_IS_INPUT_DEVICE_TOOL (tool), NULL); event = clutter_event_new (CLUTTER_SCROLL); event->scroll.time_us = timestamp_us; event->scroll.flags = flags; event->scroll.x = coords.x; event->scroll.y = coords.y; event->scroll.direction = direction; event->scroll.modifier_state = modifiers; event->scroll.tool = tool; g_set_object (&event->scroll.source_device, source_device); if (tool) { g_set_object (&event->scroll.device, source_device); } else { ClutterSeat *seat; seat = clutter_input_device_get_seat (source_device); g_set_object (&event->scroll.device, clutter_seat_get_pointer (seat)); } return event; } ClutterEvent * clutter_event_crossing_new (ClutterEventType type, ClutterEventFlags flags, int64_t timestamp_us, ClutterInputDevice *source_device, ClutterEventSequence *sequence, graphene_point_t coords, ClutterActor *source, ClutterActor *related) { ClutterInputDevice *device; ClutterEvent *event; g_return_val_if_fail (type == CLUTTER_ENTER || type == CLUTTER_LEAVE, NULL); g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (source_device), NULL); if (!!(clutter_input_device_get_capabilities (source_device) & CLUTTER_INPUT_CAPABILITY_TABLET_TOOL)) { device = source_device; } else { ClutterSeat *seat; seat = clutter_input_device_get_seat (source_device); device = clutter_seat_get_pointer (seat); } event = clutter_event_new (type); event->crossing.time_us = timestamp_us; event->crossing.flags = flags; event->crossing.x = coords.x; event->crossing.y = coords.y; event->crossing.sequence = sequence; event->crossing.source = source; event->crossing.related = related; g_set_object (&event->crossing.device, device); g_set_object (&event->crossing.source_device, source_device); return event; } ClutterEvent * clutter_event_touch_new (ClutterEventType type, ClutterEventFlags flags, int64_t timestamp_us, ClutterInputDevice *source_device, ClutterEventSequence *sequence, ClutterModifierType modifiers, graphene_point_t coords) { ClutterEvent *event; ClutterSeat *seat; g_return_val_if_fail (type == CLUTTER_TOUCH_BEGIN || type == CLUTTER_TOUCH_UPDATE || type == CLUTTER_TOUCH_END, NULL); g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (source_device), NULL); g_return_val_if_fail (sequence != NULL, NULL); seat = clutter_input_device_get_seat (source_device); event = clutter_event_new (type); event->touch.time_us = timestamp_us; event->touch.flags = flags; event->touch.x = coords.x; event->touch.y = coords.y; event->touch.modifier_state = modifiers; event->touch.sequence = sequence; /* This has traditionally been the virtual pointer device */ g_set_object (&event->touch.device, clutter_seat_get_pointer (seat)); g_set_object (&event->touch.source_device, source_device); return event; } ClutterEvent * clutter_event_touch_cancel_new (ClutterEventFlags flags, int64_t timestamp_us, ClutterInputDevice *source_device, ClutterEventSequence *sequence) { ClutterEvent *event; ClutterSeat *seat; g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (source_device), NULL); g_return_val_if_fail (sequence != NULL, NULL); seat = clutter_input_device_get_seat (source_device); event = clutter_event_new (CLUTTER_TOUCH_CANCEL); event->touch.time_us = timestamp_us; event->touch.flags = flags; event->touch.sequence = sequence; /* This has traditionally been the virtual pointer device */ g_set_object (&event->touch.device, clutter_seat_get_pointer (seat)); g_set_object (&event->touch.source_device, source_device); return event; } ClutterEvent * clutter_event_proximity_new (ClutterEventType type, ClutterEventFlags flags, int64_t timestamp_us, ClutterInputDevice *source_device, ClutterInputDeviceTool *tool) { ClutterEvent *event; g_return_val_if_fail (type == CLUTTER_PROXIMITY_IN || type == CLUTTER_PROXIMITY_OUT, NULL); g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (source_device), NULL); g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE_TOOL (tool), NULL); event = clutter_event_new (type); event->proximity.time_us = timestamp_us; event->proximity.flags = flags; event->proximity.tool = tool; g_set_object (&event->proximity.device, source_device); g_set_object (&event->proximity.source_device, source_device); return event; } ClutterEvent * clutter_event_touchpad_pinch_new (ClutterEventFlags flags, int64_t timestamp_us, ClutterInputDevice *source_device, ClutterTouchpadGesturePhase phase, uint32_t fingers, graphene_point_t coords, graphene_point_t delta, graphene_point_t delta_unaccel, float angle, float scale) { ClutterEvent *event; ClutterSeat *seat; g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (source_device), NULL); seat = clutter_input_device_get_seat (source_device); event = clutter_event_new (CLUTTER_TOUCHPAD_PINCH); event->touchpad_pinch.time_us = timestamp_us; event->touchpad_pinch.flags = flags; event->touchpad_pinch.phase = phase; event->touchpad_pinch.x = coords.x; event->touchpad_pinch.y = coords.y; event->touchpad_pinch.dx = delta.x; event->touchpad_pinch.dy = delta.y; event->touchpad_pinch.dx_unaccel = delta_unaccel.x; event->touchpad_pinch.dy_unaccel = delta_unaccel.y; event->touchpad_pinch.angle_delta = angle; event->touchpad_pinch.scale = scale; event->touchpad_pinch.n_fingers = fingers; g_set_object (&event->touchpad_pinch.device, clutter_seat_get_pointer (seat)); g_set_object (&event->touchpad_pinch.source_device, source_device); return event; } ClutterEvent * clutter_event_touchpad_swipe_new (ClutterEventFlags flags, int64_t timestamp_us, ClutterInputDevice *source_device, ClutterTouchpadGesturePhase phase, uint32_t fingers, graphene_point_t coords, graphene_point_t delta, graphene_point_t delta_unaccel) { ClutterEvent *event; ClutterSeat *seat; g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (source_device), NULL); seat = clutter_input_device_get_seat (source_device); event = clutter_event_new (CLUTTER_TOUCHPAD_SWIPE); event->touchpad_swipe.time_us = timestamp_us; event->touchpad_swipe.flags = flags; event->touchpad_swipe.phase = phase; event->touchpad_swipe.x = coords.x; event->touchpad_swipe.y = coords.y; event->touchpad_swipe.dx = delta.x; event->touchpad_swipe.dy = delta.y; event->touchpad_swipe.dx_unaccel = delta_unaccel.x; event->touchpad_swipe.dy_unaccel = delta_unaccel.y; event->touchpad_swipe.n_fingers = fingers; g_set_object (&event->touchpad_swipe.device, clutter_seat_get_pointer (seat)); g_set_object (&event->touchpad_swipe.source_device, source_device); return event; } ClutterEvent * clutter_event_touchpad_hold_new (ClutterEventFlags flags, int64_t timestamp_us, ClutterInputDevice *source_device, ClutterTouchpadGesturePhase phase, uint32_t fingers, graphene_point_t coords) { ClutterEvent *event; ClutterSeat *seat; g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (source_device), NULL); seat = clutter_input_device_get_seat (source_device); event = clutter_event_new (CLUTTER_TOUCHPAD_HOLD); event->touchpad_hold.time_us = timestamp_us; event->touchpad_hold.flags = flags; event->touchpad_hold.phase = phase; event->touchpad_hold.x = coords.x; event->touchpad_hold.y = coords.y; event->touchpad_hold.n_fingers = fingers; g_set_object (&event->touchpad_hold.device, clutter_seat_get_pointer (seat)); g_set_object (&event->touchpad_hold.source_device, source_device); return event; } ClutterEvent * clutter_event_pad_button_new (ClutterEventType type, ClutterEventFlags flags, int64_t timestamp_us, ClutterInputDevice *source_device, uint32_t button, uint32_t group, uint32_t mode) { ClutterEvent *event; g_return_val_if_fail (type == CLUTTER_PAD_BUTTON_PRESS || type == CLUTTER_PAD_BUTTON_RELEASE, NULL); g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (source_device), NULL); event = clutter_event_new (type); event->pad_button.time_us = timestamp_us; event->pad_button.flags = flags; event->pad_button.button = button; event->pad_button.group = group; event->pad_button.mode = mode; g_set_object (&event->pad_button.device, source_device); g_set_object (&event->pad_button.source_device, source_device); return event; } ClutterEvent * clutter_event_pad_strip_new (ClutterEventFlags flags, int64_t timestamp_us, ClutterInputDevice *source_device, ClutterInputDevicePadSource strip_source, uint32_t strip, uint32_t group, double value, uint32_t mode) { ClutterEvent *event; g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (source_device), NULL); event = clutter_event_new (CLUTTER_PAD_STRIP); event->pad_strip.time_us = timestamp_us; event->pad_strip.flags = flags; event->pad_strip.strip_source = strip_source; event->pad_strip.strip_number = strip; event->pad_strip.group = group; event->pad_strip.value = value; event->pad_strip.mode = mode; g_set_object (&event->pad_strip.device, source_device); g_set_object (&event->pad_strip.source_device, source_device); return event; } ClutterEvent * clutter_event_pad_ring_new (ClutterEventFlags flags, int64_t timestamp_us, ClutterInputDevice *source_device, ClutterInputDevicePadSource ring_source, uint32_t ring, uint32_t group, double angle, uint32_t mode) { ClutterEvent *event; g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (source_device), NULL); event = clutter_event_new (CLUTTER_PAD_RING); event->pad_ring.time_us = timestamp_us; event->pad_ring.flags = flags; event->pad_ring.ring_source = ring_source; event->pad_ring.ring_number = ring; event->pad_ring.group = group; event->pad_ring.angle = angle; event->pad_ring.mode = mode; g_set_object (&event->pad_ring.device, source_device); g_set_object (&event->pad_ring.source_device, source_device); return event; } ClutterEvent * clutter_event_device_notify_new (ClutterEventType type, ClutterEventFlags flags, int64_t timestamp_us, ClutterInputDevice *source_device) { ClutterEvent *event; g_return_val_if_fail (type == CLUTTER_DEVICE_ADDED || type == CLUTTER_DEVICE_REMOVED, NULL); g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (source_device), NULL); event = clutter_event_new (type); event->device.time_us = timestamp_us; event->device.flags = flags; g_set_object (&event->device.device, source_device); return event; } ClutterEvent * clutter_event_im_new (ClutterEventType type, ClutterEventFlags flags, int64_t timestamp_us, ClutterSeat *seat, const char *text, int32_t offset, int32_t anchor, uint32_t len, ClutterPreeditResetMode mode) { ClutterEvent *event; g_return_val_if_fail (type == CLUTTER_IM_COMMIT || type == CLUTTER_IM_DELETE || type == CLUTTER_IM_PREEDIT, NULL); event = clutter_event_new (type); event->im.time_us = timestamp_us; event->im.flags = flags; event->im.text = g_strdup (text); event->im.offset = offset; event->im.anchor = anchor; event->im.len = len; event->im.mode = mode; g_set_object (&event->im.device, clutter_seat_get_keyboard (seat)); return event; } static const char * scroll_source_to_string (ClutterScrollSource scroll_source) { switch (scroll_source) { case CLUTTER_SCROLL_SOURCE_UNKNOWN: return "unknown"; case CLUTTER_SCROLL_SOURCE_WHEEL: return "wheel"; case CLUTTER_SCROLL_SOURCE_FINGER: return "finger"; case CLUTTER_SCROLL_SOURCE_CONTINUOUS: return "continuous"; } g_return_val_if_reached (""); } static const char * touchpad_gesture_phase_to_string (ClutterTouchpadGesturePhase phase) { switch (phase) { case CLUTTER_TOUCHPAD_GESTURE_PHASE_BEGIN: return "begin"; case CLUTTER_TOUCHPAD_GESTURE_PHASE_UPDATE: return "update"; case CLUTTER_TOUCHPAD_GESTURE_PHASE_END: return "end"; case CLUTTER_TOUCHPAD_GESTURE_PHASE_CANCEL: return "cancel"; } g_return_val_if_reached (""); } static const char * pad_source_to_string (ClutterInputDevicePadSource pad_source) { switch (pad_source) { case CLUTTER_INPUT_DEVICE_PAD_SOURCE_UNKNOWN: return "unknown"; case CLUTTER_INPUT_DEVICE_PAD_SOURCE_FINGER: return "finger"; } g_return_val_if_reached (""); } static const char * scroll_direction_to_string (ClutterScrollDirection scroll_direction) { switch (scroll_direction) { case CLUTTER_SCROLL_SMOOTH: g_warn_if_reached (); return ""; case CLUTTER_SCROLL_LEFT: return "left"; case CLUTTER_SCROLL_RIGHT: return "right"; case CLUTTER_SCROLL_UP: return "up"; case CLUTTER_SCROLL_DOWN: return "down"; } g_return_val_if_reached (""); } static char * generate_event_description (const ClutterEvent *event) { switch (event->type) { case CLUTTER_KEY_PRESS: case CLUTTER_KEY_RELEASE: if (g_strcmp0 (g_getenv ("MUTTER_DEBUG_LOG_KEYCODES"), "1") == 0) { char unicode[7] = {}; if (event->key.unicode_value) g_unichar_to_utf8 (event->key.unicode_value, unicode); return g_strdup_printf ("keycode=%u, evdev=%u, " "keysym=%u, unicode='%s'", event->key.hardware_keycode, event->key.evdev_code, event->key.keyval, event->key.unicode_value ? unicode : "N\\A"); } else { return g_strdup ("(hidden)"); } case CLUTTER_MOTION: return g_strdup_printf ("abs=(%f, %f), rel=(%f, %f), unaccel-rel=(%f, %f)", event->motion.x, event->motion.y, event->motion.dx, event->motion.dy, event->motion.dx_unaccel, event->motion.dy_unaccel); case CLUTTER_BUTTON_PRESS: case CLUTTER_BUTTON_RELEASE: return g_strdup_printf ("button=%u, evdev=%u", event->button.button, event->button.evdev_code); case CLUTTER_SCROLL: if (event->scroll.direction == CLUTTER_SCROLL_SMOOTH) { double dx, dy; ClutterScrollSource scroll_source; clutter_event_get_scroll_delta (event, &dx, &dy); scroll_source = event->scroll.scroll_source; return g_strdup_printf ("source=%s, rel: (%f, %f)", scroll_source_to_string (scroll_source), dx, dy); } else { ClutterScrollDirection direction = event->scroll.direction; return g_strdup_printf ("direction=%s", scroll_direction_to_string (direction)); } case CLUTTER_TOUCH_BEGIN: case CLUTTER_TOUCH_UPDATE: case CLUTTER_TOUCH_END: case CLUTTER_TOUCH_CANCEL: return g_strdup_printf ("slot=%d, abs=(%f, %f)", GPOINTER_TO_INT (event->touch.sequence), event->touch.x, event->touch.y); case CLUTTER_TOUCHPAD_PINCH: return g_strdup_printf ("phase=%s, rel=(%f, %f), unaccel-rel=(%f, %f), " "angle-delta=%f, scale=%f, n-fingers=%d", touchpad_gesture_phase_to_string (event->touchpad_pinch.phase), event->touchpad_pinch.dx, event->touchpad_pinch.dy, event->touchpad_pinch.dx_unaccel, event->touchpad_pinch.dy_unaccel, event->touchpad_pinch.angle_delta, event->touchpad_pinch.scale, event->touchpad_pinch.n_fingers); case CLUTTER_TOUCHPAD_SWIPE: return g_strdup_printf ("phase=%s, rel=(%f, %f), unaccel-rel=(%f, %f), " "n-fingers=%d", touchpad_gesture_phase_to_string (event->touchpad_pinch.phase), event->touchpad_swipe.dx, event->touchpad_swipe.dy, event->touchpad_swipe.dx_unaccel, event->touchpad_swipe.dy_unaccel, event->touchpad_swipe.n_fingers); case CLUTTER_TOUCHPAD_HOLD: return g_strdup_printf ("phase=%s, n-fingers=%d", touchpad_gesture_phase_to_string (event->touchpad_pinch.phase), event->touchpad_hold.n_fingers); case CLUTTER_PROXIMITY_IN: case CLUTTER_PROXIMITY_OUT: return g_strdup (""); case CLUTTER_PAD_BUTTON_PRESS: case CLUTTER_PAD_BUTTON_RELEASE: return g_strdup_printf ("button=%u, group=%u, mode=%u", event->pad_button.button, event->pad_button.group, event->pad_button.mode); case CLUTTER_PAD_STRIP: return g_strdup_printf ("source=%s (%d), value=%f, group=%u, mode=%u", pad_source_to_string (event->pad_strip.strip_source), event->pad_strip.strip_number, event->pad_strip.value, event->pad_strip.group, event->pad_strip.mode); case CLUTTER_PAD_RING: return g_strdup_printf ("source=%s (%d), angle=%f, group=%u, mode=%u", pad_source_to_string (event->pad_ring.ring_source), event->pad_ring.ring_number, event->pad_ring.angle, event->pad_ring.group, event->pad_ring.mode); case CLUTTER_DEVICE_ADDED: case CLUTTER_DEVICE_REMOVED: { ClutterInputDevice *device; device = clutter_event_get_device (event); return g_strdup_printf ("%s (%s)", clutter_input_device_get_device_name (device), clutter_input_device_get_device_node (device)); } default: g_warn_if_reached (); return g_strdup (""); } } static char * generate_modifiers_description (const ClutterEvent *event) { ClutterModifierType modifiers; GString *str; modifiers = clutter_event_get_state (event); if (modifiers == 0) return g_strdup ("none"); str = g_string_new (NULL); if (modifiers & CLUTTER_SHIFT_MASK) g_string_append (str, "shift "); if (modifiers & CLUTTER_LOCK_MASK) g_string_append (str, "lock "); if (modifiers & CLUTTER_CONTROL_MASK) g_string_append (str, "control "); if (modifiers & CLUTTER_MOD1_MASK) g_string_append (str, "mod1 "); if (modifiers & CLUTTER_MOD2_MASK) g_string_append (str, "mod2 "); if (modifiers & CLUTTER_MOD3_MASK) g_string_append (str, "mod3 "); if (modifiers & CLUTTER_MOD4_MASK) g_string_append (str, "mod4 "); if (modifiers & CLUTTER_MOD5_MASK) g_string_append (str, "mod5 "); if (modifiers & CLUTTER_BUTTON1_MASK) g_string_append (str, "button1 "); if (modifiers & CLUTTER_BUTTON2_MASK) g_string_append (str, "button2 "); if (modifiers & CLUTTER_BUTTON3_MASK) g_string_append (str, "button3 "); if (modifiers & CLUTTER_BUTTON4_MASK) g_string_append (str, "button4 "); if (modifiers & CLUTTER_BUTTON5_MASK) g_string_append (str, "button5 "); if (modifiers & CLUTTER_SUPER_MASK) g_string_append (str, "super "); if (modifiers & CLUTTER_HYPER_MASK) g_string_append (str, "hyper "); if (modifiers & CLUTTER_META_MASK) g_string_append (str, "meta "); if (modifiers & CLUTTER_RELEASE_MASK) g_string_append (str, "release "); /* Delete trailing space */ g_string_erase (str, str->len - 1, 1); return g_string_free_and_steal (str); } char * clutter_event_describe (const ClutterEvent *event) { g_autofree char *event_description = NULL; g_autofree char *modifiers_description = NULL; ClutterInputDevice *source_device; source_device = clutter_event_get_source_device (event); event_description = generate_event_description (event); modifiers_description = generate_modifiers_description (event); return g_strdup_printf ("'%s'%s%s, time=%" G_GINT64_FORMAT " us, modifiers=%s, %s", clutter_event_get_name (event), source_device ? " from " : "", source_device ? clutter_input_device_get_device_node (source_device) : "", event->any.time_us, modifiers_description, event_description); }