static GesturePoint * gesture_register_point (ClutterGestureAction *action, ClutterEvent *event) { ClutterGestureActionPrivate *priv = action->priv; GesturePoint *point = NULL; if (priv->points->len >= MAX_GESTURE_POINTS) return NULL; g_array_set_size (priv->points, priv->points->len + 1); point = &g_array_index (priv->points, GesturePoint, priv->points->len - 1); point->last_event = clutter_event_copy (event); point->device = clutter_event_get_device (event); clutter_event_get_coords (event, &point->press_x, &point->press_y); point->last_motion_x = point->press_x; point->last_motion_y = point->press_y; point->last_motion_time = clutter_event_get_time (event); point->last_delta_x = point->last_delta_y = 0; point->last_delta_time = 0; if (clutter_event_type (event) != CLUTTER_BUTTON_PRESS) point->sequence = clutter_event_get_event_sequence (event); else point->sequence = NULL; return point; }
static GesturePoint * gesture_find_point (ClutterGestureAction *action, ClutterEvent *event, gint *position) { ClutterGestureActionPrivate *priv = action->priv; GesturePoint *point = NULL; ClutterEventType type = clutter_event_type (event); ClutterInputDevice *device = clutter_event_get_device (event); ClutterEventSequence *sequence = NULL; gint i; if ((type != CLUTTER_BUTTON_PRESS) && (type != CLUTTER_BUTTON_RELEASE) && (type != CLUTTER_MOTION)) sequence = clutter_event_get_event_sequence (event); for (i = 0; i < priv->points->len; i++) { if ((g_array_index (priv->points, GesturePoint, i).device == device) && (g_array_index (priv->points, GesturePoint, i).sequence == sequence)) { if (position != NULL) *position = i; point = &g_array_index (priv->points, GesturePoint, i); break; } } return point; }
static gboolean st_button_touch_event (ClutterActor *actor, ClutterTouchEvent *event) { StButton *button = ST_BUTTON (actor); StButtonPrivate *priv = st_button_get_instance_private (button); StButtonMask mask = ST_BUTTON_MASK_FROM_BUTTON (1); ClutterEventSequence *sequence; ClutterInputDevice *device; if (priv->pressed != 0) return CLUTTER_EVENT_PROPAGATE; device = clutter_event_get_device ((ClutterEvent*) event); sequence = clutter_event_get_event_sequence ((ClutterEvent*) event); if (event->type == CLUTTER_TOUCH_BEGIN && !priv->press_sequence) { clutter_input_device_sequence_grab (device, sequence, actor); st_button_press (button, device, 0, sequence); return CLUTTER_EVENT_STOP; } else if (event->type == CLUTTER_TOUCH_END && priv->device == device && priv->press_sequence == sequence) { st_button_release (button, device, mask, 0, sequence); clutter_input_device_sequence_ungrab (device, sequence); return CLUTTER_EVENT_STOP; } return CLUTTER_EVENT_PROPAGATE; }
static gboolean sequence_is_pointer_emulated (MetaDisplay *display, const ClutterEvent *event) { ClutterEventSequence *sequence; sequence = clutter_event_get_event_sequence (event); if (!sequence) return FALSE; if (clutter_event_is_pointer_emulated (event)) return TRUE; #ifdef HAVE_NATIVE_BACKEND MetaBackend *backend = meta_get_backend (); /* When using Clutter's native input backend there is no concept of * pointer emulating sequence, we still must make up our own to be * able to implement single-touch (hence pointer alike) behavior. * * This is implemented similarly to X11, where only the first touch * on screen gets the "pointer emulated" flag, and it won't get assigned * to another sequence until the next first touch on an idle touchscreen. */ if (META_IS_BACKEND_NATIVE (backend)) { MetaGestureTracker *tracker; tracker = meta_display_get_gesture_tracker (display); if (event->type == CLUTTER_TOUCH_BEGIN && meta_gesture_tracker_get_n_current_touches (tracker) == 0) return TRUE; } #endif /* HAVE_NATIVE_BACKEND */ return FALSE; }
static gboolean meta_display_handle_event (MetaDisplay *display, const ClutterEvent *event) { MetaWindow *window; gboolean bypass_clutter = FALSE; G_GNUC_UNUSED gboolean bypass_wayland = FALSE; MetaGestureTracker *tracker; ClutterEventSequence *sequence; ClutterInputDevice *source; sequence = clutter_event_get_event_sequence (event); /* Set the pointer emulating sequence on touch begin, if eligible */ if (event->type == CLUTTER_TOUCH_BEGIN) { if (sequence_is_pointer_emulated (display, event)) { /* This is the new pointer emulating sequence */ display->pointer_emulating_sequence = sequence; } else if (display->pointer_emulating_sequence == sequence) { /* This sequence was "pointer emulating" in a prior incarnation, * but now it isn't. We unset the pointer emulating sequence at * this point so the current sequence is not mistaken as pointer * emulating, while we've ensured that it's been deemed * "pointer emulating" throughout all of the event processing * of the previous incarnation. */ display->pointer_emulating_sequence = NULL; } } #ifdef HAVE_WAYLAND MetaWaylandCompositor *compositor = NULL; if (meta_is_wayland_compositor ()) { compositor = meta_wayland_compositor_get_default (); meta_wayland_compositor_update (compositor, event); } #endif if (!display->current_pad_osd && (event->type == CLUTTER_PAD_BUTTON_PRESS || event->type == CLUTTER_PAD_BUTTON_RELEASE)) { MetaBackend *backend = meta_get_backend (); if (meta_input_settings_handle_pad_button (meta_backend_get_input_settings (backend), clutter_event_get_source_device (event), event->type == CLUTTER_PAD_BUTTON_PRESS, event->pad_button.button)) { bypass_wayland = bypass_clutter = TRUE; goto out; } } source = clutter_event_get_source_device (event); if (source) { meta_backend_update_last_device (meta_get_backend (), clutter_input_device_get_device_id (source)); } #ifdef HAVE_WAYLAND if (meta_is_wayland_compositor () && event->type == CLUTTER_MOTION) { MetaWaylandCompositor *compositor; compositor = meta_wayland_compositor_get_default (); if (meta_wayland_tablet_manager_consumes_event (compositor->tablet_manager, event)) { meta_wayland_tablet_manager_update_cursor_position (compositor->tablet_manager, event); } else { MetaCursorTracker *tracker = meta_cursor_tracker_get_for_screen (NULL); meta_cursor_tracker_update_position (tracker, event->motion.x, event->motion.y); } display->monitor_cache_invalidated = TRUE; } #endif handle_idletime_for_event (event); window = get_window_for_event (display, event); display->current_time = event->any.time; if (window && !window->override_redirect && (event->type == CLUTTER_KEY_PRESS || event->type == CLUTTER_BUTTON_PRESS || event->type == CLUTTER_TOUCH_BEGIN)) { if (CurrentTime == display->current_time) { /* We can't use missing (i.e. invalid) timestamps to set user time, * nor do we want to use them to sanity check other timestamps. * See bug 313490 for more details. */ meta_warning ("Event has no timestamp! You may be using a broken " "program such as xse. Please ask the authors of that " "program to fix it.\n"); } else { meta_window_set_user_time (window, display->current_time); meta_display_sanity_check_timestamps (display, display->current_time); } } tracker = meta_display_get_gesture_tracker (display); if (meta_gesture_tracker_handle_event (tracker, event)) { bypass_wayland = bypass_clutter = TRUE; goto out; } if (display->event_route == META_EVENT_ROUTE_WINDOW_OP) { if (meta_window_handle_mouse_grab_op_event (window, event)) { bypass_clutter = TRUE; bypass_wayland = TRUE; goto out; } } /* For key events, it's important to enforce single-handling, or * we can get into a confused state. So if a keybinding is * handled (because it's one of our hot-keys, or because we are * in a keyboard-grabbed mode like moving a window, we don't * want to pass the key event to the compositor or Wayland at all. */ if (meta_keybindings_process_event (display, window, event)) { bypass_clutter = TRUE; bypass_wayland = TRUE; goto out; } /* Do not pass keyboard events to Wayland if key focus is not on the * stage in normal mode (e.g. during keynav in the panel) */ if (display->event_route == META_EVENT_ROUTE_NORMAL) { if (IS_KEY_EVENT (event) && !stage_has_key_focus ()) { bypass_wayland = TRUE; goto out; } } if (display->current_pad_osd) { bypass_wayland = TRUE; goto out; } if (window) { /* Events that are likely to trigger compositor gestures should * be known to clutter so they can propagate along the hierarchy. * Gesture-wise, there's two groups of events we should be getting * here: * - CLUTTER_TOUCH_* with a touch sequence that's not yet accepted * by the gesture tracker, these might trigger gesture actions * into recognition. Already accepted touch sequences are handled * directly by meta_gesture_tracker_handle_event(). * - CLUTTER_TOUCHPAD_* events over windows. These can likewise * trigger ::captured-event handlers along the way. */ bypass_clutter = !IS_GESTURE_EVENT (event); meta_window_handle_ungrabbed_event (window, event); /* This might start a grab op. If it does, then filter out the * event, and if it doesn't, replay the event to release our * own sync grab. */ if (display->event_route == META_EVENT_ROUTE_WINDOW_OP || display->event_route == META_EVENT_ROUTE_FRAME_BUTTON) { bypass_clutter = TRUE; bypass_wayland = TRUE; } else { /* Only replay button press events, since that's where we * have the synchronous grab. */ if (event->type == CLUTTER_BUTTON_PRESS) { MetaBackend *backend = meta_get_backend (); if (META_IS_BACKEND_X11 (backend)) { Display *xdisplay = meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend)); meta_verbose ("Allowing events time %u\n", (unsigned int)event->button.time); XIAllowEvents (xdisplay, clutter_event_get_device_id (event), XIReplayDevice, event->button.time); } } } goto out; } out: /* If the compositor has a grab, don't pass that through to Wayland */ if (display->event_route == META_EVENT_ROUTE_COMPOSITOR_GRAB) bypass_wayland = TRUE; /* If a Wayland client has a grab, don't pass that through to Clutter */ if (display->event_route == META_EVENT_ROUTE_WAYLAND_POPUP) bypass_clutter = TRUE; #ifdef HAVE_WAYLAND if (compositor && !bypass_wayland) { if (meta_wayland_compositor_handle_event (compositor, event)) bypass_clutter = TRUE; } #endif display->current_time = CurrentTime; return bypass_clutter; }
static gboolean on_captured_event (ClutterActor *stage, ClutterEvent *event, ClutterClickAction *action) { ClutterClickActionPrivate *priv = action->priv; ClutterActor *actor; ClutterModifierType modifier_state; gboolean has_button = TRUE; actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (action)); switch (clutter_event_type (event)) { case CLUTTER_TOUCH_END: has_button = FALSE; case CLUTTER_BUTTON_RELEASE: if (!priv->is_held) return CLUTTER_EVENT_STOP; if ((has_button && clutter_event_get_button (event) != priv->press_button) || (has_button && clutter_event_get_click_count (event) != 1) || clutter_event_get_device_id (event) != priv->press_device_id || clutter_event_get_event_sequence (event) != priv->press_sequence) return CLUTTER_EVENT_PROPAGATE; click_action_set_held (action, FALSE); click_action_cancel_long_press (action); /* disconnect the capture */ if (priv->capture_id != 0) { g_signal_handler_disconnect (priv->stage, priv->capture_id); priv->capture_id = 0; } if (priv->long_press_id != 0) { g_source_remove (priv->long_press_id); priv->long_press_id = 0; } if (!clutter_actor_contains (actor, clutter_event_get_source (event))) return CLUTTER_EVENT_PROPAGATE; /* exclude any button-mask so that we can compare * the press and release states properly */ modifier_state = clutter_event_get_state (event) & ~(CLUTTER_BUTTON1_MASK | CLUTTER_BUTTON2_MASK | CLUTTER_BUTTON3_MASK | CLUTTER_BUTTON4_MASK | CLUTTER_BUTTON5_MASK); /* if press and release states don't match we * simply ignore modifier keys. i.e. modifier keys * are expected to be pressed throughout the whole * click */ if (modifier_state != priv->modifier_state) priv->modifier_state = 0; click_action_set_pressed (action, FALSE); g_signal_emit (action, click_signals[CLICKED], 0, actor); break; case CLUTTER_MOTION: case CLUTTER_TOUCH_UPDATE: { gfloat motion_x, motion_y; gfloat delta_x, delta_y; if (!priv->is_held) return CLUTTER_EVENT_PROPAGATE; clutter_event_get_coords (event, &motion_x, &motion_y); delta_x = ABS (motion_x - priv->press_x); delta_y = ABS (motion_y - priv->press_y); if (delta_x > priv->drag_threshold || delta_y > priv->drag_threshold) click_action_cancel_long_press (action); } break; default: break; } return CLUTTER_EVENT_STOP; }
static gboolean on_event (ClutterActor *actor, ClutterEvent *event, ClutterClickAction *action) { ClutterClickActionPrivate *priv = action->priv; gboolean has_button = TRUE; if (!clutter_actor_meta_get_enabled (CLUTTER_ACTOR_META (action))) return CLUTTER_EVENT_PROPAGATE; switch (clutter_event_type (event)) { case CLUTTER_TOUCH_BEGIN: has_button = FALSE; case CLUTTER_BUTTON_PRESS: if (has_button && clutter_event_get_click_count (event) != 1) return CLUTTER_EVENT_PROPAGATE; if (priv->is_held) return CLUTTER_EVENT_STOP; if (!clutter_actor_contains (actor, clutter_event_get_source (event))) return CLUTTER_EVENT_PROPAGATE; priv->press_button = has_button ? clutter_event_get_button (event) : 0; priv->press_device_id = clutter_event_get_device_id (event); priv->press_sequence = clutter_event_get_event_sequence (event); priv->modifier_state = clutter_event_get_state (event); clutter_event_get_coords (event, &priv->press_x, &priv->press_y); if (priv->long_press_threshold < 0) { ClutterSettings *settings = clutter_settings_get_default (); g_object_get (settings, "dnd-drag-threshold", &priv->drag_threshold, NULL); } else priv->drag_threshold = priv->long_press_threshold; if (priv->stage == NULL) priv->stage = clutter_actor_get_stage (actor); priv->capture_id = g_signal_connect_after (priv->stage, "captured-event", G_CALLBACK (on_captured_event), action); click_action_set_pressed (action, TRUE); click_action_set_held (action, TRUE); click_action_query_long_press (action); break; case CLUTTER_ENTER: click_action_set_pressed (action, priv->is_held); break; case CLUTTER_LEAVE: click_action_set_pressed (action, priv->is_held); click_action_cancel_long_press (action); break; default: break; } return CLUTTER_EVENT_PROPAGATE; }
/* An event was received */ static gboolean _xfdashboard_click_action_on_event(XfdashboardClickAction *self, ClutterEvent *inEvent, gpointer inUserData) { XfdashboardClickActionPrivate *priv; gboolean hasButton; ClutterActor *actor; g_return_val_if_fail(XFDASHBOARD_IS_CLICK_ACTION(self), CLUTTER_EVENT_PROPAGATE); g_return_val_if_fail(CLUTTER_IS_ACTOR(inUserData), CLUTTER_EVENT_PROPAGATE); priv=self->priv; hasButton=TRUE; actor=CLUTTER_ACTOR(inUserData); /* Check if actor is enabled to handle events */ if(!clutter_actor_meta_get_enabled(CLUTTER_ACTOR_META(self))) return(CLUTTER_EVENT_PROPAGATE); /* Handle event */ switch(clutter_event_type(inEvent)) { case CLUTTER_TOUCH_BEGIN: hasButton=FALSE; case CLUTTER_BUTTON_PRESS: /* We only handle single clicks if it is pointer device */ if(hasButton && clutter_event_get_click_count(inEvent)!=1) { return(CLUTTER_EVENT_PROPAGATE); } /* Do we already held the press? */ if(priv->isHeld) return(CLUTTER_EVENT_STOP); /* Is the source of event a child of this actor. If not do * not handle this event but any other. */ if(!clutter_actor_contains(actor, clutter_event_get_source(inEvent))) { return(CLUTTER_EVENT_PROPAGATE); } /* Remember event data */ priv->pressButton=hasButton ? clutter_event_get_button(inEvent) : 0; priv->pressDeviceID=clutter_event_get_device_id(inEvent); priv->pressSequence=clutter_event_get_event_sequence(inEvent); priv->modifierState=clutter_event_get_state(inEvent); clutter_event_get_coords(inEvent, &priv->pressX, &priv->pressY); if(priv->longPressThreshold<0) { ClutterSettings *settings=clutter_settings_get_default(); g_object_get(settings, "dnd-drag-threshold", &priv->dragThreshold, NULL); } else priv->dragThreshold=priv->longPressThreshold; if(priv->stage==NULL) priv->stage=clutter_actor_get_stage(actor); /* Connect signals */ priv->captureID=g_signal_connect_object(priv->stage, "captured-event", G_CALLBACK(_xfdashboard_click_action_on_captured_event), self, G_CONNECT_AFTER | G_CONNECT_SWAPPED); /* Set state of this action */ _xfdashboard_click_action_set_pressed(self, TRUE); _xfdashboard_click_action_set_held(self, TRUE); _xfdashboard_click_action_query_long_press(self); break; case CLUTTER_ENTER: _xfdashboard_click_action_set_pressed(self, priv->isHeld); break; case CLUTTER_LEAVE: _xfdashboard_click_action_set_pressed(self, priv->isHeld); _xfdashboard_click_action_cancel_long_press(self); break; default: break; } return(CLUTTER_EVENT_PROPAGATE); }
/* An event was captured */ static gboolean _xfdashboard_click_action_on_captured_event(XfdashboardClickAction *self, ClutterEvent *inEvent, gpointer inUserData) { XfdashboardClickActionPrivate *priv; ClutterActor *stage G_GNUC_UNUSED; ClutterActor *actor; ClutterModifierType modifierState; gboolean hasButton; g_return_val_if_fail(XFDASHBOARD_IS_CLICK_ACTION(self), CLUTTER_EVENT_PROPAGATE); g_return_val_if_fail(CLUTTER_IS_ACTOR(inUserData), CLUTTER_EVENT_PROPAGATE); priv=self->priv; stage=CLUTTER_ACTOR(inUserData); hasButton=TRUE; /* Handle captured event */ actor=clutter_actor_meta_get_actor(CLUTTER_ACTOR_META(self)); switch(clutter_event_type(inEvent)) { case CLUTTER_TOUCH_END: hasButton=FALSE; case CLUTTER_BUTTON_RELEASE: if(!priv->isHeld) return(CLUTTER_EVENT_STOP); if((hasButton && clutter_event_get_button(inEvent)!=priv->pressButton) || (hasButton && clutter_event_get_click_count(inEvent)!=1) || clutter_event_get_device_id(inEvent)!=priv->pressDeviceID || clutter_event_get_event_sequence(inEvent)!=priv->pressSequence) { return(CLUTTER_EVENT_PROPAGATE); } _xfdashboard_click_action_set_held(self, FALSE); _xfdashboard_click_action_cancel_long_press(self); /* Disconnect the capture */ if(priv->captureID!=0) { g_signal_handler_disconnect(priv->stage, priv->captureID); priv->captureID = 0; } if(priv->longPressID!=0) { g_source_remove(priv->longPressID); priv->longPressID=0; } if(!clutter_actor_contains(actor, clutter_event_get_source(inEvent))) { return(CLUTTER_EVENT_PROPAGATE); } /* Exclude any button-mask so that we can compare * the press and release states properly */ modifierState=clutter_event_get_state(inEvent) & ~(CLUTTER_BUTTON1_MASK | CLUTTER_BUTTON2_MASK | CLUTTER_BUTTON3_MASK | CLUTTER_BUTTON4_MASK | CLUTTER_BUTTON5_MASK); /* If press and release states don't match we simply ignore * modifier keys. i.e. modifier keys are expected to be pressed * throughout the whole click */ if(modifierState!=priv->modifierState) priv->modifierState=0; _xfdashboard_click_action_set_pressed(self, FALSE); g_signal_emit(self, XfdashboardClickActionSignals[SIGNAL_CLICKED], 0, actor); break; case CLUTTER_MOTION: case CLUTTER_TOUCH_UPDATE: { gfloat motionX, motionY; gfloat deltaX, deltaY; if(!priv->isHeld) return(CLUTTER_EVENT_PROPAGATE); clutter_event_get_coords (inEvent, &motionX, &motionY); deltaX=ABS(motionX-priv->pressX); deltaY=ABS(motionY-priv->pressY); if(deltaX>priv->dragThreshold || deltaY>priv->dragThreshold) { _xfdashboard_click_action_cancel_long_press(self); } } break; default: break; } /* This is line changed in returning CLUTTER_EVENT_PROPAGATE * instead of CLUTTER_EVENT_STOP */ return(CLUTTER_EVENT_PROPAGATE); }
static gboolean button_press_event_cb (ClutterActor *actor, ClutterEvent *event, ChamplainKineticScrollView *scroll) { ChamplainKineticScrollViewPrivate *priv = scroll->priv; ClutterButtonEvent *bevent = (ClutterButtonEvent *) event; ClutterActor *stage = clutter_actor_get_stage (actor); if (event->type == CLUTTER_TOUCH_BEGIN && priv->sequence) { /* On multi touch input, shy away and cancel everything */ priv->sequence = NULL; g_signal_handlers_disconnect_by_func (stage, motion_event_cb, scroll); g_signal_handlers_disconnect_by_func (stage, button_release_event_cb, scroll); champlain_kinetic_scroll_view_stop (scroll); clamp_adjustments (scroll); g_signal_emit_by_name (scroll, "panning-completed", NULL); return FALSE; } if ((((event->type == CLUTTER_BUTTON_PRESS) && (bevent->button == 1)) || (event->type == CLUTTER_TOUCH_BEGIN && !priv->sequence)) && stage) { ChamplainKineticScrollViewMotion *motion; /* Reset motion buffer */ priv->last_motion = 0; motion = &g_array_index (priv->motion_buffer, ChamplainKineticScrollViewMotion, 0); if (clutter_actor_transform_stage_point (actor, bevent->x, bevent->y, &motion->x, &motion->y)) { g_get_current_time (&motion->time); if (priv->deceleration_timeline) { clutter_timeline_stop (priv->deceleration_timeline); g_object_unref (priv->deceleration_timeline); priv->deceleration_timeline = NULL; } priv->sequence = clutter_event_get_event_sequence (event); g_signal_connect (stage, "captured-event", G_CALLBACK (motion_event_cb), scroll); g_signal_connect (stage, "captured-event", G_CALLBACK (button_release_event_cb), scroll); } } return FALSE; }
static gboolean button_release_event_cb (ClutterActor *stage, ClutterButtonEvent *event, ChamplainKineticScrollView *scroll) { ChamplainKineticScrollViewPrivate *priv = scroll->priv; ClutterActor *actor = CLUTTER_ACTOR (scroll); gboolean decelerating = FALSE; if ((event->type != CLUTTER_MOTION || event->modifier_state & CLUTTER_BUTTON1_MASK) && (event->type != CLUTTER_BUTTON_RELEASE || event->button != 1) && (event->type != CLUTTER_TOUCH_END || priv->sequence != clutter_event_get_event_sequence ((ClutterEvent *) event))) return FALSE; g_signal_handlers_disconnect_by_func (stage, motion_event_cb, scroll); g_signal_handlers_disconnect_by_func (stage, button_release_event_cb, scroll); if (priv->kinetic && priv->viewport) { gfloat x, y; if (clutter_actor_transform_stage_point (actor, event->x, event->y, &x, &y)) { double frac, x_origin, y_origin; GTimeVal release_time, motion_time; ChamplainAdjustment *hadjust, *vadjust; glong time_diff; gint i; /* Get time delta */ g_get_current_time (&release_time); /* Get average position/time of last x mouse events */ priv->last_motion++; x_origin = y_origin = 0; motion_time = (GTimeVal){ 0, 0 }; for (i = 0; i < priv->last_motion; i++) { ChamplainKineticScrollViewMotion *motion = &g_array_index (priv->motion_buffer, ChamplainKineticScrollViewMotion, i); /* FIXME: This doesn't guard against overflows - Should * either fix that, or calculate the correct maximum * value for the buffer size */ x_origin += motion->x; y_origin += motion->y; motion_time.tv_sec += motion->time.tv_sec; motion_time.tv_usec += motion->time.tv_usec; } x_origin /= priv->last_motion; y_origin /= priv->last_motion; motion_time.tv_sec /= priv->last_motion; motion_time.tv_usec /= priv->last_motion; if (motion_time.tv_sec == release_time.tv_sec) time_diff = release_time.tv_usec - motion_time.tv_usec; else time_diff = release_time.tv_usec + (G_USEC_PER_SEC - motion_time.tv_usec); /* On a macbook that's running Ubuntu 9.04 sometimes 'time_diff' is 0 and this causes a division by 0 when computing 'frac'. This check avoids this error. */ if (time_diff != 0) { /* Work out the fraction of 1/60th of a second that has elapsed */ frac = (time_diff / 1000.0) / (1000.0 / 60.0); /* See how many units to move in 1/60th of a second */ priv->dx = (x_origin - x) / frac; priv->dy = (y_origin - y) / frac; /* Get adjustments to do step-increment snapping */ champlain_viewport_get_adjustments (CHAMPLAIN_VIEWPORT (priv->viewport), &hadjust, &vadjust); if (ABS (priv->dx) > 1 || ABS (priv->dy) > 1) { gdouble value, lower, step_increment, d, a, x, y, n; /* TODO: Convert this all to fixed point? */ /* We want n, where x / y n < z, * x = Distance to move per frame * y = Deceleration rate * z = maximum distance from target * * Rearrange to n = log (x / z) / log (y) * To simplify, z = 1, so n = log (x) / log (y) * * As z = 1, this will cause stops to be slightly abrupt - * add a constant 15 frames to compensate. */ x = MAX (ABS (priv->dx), ABS (priv->dy)); y = priv->decel_rate; n = logf (x) / logf (y) + 15.0; /* Now we have n, adjust dx/dy so that we finish on a step * boundary. * * Distance moved, using the above variable names: * * d = x + x/y + x/y 2 + ... + x/y n * * Using geometric series, * * d = (1 - 1/y (n+1))/(1 - 1/y)*x * * Let a = (1 - 1/y (n+1))/(1 - 1/y), * * d = a * x * * Find d and find its nearest page boundary, then solve for x * * x = d / a */ /* Get adjustments, work out y n */ a = (1.0 - 1.0 / pow (y, n + 1)) / (1.0 - 1.0 / y); /* Solving for dx */ d = a * priv->dx; champlain_adjustment_get_values (hadjust, &value, &lower, NULL, &step_increment); d = ((rint (((value + d) - lower) / step_increment) * step_increment) + lower) - value; priv->dx = (d / a); /* Solving for dy */ d = a * (priv->dy); champlain_adjustment_get_values (vadjust, &value, &lower, NULL, &step_increment); d = ((rint (((value + d) - lower) / step_increment) * step_increment) + lower) - value; priv->dy = (d / a); priv->deceleration_timeline = clutter_timeline_new ((n / 60) * 1000.0); } else { gdouble value, lower, step_increment, d, a, y; /* Start a short effects timeline to snap to the nearest step * boundary (see equations above) */ y = priv->decel_rate; a = (1.0 - 1.0 / pow (y, 4 + 1)) / (1.0 - 1.0 / y); champlain_adjustment_get_values (hadjust, &value, &lower, NULL, &step_increment); d = ((rint ((value - lower) / step_increment) * step_increment) + lower) - value; priv->dx = (d / a); champlain_adjustment_get_values (vadjust, &value, &lower, NULL, &step_increment); d = ((rint ((value - lower) / step_increment) * step_increment) + lower) - value; priv->dy = (d / a); priv->deceleration_timeline = clutter_timeline_new (250); } g_signal_connect (priv->deceleration_timeline, "new_frame", G_CALLBACK (deceleration_new_frame_cb), scroll); g_signal_connect (priv->deceleration_timeline, "completed", G_CALLBACK (deceleration_completed_cb), scroll); clutter_timeline_start (priv->deceleration_timeline); decelerating = TRUE; } } } priv->sequence = NULL; /* Reset motion event buffer */ priv->last_motion = 0; if (!decelerating) { clamp_adjustments (scroll); g_signal_emit_by_name (scroll, "panning-completed", NULL); } /* Due to the way gestures in progress connect to the stage in order to * receive events, we must let these events go through, as they could be * essential for the management of the ClutterZoomGesture in the * ChamplainView. */ return FALSE; }
static gboolean motion_event_cb (ClutterActor *stage, ClutterMotionEvent *event, ChamplainKineticScrollView *scroll) { ChamplainKineticScrollViewPrivate *priv = scroll->priv; ClutterActor *actor = CLUTTER_ACTOR (scroll); gfloat x, y; if ((event->type != CLUTTER_MOTION || !(event->modifier_state & CLUTTER_BUTTON1_MASK)) && (event->type != CLUTTER_TOUCH_UPDATE || priv->sequence != clutter_event_get_event_sequence ((ClutterEvent *) event))) return FALSE; if (clutter_actor_transform_stage_point (actor, event->x, event->y, &x, &y)) { ChamplainKineticScrollViewMotion *motion; if (priv->viewport) { gdouble dx, dy; ChamplainAdjustment *hadjust, *vadjust; champlain_viewport_get_adjustments (CHAMPLAIN_VIEWPORT (priv->viewport), &hadjust, &vadjust); motion = &g_array_index (priv->motion_buffer, ChamplainKineticScrollViewMotion, priv->last_motion); if (hadjust) { dx = (motion->x - x) + champlain_adjustment_get_value (hadjust); champlain_adjustment_set_value (hadjust, dx); } if (vadjust) { dy = (motion->y - y) + champlain_adjustment_get_value (vadjust); champlain_adjustment_set_value (vadjust, dy); } } priv->last_motion++; if (priv->last_motion == priv->motion_buffer->len) { priv->motion_buffer = g_array_remove_index (priv->motion_buffer, 0); g_array_set_size (priv->motion_buffer, priv->last_motion); priv->last_motion--; } motion = &g_array_index (priv->motion_buffer, ChamplainKineticScrollViewMotion, priv->last_motion); motion->x = x; motion->y = y; g_get_current_time (&motion->time); } /* Due to the way gestures in progress connect to the stage in order to * receive events, we must let these events go through, as they could be * essential for the management of the ClutterZoomGesture in the * ChamplainView. */ return FALSE; }
static gboolean meta_display_handle_event (MetaDisplay *display, const ClutterEvent *event) { MetaWindow *window; gboolean bypass_clutter = FALSE; G_GNUC_UNUSED gboolean bypass_wayland = FALSE; MetaGestureTracker *tracker; ClutterEventSequence *sequence; sequence = clutter_event_get_event_sequence (event); /* Set the pointer emulating sequence on touch begin, if eligible */ if (event->type == CLUTTER_TOUCH_BEGIN && !display->pointer_emulating_sequence && sequence_is_pointer_emulated (display, event)) display->pointer_emulating_sequence = sequence; #ifdef HAVE_WAYLAND MetaWaylandCompositor *compositor = NULL; if (meta_is_wayland_compositor ()) { compositor = meta_wayland_compositor_get_default (); meta_wayland_compositor_update (compositor, event); } #endif if (meta_is_wayland_compositor () && event->type == CLUTTER_MOTION) { MetaCursorTracker *tracker = meta_cursor_tracker_get_for_screen (NULL); meta_cursor_tracker_update_position (tracker, event->motion.x, event->motion.y); } handle_idletime_for_event (event); window = get_window_for_event (display, event); display->current_time = event->any.time; if (window && !window->override_redirect && (event->type == CLUTTER_KEY_PRESS || event->type == CLUTTER_BUTTON_PRESS || event->type == CLUTTER_TOUCH_BEGIN)) { if (CurrentTime == display->current_time) { /* We can't use missing (i.e. invalid) timestamps to set user time, * nor do we want to use them to sanity check other timestamps. * See bug 313490 for more details. */ meta_warning ("Event has no timestamp! You may be using a broken " "program such as xse. Please ask the authors of that " "program to fix it.\n"); } else { meta_window_set_user_time (window, display->current_time); meta_display_sanity_check_timestamps (display, display->current_time); } } tracker = meta_display_get_gesture_tracker (display); if (meta_gesture_tracker_handle_event (tracker, event)) { bypass_wayland = bypass_clutter = TRUE; goto out; } if (display->event_route == META_EVENT_ROUTE_WINDOW_OP) { if (meta_window_handle_mouse_grab_op_event (window, event)) { bypass_clutter = TRUE; bypass_wayland = TRUE; goto out; } } /* For key events, it's important to enforce single-handling, or * we can get into a confused state. So if a keybinding is * handled (because it's one of our hot-keys, or because we are * in a keyboard-grabbed mode like moving a window, we don't * want to pass the key event to the compositor or Wayland at all. */ if (meta_keybindings_process_event (display, window, event)) { bypass_clutter = TRUE; bypass_wayland = TRUE; goto out; } if (window) { if (!clutter_event_get_event_sequence (event)) { /* Swallow all non-touch events on windows that come our way. * Touch events that reach here aren't yet in an accepted state, * so Clutter must see them to maybe trigger gestures into * recognition. */ bypass_clutter = TRUE; } meta_window_handle_ungrabbed_event (window, event); /* This might start a grab op. If it does, then filter out the * event, and if it doesn't, replay the event to release our * own sync grab. */ if (display->event_route == META_EVENT_ROUTE_WINDOW_OP) { bypass_clutter = TRUE; bypass_wayland = TRUE; } else { /* Only replay button press events, since that's where we * have the synchronous grab. */ if (event->type == CLUTTER_BUTTON_PRESS) { MetaBackend *backend = meta_get_backend (); if (META_IS_BACKEND_X11 (backend)) { Display *xdisplay = meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend)); meta_verbose ("Allowing events time %u\n", (unsigned int)event->button.time); XIAllowEvents (xdisplay, clutter_event_get_device_id (event), XIReplayDevice, event->button.time); } } } goto out; } out: /* If the compositor has a grab, don't pass that through to Wayland */ if (display->event_route == META_EVENT_ROUTE_COMPOSITOR_GRAB) bypass_wayland = TRUE; /* If a Wayland client has a grab, don't pass that through to Clutter */ if (display->event_route == META_EVENT_ROUTE_WAYLAND_POPUP) bypass_clutter = TRUE; #ifdef HAVE_WAYLAND if (compositor && !bypass_wayland) { if (meta_wayland_compositor_handle_event (compositor, event)) bypass_clutter = TRUE; } #endif /* Unset the pointer emulating sequence after its end event is processed */ if (event->type == CLUTTER_TOUCH_END && display->pointer_emulating_sequence == sequence) display->pointer_emulating_sequence = NULL; display->current_time = CurrentTime; return bypass_clutter; }