Exemplo n.º 1
0
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;
}
Exemplo n.º 2
0
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;
}
Exemplo n.º 3
0
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;
}
Exemplo n.º 4
0
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;
}
Exemplo n.º 5
0
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;
}
Exemplo n.º 6
0
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;
}
Exemplo n.º 7
0
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;
}
Exemplo n.º 8
0
/* 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);
}
Exemplo n.º 9
0
/* 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;
}
Exemplo n.º 13
0
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;
}