static ClutterEvent * new_absolute_motion_event (ClutterSeatEvdev *seat, ClutterInputDevice *input_device, guint64 time_us, gfloat x, gfloat y, gdouble *axes) { ClutterStage *stage = _clutter_input_device_get_stage (input_device); ClutterEvent *event; event = clutter_event_new (CLUTTER_MOTION); if (clutter_input_device_get_device_type (input_device) != CLUTTER_TABLET_DEVICE) _clutter_device_manager_evdev_constrain_pointer (seat->manager_evdev, seat->core_pointer, time_us, seat->pointer_x, seat->pointer_y, &x, &y); _clutter_evdev_event_set_time_usec (event, time_us); event->motion.time = us2ms (time_us); event->motion.stage = stage; _clutter_xkb_translate_state (event, seat->xkb, seat->button_state); event->motion.x = x; event->motion.y = y; event->motion.axes = axes; clutter_event_set_device (event, seat->core_pointer); clutter_event_set_source_device (event, input_device); if (clutter_input_device_get_device_type (input_device) == CLUTTER_TABLET_DEVICE) { ClutterInputDeviceEvdev *device_evdev = CLUTTER_INPUT_DEVICE_EVDEV (input_device); clutter_event_set_device_tool (event, device_evdev->last_tool); clutter_event_set_device (event, input_device); } else { clutter_event_set_device (event, seat->core_pointer); } _clutter_input_device_set_stage (seat->core_pointer, stage); if (clutter_input_device_get_device_type (input_device) != CLUTTER_TABLET_DEVICE) { seat->pointer_x = x; seat->pointer_y = y; } return event; }
static void notify_scroll (ClutterInputDevice *input_device, guint64 time_us, gdouble dx, gdouble dy, ClutterScrollSource scroll_source, ClutterScrollFinishFlags flags, gboolean emulated) { ClutterInputDeviceEvdev *device_evdev; ClutterSeatEvdev *seat; ClutterStage *stage; ClutterEvent *event = NULL; gdouble scroll_factor; /* We can drop the event on the floor if no stage has been * associated with the device yet. */ stage = _clutter_input_device_get_stage (input_device); if (stage == NULL) return; device_evdev = CLUTTER_INPUT_DEVICE_EVDEV (input_device); seat = _clutter_input_device_evdev_get_seat (device_evdev); event = clutter_event_new (CLUTTER_SCROLL); _clutter_evdev_event_set_time_usec (event, time_us); event->scroll.time = us2ms (time_us); event->scroll.stage = CLUTTER_STAGE (stage); _clutter_xkb_translate_state (event, seat->xkb, seat->button_state); /* libinput pointer axis events are in pointer motion coordinate space. * To convert to Xi2 discrete step coordinate space, multiply the factor * 1/10. */ event->scroll.direction = CLUTTER_SCROLL_SMOOTH; scroll_factor = 1.0 / DISCRETE_SCROLL_STEP; clutter_event_set_scroll_delta (event, scroll_factor * dx, scroll_factor * dy); event->scroll.x = seat->pointer_x; event->scroll.y = seat->pointer_y; clutter_event_set_device (event, seat->core_pointer); clutter_event_set_source_device (event, input_device); event->scroll.scroll_source = scroll_source; event->scroll.finish_flags = flags; _clutter_event_set_pointer_emulated (event, emulated); queue_event (event); }
static void notify_discrete_scroll (ClutterInputDevice *input_device, uint64_t time_us, ClutterScrollDirection direction, ClutterScrollSource scroll_source, gboolean emulated) { ClutterInputDeviceEvdev *device_evdev; ClutterSeatEvdev *seat; ClutterStage *stage; ClutterEvent *event = NULL; if (direction == CLUTTER_SCROLL_SMOOTH) return; /* We can drop the event on the floor if no stage has been * associated with the device yet. */ stage = _clutter_input_device_get_stage (input_device); if (stage == NULL) return; device_evdev = CLUTTER_INPUT_DEVICE_EVDEV (input_device); seat = _clutter_input_device_evdev_get_seat (device_evdev); event = clutter_event_new (CLUTTER_SCROLL); _clutter_evdev_event_set_time_usec (event, time_us); event->scroll.time = us2ms (time_us); event->scroll.stage = CLUTTER_STAGE (stage); _clutter_xkb_translate_state (event, seat->xkb, seat->button_state); event->scroll.direction = direction; event->scroll.x = seat->pointer_x; event->scroll.y = seat->pointer_y; clutter_event_set_device (event, seat->core_pointer); clutter_event_set_source_device (event, input_device); event->scroll.scroll_source = scroll_source; _clutter_event_set_pointer_emulated (event, emulated); queue_event (event); }
void clutter_seat_evdev_notify_touch_event (ClutterSeatEvdev *seat, ClutterInputDevice *input_device, ClutterEventType evtype, uint64_t time_us, int slot, double x, double y) { ClutterStage *stage; ClutterEvent *event = NULL; /* We can drop the event on the floor if no stage has been * associated with the device yet. */ stage = _clutter_input_device_get_stage (input_device); if (stage == NULL) return; event = clutter_event_new (evtype); _clutter_evdev_event_set_time_usec (event, time_us); event->touch.time = us2ms (time_us); event->touch.stage = CLUTTER_STAGE (stage); event->touch.x = x; event->touch.y = y; clutter_input_device_evdev_translate_coordinates (input_device, stage, &event->touch.x, &event->touch.y); /* "NULL" sequences are special cased in clutter */ event->touch.sequence = GINT_TO_POINTER (MAX (1, slot + 1)); _clutter_xkb_translate_state (event, seat->xkb, seat->button_state); if (evtype == CLUTTER_TOUCH_BEGIN || evtype == CLUTTER_TOUCH_UPDATE) event->touch.modifier_state |= CLUTTER_BUTTON1_MASK; clutter_event_set_device (event, seat->core_pointer); clutter_event_set_source_device (event, input_device); queue_event (event); }
static inline void make_button_event (const MSG *msg, ClutterStage *stage, int button, int click_count, gboolean release, ClutterInputDevice *device) { ClutterEvent *event = clutter_event_new (release ? CLUTTER_BUTTON_RELEASE : CLUTTER_BUTTON_PRESS); event->any.stage = stage; event->button.time = msg->time; event->button.x = GET_X_LPARAM (msg->lParam); event->button.y = GET_Y_LPARAM (msg->lParam); event->button.modifier_state = get_modifier_state (msg->wParam); event->button.button = button; event->button.click_count = click_count; clutter_event_set_device (event, device); take_and_queue_event (event); }
/*< private > * clutter_input_device_set_actor: * @device: a #ClutterInputDevice * @actor: a #ClutterActor * @emit_crossing: %TRUE to emit crossing events * * Sets the actor under the pointer coordinates of @device * * This function is called by _clutter_input_device_update() * and it will: * * - queue a %CLUTTER_LEAVE event on the previous pointer actor * of @device, if any * - set to %FALSE the :has-pointer property of the previous * pointer actor of @device, if any * - queue a %CLUTTER_ENTER event on the new pointer actor * - set to %TRUE the :has-pointer property of the new pointer * actor */ void _clutter_input_device_set_actor (ClutterInputDevice *device, ClutterActor *actor, gboolean emit_crossing) { ClutterActor *old_actor; if (device->cursor_actor == actor) return; old_actor = device->cursor_actor; if (old_actor != NULL) { if (emit_crossing) { ClutterEvent *event; event = clutter_event_new (CLUTTER_LEAVE); event->crossing.time = device->current_time; event->crossing.flags = 0; event->crossing.stage = device->stage; event->crossing.source = device->cursor_actor; event->crossing.x = device->current_x; event->crossing.y = device->current_y; event->crossing.related = actor; clutter_event_set_device (event, device); /* we need to make sure that this event is processed * before any other event we might have queued up until * now, so we go on, and synthesize the event emission * ourselves */ _clutter_process_event (event); clutter_event_free (event); } /* processing the event might have destroyed the actor */ if (device->cursor_actor != NULL) { _clutter_actor_set_has_pointer (device->cursor_actor, FALSE); g_object_weak_unref (G_OBJECT (device->cursor_actor), cursor_weak_unref, device); device->cursor_actor = NULL; } } if (actor != NULL) { if (emit_crossing) { ClutterEvent *event; event = clutter_event_new (CLUTTER_ENTER); event->crossing.time = device->current_time; event->crossing.flags = 0; event->crossing.stage = device->stage; event->crossing.x = device->current_x; event->crossing.y = device->current_y; event->crossing.source = actor; event->crossing.related = old_actor; clutter_event_set_device (event, device); /* see above */ _clutter_process_event (event); clutter_event_free (event); } } device->cursor_actor = actor; if (device->cursor_actor != NULL) { g_object_weak_ref (G_OBJECT (device->cursor_actor), cursor_weak_unref, device); _clutter_actor_set_has_pointer (device->cursor_actor, TRUE); } }
/** * clutter_win32_handle_event: * @msg: A pointer to a structure describing a Win32 message. * * This function processes a single Win32 message. It can be used to * hook into external windows message processing (for example, a GDK * filter function). * * If clutter_win32_disable_event_retrieval() has been called, you must * let this function process events to update Clutter's internal state. * * Return value: %TRUE if the message was handled entirely by Clutter * and no further processing (such as calling the default window * procedure) should take place. %FALSE is returned if is the message * was not handled at all or if Clutter expects processing to take * place. * * Since: 1.6 */ gboolean clutter_win32_handle_event (const MSG *msg) { ClutterBackendWin32 *backend_win32; ClutterStageWin32 *stage_win32; ClutterDeviceManager *manager; ClutterInputDevice *core_pointer, *core_keyboard; ClutterStage *stage; ClutterStageWindow *impl; gboolean return_value = FALSE; stage = clutter_win32_get_stage_from_window (msg->hwnd); /* Ignore any messages for windows which we don't have a stage for */ if (stage == NULL) return FALSE; impl = _clutter_stage_get_window (stage); stage_win32 = CLUTTER_STAGE_WIN32 (impl); backend_win32 = stage_win32->backend; manager = clutter_device_manager_get_default (); core_pointer = clutter_device_manager_get_core_device (manager, CLUTTER_POINTER_DEVICE); core_keyboard = clutter_device_manager_get_core_device (manager, CLUTTER_KEYBOARD_DEVICE); switch (msg->message) { case WM_SIZE: if (!stage_win32->is_foreign_win /* Ignore size changes resulting from the stage being minimized - otherwise the window size will be set to 0,0 */ && msg->wParam != SIZE_MINIMIZED) { WORD new_width = LOWORD (msg->lParam); WORD new_height = HIWORD (msg->lParam); gfloat old_width, old_height; clutter_actor_get_size (CLUTTER_ACTOR (stage), &old_width, &old_height); if (new_width != old_width || new_height != old_height) clutter_actor_set_size (CLUTTER_ACTOR (stage), new_width, new_height); } break; case WM_SHOWWINDOW: if (msg->wParam) clutter_stage_win32_map (stage_win32); else clutter_stage_win32_unmap (stage_win32); break; case WM_ACTIVATE: if (msg->wParam == WA_INACTIVE) { if (stage_win32->state & CLUTTER_STAGE_STATE_ACTIVATED) { ClutterEvent *event = clutter_event_new (CLUTTER_STAGE_STATE); stage_win32->state &= ~CLUTTER_STAGE_STATE_ACTIVATED; event->any.stage = stage; event->stage_state.changed_mask = CLUTTER_STAGE_STATE_ACTIVATED; event->stage_state.new_state = stage_win32->state; take_and_queue_event (event); } } else if (!(stage_win32->state & CLUTTER_STAGE_STATE_ACTIVATED)) { ClutterEvent *event = clutter_event_new (CLUTTER_STAGE_STATE); stage_win32->state |= CLUTTER_STAGE_STATE_ACTIVATED; event->any.stage = stage; event->stage_state.changed_mask = CLUTTER_STAGE_STATE_ACTIVATED; event->stage_state.new_state = stage_win32->state; take_and_queue_event (event); } break; case WM_PAINT: CLUTTER_NOTE (MULTISTAGE, "expose for stage:%p, redrawing", stage); clutter_redraw (stage); break; case WM_DESTROY: { ClutterEvent *event = clutter_event_new (CLUTTER_DESTROY_NOTIFY); CLUTTER_NOTE (EVENT, "WM_DESTROY"); event->any.stage = stage; take_and_queue_event (event); } break; case WM_CLOSE: { ClutterEvent *event = clutter_event_new (CLUTTER_DELETE); CLUTTER_NOTE (EVENT, "WM_CLOSE"); event->any.stage = stage; take_and_queue_event (event); /* The default window proc will destroy the window so we want to prevent this to allow applications to optionally destroy the window themselves */ return_value = TRUE; } break; case WM_LBUTTONDOWN: make_button_event (msg, stage, 1, 1, FALSE, core_pointer); break; case WM_MBUTTONDOWN: make_button_event (msg, stage, 2, 1, FALSE, core_pointer); break; case WM_RBUTTONDOWN: make_button_event (msg, stage, 3, 1, FALSE, core_pointer); break; case WM_LBUTTONUP: make_button_event (msg, stage, 1, 1, TRUE, core_pointer); break; case WM_MBUTTONUP: make_button_event (msg, stage, 2, 1, TRUE, core_pointer); break; case WM_RBUTTONUP: make_button_event (msg, stage, 3, 1, TRUE, core_pointer); break; case WM_LBUTTONDBLCLK: make_button_event (msg, stage, 1, 2, FALSE, core_pointer); break; case WM_MBUTTONDBLCLK: make_button_event (msg, stage, 2, 2, FALSE, core_pointer); break; case WM_RBUTTONDBLCLK: make_button_event (msg, stage, 3, 2, FALSE, core_pointer); break; case WM_MOUSEWHEEL: stage_win32->scroll_pos += (SHORT) HIWORD (msg->wParam); while (abs (stage_win32->scroll_pos) >= WHEEL_DELTA) { ClutterEvent *event = clutter_event_new (CLUTTER_SCROLL); POINT pt; event->scroll.time = msg->time; event->scroll.modifier_state = get_modifier_state (LOWORD (msg->wParam)); event->any.stage = stage; clutter_event_set_device (event, core_pointer); /* conversion to window coordinates is required */ pt.x = GET_X_LPARAM (msg->lParam); pt.y = GET_Y_LPARAM (msg->lParam); ScreenToClient (msg->hwnd, &pt); event->scroll.x = pt.x; event->scroll.y = pt.y; if (stage_win32->scroll_pos > 0) { event->scroll.direction = CLUTTER_SCROLL_UP; stage_win32->scroll_pos -= WHEEL_DELTA; } else { event->scroll.direction = CLUTTER_SCROLL_DOWN; stage_win32->scroll_pos += WHEEL_DELTA; } take_and_queue_event (event); } break; case WM_MOUSEMOVE: { ClutterEvent *event = clutter_event_new (CLUTTER_MOTION); event->motion.time = msg->time; event->motion.x = GET_X_LPARAM (msg->lParam); event->motion.y = GET_Y_LPARAM (msg->lParam); event->motion.modifier_state = get_modifier_state (msg->wParam); event->any.stage = stage; clutter_event_set_device (event, core_pointer); /* We need to start tracking when the mouse enters the stage if we're not already */ if (!stage_win32->tracking_mouse) { ClutterEvent *crossing = clutter_event_new (CLUTTER_ENTER); TRACKMOUSEEVENT tmevent; tmevent.cbSize = sizeof (tmevent); tmevent.dwFlags = TME_LEAVE; tmevent.hwndTrack = stage_win32->hwnd; TrackMouseEvent (&tmevent); event->crossing.time = msg->time; event->crossing.x = event->motion.x; event->crossing.y = event->motion.y; event->crossing.stage = stage; event->crossing.source = CLUTTER_ACTOR (stage); event->crossing.related = NULL; clutter_event_set_device (event, core_pointer); /* we entered the stage */ _clutter_stage_add_device (stage, event->crossing.device); take_and_queue_event (crossing); stage_win32->tracking_mouse = TRUE; } take_and_queue_event (event); } break; case WM_MOUSELEAVE: { ClutterEvent *event = clutter_event_new (CLUTTER_LEAVE); event->crossing.time = msg->time; event->crossing.x = msg->pt.x; event->crossing.y = msg->pt.y; event->crossing.stage = stage; event->crossing.source = CLUTTER_ACTOR (stage); event->crossing.related = NULL; clutter_event_set_device (event, core_pointer); /* we left the stage */ _clutter_stage_remove_device (stage, core_pointer); /* When we get a leave message the mouse tracking is automatically cancelled so we'll need to start it again when the mouse next enters the window */ stage_win32->tracking_mouse = FALSE; take_and_queue_event (event); } break; case WM_KEYDOWN: case WM_KEYUP: case WM_SYSKEYDOWN: case WM_SYSKEYUP: { ClutterEvent *event = clutter_event_new (CLUTTER_EVENT_NONE); int scan_code = (msg->lParam >> 16) & 0xff; int min = 0, max = CLUTTER_WIN32_KEY_MAP_SIZE, mid; BYTE key_states[256]; /* Get the keyboard modifier states. GetKeyboardState conveniently gets the key state that was current when the last keyboard message read was generated */ GetKeyboardState(key_states); /* Binary chop to check if we have a direct mapping for this key code */ while (min < max) { mid = (min + max) / 2; if (clutter_win32_key_map[mid].win_sym == msg->wParam) { event->key.keyval = clutter_win32_key_map[mid].clutter_sym; event->key.unicode_value = 0; break; } else if (clutter_win32_key_map[mid].win_sym < msg->wParam) min = mid + 1; else max = mid; } /* If we don't have a direct mapping then try getting the unicode value of the key sym */ if (min >= max) { WCHAR ch; BYTE shift_state[256]; /* Translate to a Unicode value, but only take into account the shift key. That way Ctrl+Shift+C will generate a capital C virtual key code with a zero unicode value for example */ memset (shift_state, 0, 256); shift_state[VK_SHIFT] = key_states[VK_SHIFT]; shift_state[VK_LSHIFT] = key_states[VK_LSHIFT]; shift_state[VK_RSHIFT] = key_states[VK_RSHIFT]; shift_state[VK_CAPITAL] = key_states[VK_CAPITAL]; if (ToUnicode (msg->wParam, scan_code, shift_state, &ch, 1, 0) == 1 /* The codes in this range directly match the Latin 1 codes so we can just use the Unicode value as the key sym */ && ch >= 0x20 && ch <= 0xff) event->key.keyval = ch; else /* Otherwise we don't know what the key means but the application might be able to do something with the scan code so we might as well still generate the event */ event->key.keyval = CLUTTER_KEY_VoidSymbol; /* Get the unicode value of the keypress again using the full modifier state */ if (ToUnicode (msg->wParam, scan_code, key_states, &ch, 1, 0) == 1) event->key.unicode_value = ch; else event->key.unicode_value = 0; } event->key.type = msg->message == WM_KEYDOWN || msg->message == WM_SYSKEYDOWN ? CLUTTER_KEY_PRESS : CLUTTER_KEY_RELEASE; event->key.time = msg->time; event->key.modifier_state = get_key_modifier_state (key_states); event->key.hardware_keycode = scan_code; event->any.stage = stage; clutter_event_set_device (event, core_keyboard); take_and_queue_event (event); } break; case WM_GETMINMAXINFO: { MINMAXINFO *min_max_info = (MINMAXINFO *) msg->lParam; _clutter_stage_win32_get_min_max_info (stage_win32, min_max_info); return_value = TRUE; } break; case WM_SETCURSOR: /* If the cursor is in the window's client area and the stage's cursor should be invisible then we'll set a blank cursor instead */ if (LOWORD (msg->lParam) == HTCLIENT && !stage_win32->is_cursor_visible) { return_value = TRUE; _clutter_stage_win32_update_cursor (stage_win32); } break; } return return_value; }
/** * clutter_gdk_handle_event: * @event: a #GdkEvent * * This function processes a single GDK event; it can be used to hook * into external event processing * * Return value: #GdkFilterReturn. %GDK_FILTER_REMOVE indicates that * Clutter has internally handled the event and the caller should do * no further processing. %GDK_FILTER_CONTINUE indicates that Clutter * is either not interested in the event, or has used the event to * update internal state without taking any exclusive action. * %GDK_FILTER_TRANSLATE will not occur. * */ GdkFilterReturn clutter_gdk_handle_event (GdkEvent *gdk_event) { ClutterDeviceManager *device_manager; ClutterBackendGdk *backend_gdk; ClutterBackend *backend; ClutterStage *stage = NULL; ClutterEvent *event = NULL; gint spin = 0; GdkFilterReturn result = GDK_FILTER_CONTINUE; ClutterInputDevice *device, *source_device; GdkDevice *gdk_device; backend = clutter_get_default_backend (); if (!CLUTTER_IS_BACKEND_GDK (backend)) return GDK_FILTER_CONTINUE; if (gdk_event->any.window == NULL) return GDK_FILTER_CONTINUE; device_manager = clutter_device_manager_get_default (); if (G_UNLIKELY (device_manager == NULL)) return GDK_FILTER_CONTINUE; backend_gdk = CLUTTER_BACKEND_GDK (backend); stage = clutter_gdk_get_stage_from_window (gdk_event->any.window); gdk_device = gdk_event_get_device (gdk_event); if (gdk_device != NULL) device = _clutter_device_manager_gdk_lookup_device (device_manager, gdk_device); else device = NULL; gdk_device = gdk_event_get_source_device (gdk_event); if (gdk_device != NULL) source_device = _clutter_device_manager_gdk_lookup_device (device_manager, gdk_device); else source_device = NULL; if (stage == NULL) return GDK_FILTER_CONTINUE; _clutter_threads_acquire_lock (); switch (gdk_event->type) { case GDK_DELETE: event = clutter_event_new (CLUTTER_DELETE); break; case GDK_DESTROY: event = clutter_event_new (CLUTTER_DESTROY_NOTIFY); break; case GDK_EXPOSE: { ClutterPaintVolume clip; ClutterVertex origin; CLUTTER_NOTE (EVENT, "Expose for stage '%s' [%p] { %d, %d - %d x %d }", _clutter_actor_get_debug_name (CLUTTER_ACTOR (stage)), stage, gdk_event->expose.area.x, gdk_event->expose.area.y, gdk_event->expose.area.width, gdk_event->expose.area.height); origin.x = gdk_event->expose.area.x; origin.y = gdk_event->expose.area.y; origin.z = 0; _clutter_paint_volume_init_static (&clip, CLUTTER_ACTOR (stage)); clutter_paint_volume_set_origin (&clip, &origin); clutter_paint_volume_set_width (&clip, gdk_event->expose.area.width); clutter_paint_volume_set_height (&clip, gdk_event->expose.area.height); _clutter_actor_queue_redraw_with_clip (CLUTTER_ACTOR (stage), 0, &clip); clutter_paint_volume_free (&clip); } break; case GDK_DAMAGE: /* This is handled by cogl */ break; case GDK_MOTION_NOTIFY: event = clutter_event_new (CLUTTER_MOTION); event->motion.time = gdk_event->motion.time; event->motion.x = gdk_event->motion.x; event->motion.y = gdk_event->motion.y; event->motion.axes = NULL; /* It's all X in the end, right? */ event->motion.modifier_state = gdk_event->motion.state; clutter_event_set_device (event, device); clutter_event_set_source_device (event, source_device); CLUTTER_NOTE (EVENT, "Motion notifiy [%.2f, %.2f]", event->motion.x, event->motion.y); break; case GDK_BUTTON_PRESS: case GDK_BUTTON_RELEASE: event = clutter_event_new (gdk_event->type == GDK_BUTTON_PRESS ? CLUTTER_BUTTON_PRESS : CLUTTER_BUTTON_RELEASE); event->button.time = gdk_event->button.time; event->button.x = gdk_event->button.x; event->button.y = gdk_event->button.y; event->button.axes = NULL; event->button.modifier_state = gdk_event->button.state; event->button.button = gdk_event->button.button; event->button.click_count = 1; clutter_event_set_device (event, device); clutter_event_set_source_device (event, source_device); CLUTTER_NOTE (EVENT, "Button %d %s [%.2f, %.2f]", event->button.button, event->type == CLUTTER_BUTTON_PRESS ? "press" : "release", event->button.x, event->button.y); break; case GDK_2BUTTON_PRESS: case GDK_3BUTTON_PRESS: /* these are handled by clutter-main.c updating click_count */ break; case GDK_KEY_PRESS: case GDK_KEY_RELEASE: event = clutter_event_new (gdk_event->type == GDK_KEY_PRESS ? CLUTTER_KEY_PRESS : CLUTTER_KEY_RELEASE); event->key.time = gdk_event->key.time; event->key.modifier_state = gdk_event->key.state; event->key.keyval = gdk_event->key.keyval; event->key.hardware_keycode = gdk_event->key.hardware_keycode; event->key.unicode_value = g_utf8_get_char (gdk_event->key.string); clutter_event_set_device (event, device); clutter_event_set_source_device (event, source_device); CLUTTER_NOTE (EVENT, "Key %d %s", event->key.keyval, event->type == CLUTTER_KEY_PRESS ? "press" : "release"); break; case GDK_ENTER_NOTIFY: case GDK_LEAVE_NOTIFY: event = clutter_event_new (gdk_event->type == GDK_ENTER_NOTIFY ? CLUTTER_ENTER : CLUTTER_LEAVE); event->crossing.source = CLUTTER_ACTOR (stage); event->crossing.time = gdk_event_get_time (gdk_event); event->crossing.x = gdk_event->crossing.x; event->crossing.y = gdk_event->crossing.y; /* XXX: no better fallback here? */ clutter_event_set_device (event, device); clutter_event_set_source_device (event, source_device); if (gdk_event->type == GDK_ENTER_NOTIFY) _clutter_input_device_set_stage (clutter_event_get_device (event), stage); else _clutter_input_device_set_stage (clutter_event_get_device (event), NULL); CLUTTER_NOTE (EVENT, "Crossing %s [%.2f, %.2f]", event->type == CLUTTER_ENTER ? "enter" : "leave", event->crossing.x, event->crossing.y); break; case GDK_FOCUS_CHANGE: if (gdk_event->focus_change.in) _clutter_stage_update_state (stage, 0, CLUTTER_STAGE_STATE_ACTIVATED); else _clutter_stage_update_state (stage, CLUTTER_STAGE_STATE_ACTIVATED, 0); break; case GDK_CONFIGURE: { gfloat w, h; clutter_actor_get_size (CLUTTER_ACTOR (stage), &w, &h); if (w != gdk_event->configure.width || h != gdk_event->configure.height) { clutter_actor_set_size (CLUTTER_ACTOR (stage), gdk_event->configure.width, gdk_event->configure.height); } } break; case GDK_SCROLL: event = clutter_event_new (CLUTTER_SCROLL); event->scroll.time = gdk_event->scroll.time; event->scroll.x = gdk_event->scroll.x; event->scroll.y = gdk_event->scroll.y; event->scroll.modifier_state = gdk_event->scroll.state; event->scroll.axes = NULL; /* XXX: must keep ClutterScrollDirection compatible with GdkScrollDirection */ event->scroll.direction = (ClutterScrollDirection) gdk_event->scroll.direction; clutter_event_set_device (event, device); clutter_event_set_source_device (event, source_device); clutter_event_set_scroll_delta (event, gdk_event->scroll.delta_x, gdk_event->scroll.delta_y); break; case GDK_WINDOW_STATE: if (gdk_event->window_state.changed_mask & GDK_WINDOW_STATE_FULLSCREEN) { gboolean is_fullscreen; is_fullscreen = (gdk_event->window_state.new_window_state & GDK_WINDOW_STATE_FULLSCREEN) != 0; if (is_fullscreen) _clutter_stage_update_state (stage, 0, CLUTTER_STAGE_STATE_FULLSCREEN); else _clutter_stage_update_state (stage, CLUTTER_STAGE_STATE_FULLSCREEN, 0); } break; case GDK_SETTING: _clutter_backend_gdk_update_setting (backend_gdk, gdk_event->setting.name); break; default: break; } if (event != NULL) { event->any.stage = stage; if (gdk_event->any.send_event) event->any.flags = CLUTTER_EVENT_FLAG_SYNTHETIC; _clutter_event_push (event, FALSE); spin = 1; CLUTTER_NOTE (EVENT, "Translated one event from Gdk"); /* handle also synthetic enter/leave events */ if (event->type == CLUTTER_MOTION) spin += 2; while (spin > 0 && (event = clutter_event_get ())) { /* forward the event into clutter for emission etc. */ clutter_do_event (event); clutter_event_free (event); --spin; } result = GDK_FILTER_REMOVE; } _clutter_threads_release_lock (); return result; }
void clutter_seat_evdev_notify_button (ClutterSeatEvdev *seat, ClutterInputDevice *input_device, uint64_t time_us, uint32_t button, uint32_t state) { ClutterInputDeviceEvdev *device_evdev = (ClutterInputDeviceEvdev *) input_device; ClutterStage *stage; ClutterEvent *event = NULL; gint button_nr; static gint maskmap[8] = { CLUTTER_BUTTON1_MASK, CLUTTER_BUTTON3_MASK, CLUTTER_BUTTON2_MASK, CLUTTER_BUTTON4_MASK, CLUTTER_BUTTON5_MASK, 0, 0, 0 }; int button_count; /* Drop any repeated button press (for example from virtual devices. */ button_count = update_button_count (seat, button, state); if (state && button_count > 1) return; if (!state && button_count != 0) return; /* We can drop the event on the floor if no stage has been * associated with the device yet. */ stage = _clutter_input_device_get_stage (input_device); if (stage == NULL) return; /* The evdev button numbers don't map sequentially to clutter button * numbers (the right and middle mouse buttons are in the opposite * order) so we'll map them directly with a switch statement */ switch (button) { case BTN_LEFT: case BTN_TOUCH: button_nr = CLUTTER_BUTTON_PRIMARY; break; case BTN_RIGHT: case BTN_STYLUS: button_nr = CLUTTER_BUTTON_SECONDARY; break; case BTN_MIDDLE: case BTN_STYLUS2: button_nr = CLUTTER_BUTTON_MIDDLE; break; case 0x149: /* BTN_STYLUS3 */ button_nr = 8; break; default: /* For compatibility reasons, all additional buttons go after the old 4-7 scroll ones */ if (clutter_input_device_get_device_type (input_device) == CLUTTER_TABLET_DEVICE) button_nr = button - BTN_TOOL_PEN + 4; else button_nr = button - (BTN_LEFT - 1) + 4; break; } if (button_nr < 1 || button_nr > 12) { g_warning ("Unhandled button event 0x%x", button); return; } if (state) event = clutter_event_new (CLUTTER_BUTTON_PRESS); else event = clutter_event_new (CLUTTER_BUTTON_RELEASE); if (button_nr < G_N_ELEMENTS (maskmap)) { /* Update the modifiers */ if (state) seat->button_state |= maskmap[button_nr - 1]; else seat->button_state &= ~maskmap[button_nr - 1]; } _clutter_evdev_event_set_time_usec (event, time_us); event->button.time = us2ms (time_us); event->button.stage = CLUTTER_STAGE (stage); _clutter_xkb_translate_state (event, seat->xkb, seat->button_state); event->button.button = button_nr; if (clutter_input_device_get_device_type (input_device) == CLUTTER_TABLET_DEVICE) { ClutterPoint point; clutter_input_device_get_coords (input_device, NULL, &point); event->button.x = point.x; event->button.y = point.y; } else { event->button.x = seat->pointer_x; event->button.y = seat->pointer_y; } clutter_event_set_device (event, seat->core_pointer); clutter_event_set_source_device (event, input_device); if (device_evdev->last_tool) { /* Apply the button event code as per the tool mapping */ guint mapped_button; mapped_button = clutter_input_device_tool_evdev_get_button_code (device_evdev->last_tool, button_nr); if (mapped_button != 0) button = mapped_button; } _clutter_evdev_event_set_event_code (event, button); if (clutter_input_device_get_device_type (input_device) == CLUTTER_TABLET_DEVICE) { clutter_event_set_device_tool (event, device_evdev->last_tool); clutter_event_set_device (event, input_device); } else { clutter_event_set_device (event, seat->core_pointer); } _clutter_input_device_set_stage (seat->core_pointer, stage); queue_event (event); }