| // Copyright 2013 The Flutter Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "flutter/shell/platform/linux/fl_pointer_manager.h" | |
| #include "flutter/shell/platform/linux/fl_engine_private.h" | |
| static constexpr int kMicrosecondsPerMillisecond = 1000; | |
| struct _FlPointerManager { | |
| GObject parent_instance; | |
| // Engine to send pointer events to. | |
| GWeakRef engine; | |
| // ID to mark events with. | |
| FlutterViewId view_id; | |
| // TRUE if the mouse pointer is inside the view, used for generating missing | |
| // add events. | |
| gboolean pointer_inside; | |
| // Pointer button state recorded for sending status updates. | |
| int64_t button_state; | |
| }; | |
| G_DEFINE_TYPE(FlPointerManager, fl_pointer_manager, G_TYPE_OBJECT); | |
| // Generates a mouse pointer event if the pointer appears inside the window. | |
| static void ensure_pointer_added(FlPointerManager* self, | |
| guint event_time, | |
| FlutterPointerDeviceKind device_kind, | |
| gdouble x, | |
| gdouble y) { | |
| if (self->pointer_inside) { | |
| return; | |
| } | |
| self->pointer_inside = TRUE; | |
| g_autoptr(FlEngine) engine = FL_ENGINE(g_weak_ref_get(&self->engine)); | |
| if (engine == nullptr) { | |
| return; | |
| } | |
| fl_engine_send_mouse_pointer_event( | |
| engine, self->view_id, kAdd, event_time * kMicrosecondsPerMillisecond, x, | |
| y, device_kind, 0, 0, self->button_state); | |
| } | |
| static void fl_pointer_manager_dispose(GObject* object) { | |
| FlPointerManager* self = FL_POINTER_MANAGER(object); | |
| g_weak_ref_clear(&self->engine); | |
| G_OBJECT_CLASS(fl_pointer_manager_parent_class)->dispose(object); | |
| } | |
| static void fl_pointer_manager_class_init(FlPointerManagerClass* klass) { | |
| G_OBJECT_CLASS(klass)->dispose = fl_pointer_manager_dispose; | |
| } | |
| static void fl_pointer_manager_init(FlPointerManager* self) {} | |
| FlPointerManager* fl_pointer_manager_new(FlutterViewId view_id, | |
| FlEngine* engine) { | |
| FlPointerManager* self = | |
| FL_POINTER_MANAGER(g_object_new(fl_pointer_manager_get_type(), nullptr)); | |
| self->view_id = view_id; | |
| g_weak_ref_init(&self->engine, engine); | |
| return self; | |
| } | |
| gboolean fl_pointer_manager_handle_button_press( | |
| FlPointerManager* self, | |
| guint event_time, | |
| FlutterPointerDeviceKind device_kind, | |
| gdouble x, | |
| gdouble y, | |
| int64_t button) { | |
| g_return_val_if_fail(FL_IS_POINTER_MANAGER(self), FALSE); | |
| ensure_pointer_added(self, event_time, device_kind, x, y); | |
| // Drop the event if Flutter already thinks the button is down. | |
| if ((self->button_state & button) != 0) { | |
| return FALSE; | |
| } | |
| int old_button_state = self->button_state; | |
| FlutterPointerPhase phase = kMove; | |
| self->button_state ^= button; | |
| phase = old_button_state == 0 ? kDown : kMove; | |
| g_autoptr(FlEngine) engine = FL_ENGINE(g_weak_ref_get(&self->engine)); | |
| if (engine == nullptr) { | |
| return FALSE; | |
| } | |
| fl_engine_send_mouse_pointer_event( | |
| engine, self->view_id, phase, event_time * kMicrosecondsPerMillisecond, x, | |
| y, device_kind, 0, 0, self->button_state); | |
| return TRUE; | |
| } | |
| gboolean fl_pointer_manager_handle_button_release( | |
| FlPointerManager* self, | |
| guint event_time, | |
| FlutterPointerDeviceKind device_kind, | |
| gdouble x, | |
| gdouble y, | |
| int64_t button) { | |
| g_return_val_if_fail(FL_IS_POINTER_MANAGER(self), FALSE); | |
| // Drop the event if Flutter already thinks the button is up. | |
| if ((self->button_state & button) == 0) { | |
| return FALSE; | |
| } | |
| FlutterPointerPhase phase = kMove; | |
| self->button_state ^= button; | |
| phase = self->button_state == 0 ? kUp : kMove; | |
| g_autoptr(FlEngine) engine = FL_ENGINE(g_weak_ref_get(&self->engine)); | |
| if (engine == nullptr) { | |
| return FALSE; | |
| } | |
| fl_engine_send_mouse_pointer_event( | |
| engine, self->view_id, phase, event_time * kMicrosecondsPerMillisecond, x, | |
| y, device_kind, 0, 0, self->button_state); | |
| return TRUE; | |
| } | |
| gboolean fl_pointer_manager_handle_motion(FlPointerManager* self, | |
| guint event_time, | |
| FlutterPointerDeviceKind device_kind, | |
| gdouble x, | |
| gdouble y) { | |
| g_return_val_if_fail(FL_IS_POINTER_MANAGER(self), FALSE); | |
| g_autoptr(FlEngine) engine = FL_ENGINE(g_weak_ref_get(&self->engine)); | |
| if (engine == nullptr) { | |
| return FALSE; | |
| } | |
| ensure_pointer_added(self, event_time, device_kind, x, y); | |
| fl_engine_send_mouse_pointer_event( | |
| engine, self->view_id, self->button_state != 0 ? kMove : kHover, | |
| event_time * kMicrosecondsPerMillisecond, x, y, device_kind, 0, 0, | |
| self->button_state); | |
| return TRUE; | |
| } | |
| gboolean fl_pointer_manager_handle_enter(FlPointerManager* self, | |
| guint event_time, | |
| FlutterPointerDeviceKind device_kind, | |
| gdouble x, | |
| gdouble y) { | |
| g_return_val_if_fail(FL_IS_POINTER_MANAGER(self), FALSE); | |
| g_autoptr(FlEngine) engine = FL_ENGINE(g_weak_ref_get(&self->engine)); | |
| if (engine == nullptr) { | |
| return FALSE; | |
| } | |
| ensure_pointer_added(self, event_time, device_kind, x, y); | |
| return TRUE; | |
| } | |
| gboolean fl_pointer_manager_handle_leave(FlPointerManager* self, | |
| guint event_time, | |
| FlutterPointerDeviceKind device_kind, | |
| gdouble x, | |
| gdouble y) { | |
| g_return_val_if_fail(FL_IS_POINTER_MANAGER(self), FALSE); | |
| g_autoptr(FlEngine) engine = FL_ENGINE(g_weak_ref_get(&self->engine)); | |
| if (engine == nullptr) { | |
| return FALSE; | |
| } | |
| // Don't remove pointer while button is down; In case of dragging outside of | |
| // window with mouse grab active Gtk will send another leave notify on | |
| // release. | |
| if (self->pointer_inside && self->button_state == 0) { | |
| fl_engine_send_mouse_pointer_event(engine, self->view_id, kRemove, | |
| event_time * kMicrosecondsPerMillisecond, | |
| x, y, device_kind, 0, 0, | |
| self->button_state); | |
| self->pointer_inside = FALSE; | |
| } | |
| return TRUE; | |
| } |