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 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; }