Add FlPointerManager to process pointer events from GTK in a form suitable for Flutter. (#56443)
This matches FlScrollingManager and FlKeyboardManager.
Add tests for this behaviour that was previously missing.
diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter
index d6da05f..4ee2e7f 100644
--- a/ci/licenses_golden/licenses_flutter
+++ b/ci/licenses_golden/licenses_flutter
@@ -45022,6 +45022,9 @@
ORIGIN: ../../../flutter/shell/platform/linux/fl_plugin_registrar_private.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/linux/fl_plugin_registrar_test.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/linux/fl_plugin_registry.cc + ../../../flutter/LICENSE
+ORIGIN: ../../../flutter/shell/platform/linux/fl_pointer_manager.cc + ../../../flutter/LICENSE
+ORIGIN: ../../../flutter/shell/platform/linux/fl_pointer_manager.h + ../../../flutter/LICENSE
+ORIGIN: ../../../flutter/shell/platform/linux/fl_pointer_manager_test.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/linux/fl_renderable.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/linux/fl_renderable.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/linux/fl_renderer.cc + ../../../flutter/LICENSE
@@ -47918,6 +47921,9 @@
FILE: ../../../flutter/shell/platform/linux/fl_plugin_registrar_private.h
FILE: ../../../flutter/shell/platform/linux/fl_plugin_registrar_test.cc
FILE: ../../../flutter/shell/platform/linux/fl_plugin_registry.cc
+FILE: ../../../flutter/shell/platform/linux/fl_pointer_manager.cc
+FILE: ../../../flutter/shell/platform/linux/fl_pointer_manager.h
+FILE: ../../../flutter/shell/platform/linux/fl_pointer_manager_test.cc
FILE: ../../../flutter/shell/platform/linux/fl_renderable.cc
FILE: ../../../flutter/shell/platform/linux/fl_renderable.h
FILE: ../../../flutter/shell/platform/linux/fl_renderer.cc
diff --git a/shell/platform/linux/BUILD.gn b/shell/platform/linux/BUILD.gn
index d018c0c..b11f734 100644
--- a/shell/platform/linux/BUILD.gn
+++ b/shell/platform/linux/BUILD.gn
@@ -92,6 +92,7 @@
"fl_method_channel_private.h",
"fl_method_codec_private.h",
"fl_plugin_registrar_private.h",
+ "fl_pointer_manager.h",
"fl_window_state_monitor.h",
"key_mapping.h",
]
@@ -130,6 +131,7 @@
"fl_platform_handler.cc",
"fl_plugin_registrar.cc",
"fl_plugin_registry.cc",
+ "fl_pointer_manager.cc",
"fl_renderable.cc",
"fl_renderer.cc",
"fl_renderer_gdk.cc",
@@ -224,6 +226,7 @@
"fl_pixel_buffer_texture_test.cc",
"fl_platform_handler_test.cc",
"fl_plugin_registrar_test.cc",
+ "fl_pointer_manager_test.cc",
"fl_renderer_test.cc",
"fl_scrolling_manager_test.cc",
"fl_settings_handler_test.cc",
diff --git a/shell/platform/linux/fl_pointer_manager.cc b/shell/platform/linux/fl_pointer_manager.cc
new file mode 100644
index 0000000..67f360b
--- /dev/null
+++ b/shell/platform/linux/fl_pointer_manager.cc
@@ -0,0 +1,203 @@
+// 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;
+}
diff --git a/shell/platform/linux/fl_pointer_manager.h b/shell/platform/linux/fl_pointer_manager.h
new file mode 100644
index 0000000..2647512
--- /dev/null
+++ b/shell/platform/linux/fl_pointer_manager.h
@@ -0,0 +1,118 @@
+// 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.
+
+#ifndef FLUTTER_SHELL_PLATFORM_LINUX_FL_POINTER_MANAGER_H_
+#define FLUTTER_SHELL_PLATFORM_LINUX_FL_POINTER_MANAGER_H_
+
+#include "flutter/shell/platform/embedder/embedder.h"
+#include "flutter/shell/platform/linux/public/flutter_linux/fl_engine.h"
+
+G_BEGIN_DECLS
+
+G_DECLARE_FINAL_TYPE(FlPointerManager,
+ fl_pointer_manager,
+ FL,
+ POINTER_MANAGER,
+ GObject);
+
+/**
+ * fl_pointer_manager_new:
+ * @view_id: view ID to report events for.
+ * @engine: an #FlEngine.
+ *
+ * Create a new #FlPointerManager.
+ *
+ * Returns: a new #FlPointerManager.
+ */
+FlPointerManager* fl_pointer_manager_new(FlutterViewId view_id,
+ FlEngine* engine);
+
+/**
+ * fl_pointer_manager_handle_button_press:
+ * @manager: an #FlPointerManager.
+ * @event_time: event time in milliseconds.
+ * @device_kind: kind of device generating the event.
+ * @x: x co-ordinate of event.
+ * @y: y co-ordinate of event.
+ * @button: button being pressed.
+ *
+ * Returns %TRUE if this event was handled.
+ */
+gboolean fl_pointer_manager_handle_button_press(
+ FlPointerManager* manager,
+ guint event_time,
+ FlutterPointerDeviceKind device_kind,
+ gdouble x,
+ gdouble y,
+ int64_t button);
+
+/**
+ * fl_pointer_manager_handle_button_release:
+ * @manager: an #FlPointerManager.
+ * @event_time: event time in milliseconds.
+ * @device_kind: kind of device generating the event.
+ * @x: x co-ordinate of event.
+ * @y: y co-ordinate of event.
+ * @button: button being released.
+ *
+ * Returns %TRUE if this event was handled.
+ */
+gboolean fl_pointer_manager_handle_button_release(
+ FlPointerManager* manager,
+ guint event_time,
+ FlutterPointerDeviceKind device_kind,
+ gdouble x,
+ gdouble y,
+ int64_t button);
+
+/**
+ * fl_pointer_manager_handle_motion:
+ * @manager: an #FlPointerManager.
+ * @event_time: event time in milliseconds.
+ * @device_kind: kind of device generating the event.
+ * @x: x co-ordinate of event.
+ * @y: y co-ordinate of event.
+ *
+ * Returns %TRUE if this event was handled.
+ */
+gboolean fl_pointer_manager_handle_motion(FlPointerManager* manager,
+ guint event_time,
+ FlutterPointerDeviceKind device_kind,
+ gdouble x,
+ gdouble y);
+
+/**
+ * fl_pointer_manager_handle_enter:
+ * @manager: an #FlPointerManager.
+ * @event_time: event time in milliseconds.
+ * @device_kind: kind of device generating the event.
+ * @x: x co-ordinate of event.
+ * @y: y co-ordinate of event.
+ *
+ * Returns %TRUE if this event was handled.
+ */
+gboolean fl_pointer_manager_handle_enter(FlPointerManager* manager,
+ guint event_time,
+ FlutterPointerDeviceKind device_kind,
+ gdouble x,
+ gdouble y);
+
+/**
+ * fl_pointer_manager_handle_leave:
+ * @manager: an #FlPointerManager.
+ * @event_time: event time in milliseconds.
+ * @device_kind: kind of device generating the event.
+ * @x: x co-ordinate of event.
+ * @y: y co-ordinate of event.
+ *
+ * Returns %TRUE if this event was handled.
+ */
+gboolean fl_pointer_manager_handle_leave(FlPointerManager* manager,
+ guint event_time,
+ FlutterPointerDeviceKind device_kind,
+ gdouble x,
+ gdouble y);
+G_END_DECLS
+
+#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_POINTER_MANAGER_H_
diff --git a/shell/platform/linux/fl_pointer_manager_test.cc b/shell/platform/linux/fl_pointer_manager_test.cc
new file mode 100644
index 0000000..503397e
--- /dev/null
+++ b/shell/platform/linux/fl_pointer_manager_test.cc
@@ -0,0 +1,448 @@
+// 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/embedder/test_utils/proc_table_replacement.h"
+#include "flutter/shell/platform/linux/fl_engine_private.h"
+#include "flutter/shell/platform/linux/testing/fl_test.h"
+
+#include "gtest/gtest.h"
+
+static void log_pointer_events(
+ FlEngine* engine,
+ std::vector<FlutterPointerEvent>& pointer_events) {
+ FlutterEngineProcTable* embedder_api = fl_engine_get_embedder_api(engine);
+ embedder_api->SendPointerEvent = MOCK_ENGINE_PROC(
+ SendPointerEvent,
+ ([&pointer_events](auto engine, const FlutterPointerEvent* events,
+ size_t events_count) {
+ for (size_t i = 0; i < events_count; i++) {
+ pointer_events.push_back(events[i]);
+ }
+
+ return kSuccess;
+ }));
+}
+
+TEST(FlPointerManagerTest, EnterLeave) {
+ g_autoptr(FlEngine) engine = make_mock_engine();
+ std::vector<FlutterPointerEvent> pointer_events;
+ log_pointer_events(engine, pointer_events);
+
+ g_autoptr(FlPointerManager) manager = fl_pointer_manager_new(42, engine);
+ fl_pointer_manager_handle_enter(manager, 1234, kFlutterPointerDeviceKindMouse,
+ 1.0, 2.0);
+ fl_pointer_manager_handle_leave(manager, 1235, kFlutterPointerDeviceKindMouse,
+ 3.0, 4.0);
+
+ EXPECT_EQ(pointer_events.size(), 2u);
+
+ EXPECT_EQ(pointer_events[0].timestamp, 1234000u);
+ EXPECT_EQ(pointer_events[0].x, 1.0);
+ EXPECT_EQ(pointer_events[0].y, 2.0);
+ EXPECT_EQ(pointer_events[0].device_kind, kFlutterPointerDeviceKindMouse);
+ EXPECT_EQ(pointer_events[0].buttons, 0);
+ EXPECT_EQ(pointer_events[0].view_id, 42);
+
+ EXPECT_EQ(pointer_events[1].timestamp, 1235000u);
+ EXPECT_EQ(pointer_events[1].x, 3.0);
+ EXPECT_EQ(pointer_events[1].y, 4.0);
+ EXPECT_EQ(pointer_events[1].device_kind, kFlutterPointerDeviceKindMouse);
+ EXPECT_EQ(pointer_events[1].buttons, 0);
+ EXPECT_EQ(pointer_events[1].view_id, 42);
+}
+
+TEST(FlPointerManagerTest, EnterEnter) {
+ g_autoptr(FlEngine) engine = make_mock_engine();
+ std::vector<FlutterPointerEvent> pointer_events;
+ log_pointer_events(engine, pointer_events);
+
+ g_autoptr(FlPointerManager) manager = fl_pointer_manager_new(42, engine);
+ fl_pointer_manager_handle_enter(manager, 1234, kFlutterPointerDeviceKindMouse,
+ 1.0, 2.0);
+ // Duplicate enter is ignored
+ fl_pointer_manager_handle_enter(manager, 1235, kFlutterPointerDeviceKindMouse,
+ 3.0, 4.0);
+
+ EXPECT_EQ(pointer_events.size(), 1u);
+
+ EXPECT_EQ(pointer_events[0].timestamp, 1234000u);
+ EXPECT_EQ(pointer_events[0].x, 1.0);
+ EXPECT_EQ(pointer_events[0].y, 2.0);
+ EXPECT_EQ(pointer_events[0].device_kind, kFlutterPointerDeviceKindMouse);
+ EXPECT_EQ(pointer_events[0].buttons, 0);
+ EXPECT_EQ(pointer_events[0].view_id, 42);
+}
+
+TEST(FlPointerManagerTest, EnterLeaveLeave) {
+ g_autoptr(FlEngine) engine = make_mock_engine();
+ std::vector<FlutterPointerEvent> pointer_events;
+ log_pointer_events(engine, pointer_events);
+
+ g_autoptr(FlPointerManager) manager = fl_pointer_manager_new(42, engine);
+ fl_pointer_manager_handle_enter(manager, 1234, kFlutterPointerDeviceKindMouse,
+ 1.0, 2.0);
+ fl_pointer_manager_handle_leave(manager, 1235, kFlutterPointerDeviceKindMouse,
+ 3.0, 4.0);
+ // Duplicate leave is ignored
+ fl_pointer_manager_handle_leave(manager, 1235, kFlutterPointerDeviceKindMouse,
+ 5.0, 6.0);
+
+ EXPECT_EQ(pointer_events.size(), 2u);
+
+ EXPECT_EQ(pointer_events[0].timestamp, 1234000u);
+ EXPECT_EQ(pointer_events[0].x, 1.0);
+ EXPECT_EQ(pointer_events[0].y, 2.0);
+ EXPECT_EQ(pointer_events[0].device_kind, kFlutterPointerDeviceKindMouse);
+ EXPECT_EQ(pointer_events[0].buttons, 0);
+ EXPECT_EQ(pointer_events[0].view_id, 42);
+
+ EXPECT_EQ(pointer_events[1].timestamp, 1235000u);
+ EXPECT_EQ(pointer_events[1].x, 3.0);
+ EXPECT_EQ(pointer_events[1].y, 4.0);
+ EXPECT_EQ(pointer_events[1].device_kind, kFlutterPointerDeviceKindMouse);
+ EXPECT_EQ(pointer_events[1].buttons, 0);
+ EXPECT_EQ(pointer_events[1].view_id, 42);
+}
+
+TEST(FlPointerManagerTest, EnterButtonPress) {
+ g_autoptr(FlEngine) engine = make_mock_engine();
+ std::vector<FlutterPointerEvent> pointer_events;
+ log_pointer_events(engine, pointer_events);
+
+ g_autoptr(FlPointerManager) manager = fl_pointer_manager_new(42, engine);
+ fl_pointer_manager_handle_enter(manager, 1234, kFlutterPointerDeviceKindMouse,
+ 1.0, 2.0);
+ fl_pointer_manager_handle_button_press(
+ manager, 1235, kFlutterPointerDeviceKindMouse, 4.0, 8.0,
+ kFlutterPointerButtonMousePrimary);
+
+ EXPECT_EQ(pointer_events.size(), 2u);
+
+ EXPECT_EQ(pointer_events[0].timestamp, 1234000u);
+ EXPECT_EQ(pointer_events[0].x, 1.0);
+ EXPECT_EQ(pointer_events[0].y, 2.0);
+ EXPECT_EQ(pointer_events[0].device_kind, kFlutterPointerDeviceKindMouse);
+ EXPECT_EQ(pointer_events[0].buttons, 0);
+ EXPECT_EQ(pointer_events[0].view_id, 42);
+
+ EXPECT_EQ(pointer_events[1].timestamp, 1235000u);
+ EXPECT_EQ(pointer_events[1].x, 4.0);
+ EXPECT_EQ(pointer_events[1].y, 8.0);
+ EXPECT_EQ(pointer_events[1].device_kind, kFlutterPointerDeviceKindMouse);
+ EXPECT_EQ(pointer_events[1].buttons, kFlutterPointerButtonMousePrimary);
+ EXPECT_EQ(pointer_events[1].view_id, 42);
+}
+
+TEST(FlPointerManagerTest, NoEnterButtonPress) {
+ g_autoptr(FlEngine) engine = make_mock_engine();
+ std::vector<FlutterPointerEvent> pointer_events;
+ log_pointer_events(engine, pointer_events);
+
+ g_autoptr(FlPointerManager) manager = fl_pointer_manager_new(42, engine);
+ fl_pointer_manager_handle_button_press(
+ manager, 1234, kFlutterPointerDeviceKindMouse, 4.0, 8.0,
+ kFlutterPointerButtonMousePrimary);
+
+ EXPECT_EQ(pointer_events.size(), 2u);
+
+ // Synthetic enter events
+ EXPECT_EQ(pointer_events[0].timestamp, 1234000u);
+ EXPECT_EQ(pointer_events[0].x, 4.0);
+ EXPECT_EQ(pointer_events[0].y, 8.0);
+ EXPECT_EQ(pointer_events[0].device_kind, kFlutterPointerDeviceKindMouse);
+ EXPECT_EQ(pointer_events[0].buttons, 0);
+ EXPECT_EQ(pointer_events[0].view_id, 42);
+
+ EXPECT_EQ(pointer_events[1].timestamp, 1234000u);
+ EXPECT_EQ(pointer_events[1].x, 4.0);
+ EXPECT_EQ(pointer_events[1].y, 8.0);
+ EXPECT_EQ(pointer_events[1].device_kind, kFlutterPointerDeviceKindMouse);
+ EXPECT_EQ(pointer_events[1].buttons, kFlutterPointerButtonMousePrimary);
+ EXPECT_EQ(pointer_events[1].view_id, 42);
+}
+
+TEST(FlPointerManagerTest, ButtonPressButtonRelease) {
+ g_autoptr(FlEngine) engine = make_mock_engine();
+ std::vector<FlutterPointerEvent> pointer_events;
+ log_pointer_events(engine, pointer_events);
+
+ g_autoptr(FlPointerManager) manager = fl_pointer_manager_new(42, engine);
+ fl_pointer_manager_handle_button_press(
+ manager, 1234, kFlutterPointerDeviceKindMouse, 4.0, 8.0,
+ kFlutterPointerButtonMousePrimary);
+ fl_pointer_manager_handle_button_release(
+ manager, 1235, kFlutterPointerDeviceKindMouse, 5.0, 9.0,
+ kFlutterPointerButtonMousePrimary);
+
+ EXPECT_EQ(pointer_events.size(), 3u);
+
+ // Ignore first synthetic enter event
+ EXPECT_EQ(pointer_events[1].timestamp, 1234000u);
+ EXPECT_EQ(pointer_events[1].x, 4.0);
+ EXPECT_EQ(pointer_events[1].y, 8.0);
+ EXPECT_EQ(pointer_events[1].device_kind, kFlutterPointerDeviceKindMouse);
+ EXPECT_EQ(pointer_events[1].buttons, kFlutterPointerButtonMousePrimary);
+ EXPECT_EQ(pointer_events[1].view_id, 42);
+ EXPECT_EQ(pointer_events[2].timestamp, 1235000u);
+ EXPECT_EQ(pointer_events[2].x, 5.0);
+ EXPECT_EQ(pointer_events[2].y, 9.0);
+ EXPECT_EQ(pointer_events[2].device_kind, kFlutterPointerDeviceKindMouse);
+ EXPECT_EQ(pointer_events[2].buttons, 0);
+ EXPECT_EQ(pointer_events[2].view_id, 42);
+}
+
+TEST(FlPointerManagerTest, ButtonPressButtonReleaseThreeButtons) {
+ g_autoptr(FlEngine) engine = make_mock_engine();
+ std::vector<FlutterPointerEvent> pointer_events;
+ log_pointer_events(engine, pointer_events);
+
+ g_autoptr(FlPointerManager) manager = fl_pointer_manager_new(42, engine);
+ // Press buttons 1-2-3, release 3-2-1
+ fl_pointer_manager_handle_button_press(
+ manager, 1234, kFlutterPointerDeviceKindMouse, 1.0, 2.0,
+ kFlutterPointerButtonMousePrimary);
+ fl_pointer_manager_handle_button_press(
+ manager, 1235, kFlutterPointerDeviceKindMouse, 3.0, 4.0,
+ kFlutterPointerButtonMouseSecondary);
+ fl_pointer_manager_handle_button_press(manager, 1236,
+ kFlutterPointerDeviceKindMouse, 5.0,
+ 6.0, kFlutterPointerButtonMouseMiddle);
+ fl_pointer_manager_handle_button_release(
+ manager, 1237, kFlutterPointerDeviceKindMouse, 7.0, 8.0,
+ kFlutterPointerButtonMouseMiddle);
+ fl_pointer_manager_handle_button_release(
+ manager, 1238, kFlutterPointerDeviceKindMouse, 9.0, 10.0,
+ kFlutterPointerButtonMouseSecondary);
+ fl_pointer_manager_handle_button_release(
+ manager, 1239, kFlutterPointerDeviceKindMouse, 11.0, 12.0,
+ kFlutterPointerButtonMousePrimary);
+
+ EXPECT_EQ(pointer_events.size(), 7u);
+
+ // Ignore first synthetic enter event
+ EXPECT_EQ(pointer_events[1].timestamp, 1234000u);
+ EXPECT_EQ(pointer_events[1].x, 1.0);
+ EXPECT_EQ(pointer_events[1].y, 2.0);
+ EXPECT_EQ(pointer_events[1].buttons, kFlutterPointerButtonMousePrimary);
+ EXPECT_EQ(pointer_events[2].timestamp, 1235000u);
+ EXPECT_EQ(pointer_events[2].x, 3.0);
+ EXPECT_EQ(pointer_events[2].y, 4.0);
+ EXPECT_EQ(pointer_events[2].buttons, kFlutterPointerButtonMousePrimary |
+ kFlutterPointerButtonMouseSecondary);
+ EXPECT_EQ(pointer_events[3].timestamp, 1236000u);
+ EXPECT_EQ(pointer_events[3].x, 5.0);
+ EXPECT_EQ(pointer_events[3].y, 6.0);
+ EXPECT_EQ(pointer_events[3].buttons, kFlutterPointerButtonMousePrimary |
+ kFlutterPointerButtonMouseSecondary |
+ kFlutterPointerButtonMouseMiddle);
+ EXPECT_EQ(pointer_events[4].timestamp, 1237000u);
+ EXPECT_EQ(pointer_events[4].x, 7.0);
+ EXPECT_EQ(pointer_events[4].y, 8.0);
+ EXPECT_EQ(pointer_events[4].buttons, kFlutterPointerButtonMousePrimary |
+ kFlutterPointerButtonMouseSecondary);
+ EXPECT_EQ(pointer_events[5].timestamp, 1238000u);
+ EXPECT_EQ(pointer_events[5].x, 9.0);
+ EXPECT_EQ(pointer_events[5].y, 10.0);
+ EXPECT_EQ(pointer_events[5].buttons, kFlutterPointerButtonMousePrimary);
+ EXPECT_EQ(pointer_events[6].timestamp, 1239000u);
+ EXPECT_EQ(pointer_events[6].x, 11.0);
+ EXPECT_EQ(pointer_events[6].y, 12.0);
+ EXPECT_EQ(pointer_events[6].buttons, 0);
+}
+
+TEST(FlPointerManagerTest, ButtonPressButtonPressButtonRelease) {
+ g_autoptr(FlEngine) engine = make_mock_engine();
+ std::vector<FlutterPointerEvent> pointer_events;
+ log_pointer_events(engine, pointer_events);
+
+ g_autoptr(FlPointerManager) manager = fl_pointer_manager_new(42, engine);
+ fl_pointer_manager_handle_button_press(
+ manager, 1234, kFlutterPointerDeviceKindMouse, 4.0, 8.0,
+ kFlutterPointerButtonMousePrimary);
+ // Ignore duplicate press
+ fl_pointer_manager_handle_button_press(
+ manager, 1234, kFlutterPointerDeviceKindMouse, 6.0, 10.0,
+ kFlutterPointerButtonMousePrimary);
+ fl_pointer_manager_handle_button_release(
+ manager, 1235, kFlutterPointerDeviceKindMouse, 5.0, 9.0,
+ kFlutterPointerButtonMousePrimary);
+
+ EXPECT_EQ(pointer_events.size(), 3u);
+
+ // Ignore first synthetic enter event
+ EXPECT_EQ(pointer_events[1].timestamp, 1234000u);
+ EXPECT_EQ(pointer_events[1].x, 4.0);
+ EXPECT_EQ(pointer_events[1].y, 8.0);
+ EXPECT_EQ(pointer_events[1].device_kind, kFlutterPointerDeviceKindMouse);
+ EXPECT_EQ(pointer_events[1].buttons, kFlutterPointerButtonMousePrimary);
+ EXPECT_EQ(pointer_events[1].view_id, 42);
+ EXPECT_EQ(pointer_events[2].timestamp, 1235000u);
+ EXPECT_EQ(pointer_events[2].x, 5.0);
+ EXPECT_EQ(pointer_events[2].y, 9.0);
+ EXPECT_EQ(pointer_events[2].device_kind, kFlutterPointerDeviceKindMouse);
+ EXPECT_EQ(pointer_events[2].buttons, 0);
+ EXPECT_EQ(pointer_events[2].view_id, 42);
+}
+
+TEST(FlPointerManagerTest, ButtonPressButtonReleaseButtonRelease) {
+ g_autoptr(FlEngine) engine = make_mock_engine();
+ std::vector<FlutterPointerEvent> pointer_events;
+ log_pointer_events(engine, pointer_events);
+
+ g_autoptr(FlPointerManager) manager = fl_pointer_manager_new(42, engine);
+ fl_pointer_manager_handle_button_press(
+ manager, 1234, kFlutterPointerDeviceKindMouse, 4.0, 8.0,
+ kFlutterPointerButtonMousePrimary);
+ fl_pointer_manager_handle_button_release(
+ manager, 1235, kFlutterPointerDeviceKindMouse, 5.0, 9.0,
+ kFlutterPointerButtonMousePrimary);
+ // Ignore duplicate release
+ fl_pointer_manager_handle_button_release(
+ manager, 1235, kFlutterPointerDeviceKindMouse, 6.0, 10.0,
+ kFlutterPointerButtonMousePrimary);
+
+ EXPECT_EQ(pointer_events.size(), 3u);
+
+ // Ignore first synthetic enter event
+ EXPECT_EQ(pointer_events[1].timestamp, 1234000u);
+ EXPECT_EQ(pointer_events[1].x, 4.0);
+ EXPECT_EQ(pointer_events[1].y, 8.0);
+ EXPECT_EQ(pointer_events[1].device_kind, kFlutterPointerDeviceKindMouse);
+ EXPECT_EQ(pointer_events[1].buttons, kFlutterPointerButtonMousePrimary);
+ EXPECT_EQ(pointer_events[1].view_id, 42);
+ EXPECT_EQ(pointer_events[2].timestamp, 1235000u);
+ EXPECT_EQ(pointer_events[2].x, 5.0);
+ EXPECT_EQ(pointer_events[2].y, 9.0);
+ EXPECT_EQ(pointer_events[2].device_kind, kFlutterPointerDeviceKindMouse);
+ EXPECT_EQ(pointer_events[2].buttons, 0);
+ EXPECT_EQ(pointer_events[2].view_id, 42);
+}
+
+TEST(FlPointerManagerTest, NoButtonPressButtonRelease) {
+ g_autoptr(FlEngine) engine = make_mock_engine();
+ std::vector<FlutterPointerEvent> pointer_events;
+ log_pointer_events(engine, pointer_events);
+
+ g_autoptr(FlPointerManager) manager = fl_pointer_manager_new(42, engine);
+ // Release without associated press, will be ignored
+ fl_pointer_manager_handle_button_release(
+ manager, 1235, kFlutterPointerDeviceKindMouse, 5.0, 9.0,
+ kFlutterPointerButtonMousePrimary);
+
+ EXPECT_EQ(pointer_events.size(), 0u);
+}
+
+TEST(FlPointerManagerTest, Motion) {
+ g_autoptr(FlEngine) engine = make_mock_engine();
+ std::vector<FlutterPointerEvent> pointer_events;
+ log_pointer_events(engine, pointer_events);
+
+ g_autoptr(FlPointerManager) manager = fl_pointer_manager_new(42, engine);
+ fl_pointer_manager_handle_motion(manager, 1234,
+ kFlutterPointerDeviceKindMouse, 1.0, 2.0);
+ fl_pointer_manager_handle_motion(manager, 1235,
+ kFlutterPointerDeviceKindMouse, 3.0, 4.0);
+ fl_pointer_manager_handle_motion(manager, 1236,
+ kFlutterPointerDeviceKindMouse, 5.0, 6.0);
+
+ EXPECT_EQ(pointer_events.size(), 4u);
+
+ // Ignore first synthetic enter event
+ EXPECT_EQ(pointer_events[1].timestamp, 1234000u);
+ EXPECT_EQ(pointer_events[1].x, 1.0);
+ EXPECT_EQ(pointer_events[1].y, 2.0);
+ EXPECT_EQ(pointer_events[1].device_kind, kFlutterPointerDeviceKindMouse);
+ EXPECT_EQ(pointer_events[1].buttons, 0);
+ EXPECT_EQ(pointer_events[1].view_id, 42);
+ EXPECT_EQ(pointer_events[2].timestamp, 1235000u);
+ EXPECT_EQ(pointer_events[2].x, 3.0);
+ EXPECT_EQ(pointer_events[2].y, 4.0);
+ EXPECT_EQ(pointer_events[2].device_kind, kFlutterPointerDeviceKindMouse);
+ EXPECT_EQ(pointer_events[2].buttons, 0);
+ EXPECT_EQ(pointer_events[2].view_id, 42);
+ EXPECT_EQ(pointer_events[3].timestamp, 1236000u);
+ EXPECT_EQ(pointer_events[3].x, 5.0);
+ EXPECT_EQ(pointer_events[3].y, 6.0);
+ EXPECT_EQ(pointer_events[3].device_kind, kFlutterPointerDeviceKindMouse);
+ EXPECT_EQ(pointer_events[3].buttons, 0);
+ EXPECT_EQ(pointer_events[3].view_id, 42);
+}
+
+TEST(FlPointerManagerTest, Drag) {
+ g_autoptr(FlEngine) engine = make_mock_engine();
+ std::vector<FlutterPointerEvent> pointer_events;
+ log_pointer_events(engine, pointer_events);
+
+ g_autoptr(FlPointerManager) manager = fl_pointer_manager_new(42, engine);
+ fl_pointer_manager_handle_motion(manager, 1234,
+ kFlutterPointerDeviceKindMouse, 1.0, 2.0);
+ fl_pointer_manager_handle_button_press(
+ manager, 1235, kFlutterPointerDeviceKindMouse, 3.0, 4.0,
+ kFlutterPointerButtonMousePrimary);
+ fl_pointer_manager_handle_motion(manager, 1236,
+ kFlutterPointerDeviceKindMouse, 5.0, 6.0);
+ fl_pointer_manager_handle_button_release(
+ manager, 1237, kFlutterPointerDeviceKindMouse, 7.0, 8.0,
+ kFlutterPointerButtonMousePrimary);
+ fl_pointer_manager_handle_motion(manager, 1238,
+ kFlutterPointerDeviceKindMouse, 9.0, 10.0);
+
+ EXPECT_EQ(pointer_events.size(), 6u);
+
+ // Ignore first synthetic enter event
+ EXPECT_EQ(pointer_events[1].timestamp, 1234000u);
+ EXPECT_EQ(pointer_events[1].x, 1.0);
+ EXPECT_EQ(pointer_events[1].y, 2.0);
+ EXPECT_EQ(pointer_events[1].buttons, 0);
+ EXPECT_EQ(pointer_events[1].view_id, 42);
+ EXPECT_EQ(pointer_events[2].timestamp, 1235000u);
+ EXPECT_EQ(pointer_events[2].x, 3.0);
+ EXPECT_EQ(pointer_events[2].y, 4.0);
+ EXPECT_EQ(pointer_events[2].buttons, kFlutterPointerButtonMousePrimary);
+ EXPECT_EQ(pointer_events[2].view_id, 42);
+ EXPECT_EQ(pointer_events[3].timestamp, 1236000u);
+ EXPECT_EQ(pointer_events[3].x, 5.0);
+ EXPECT_EQ(pointer_events[3].y, 6.0);
+ EXPECT_EQ(pointer_events[3].buttons, kFlutterPointerButtonMousePrimary);
+ EXPECT_EQ(pointer_events[3].view_id, 42);
+ EXPECT_EQ(pointer_events[4].timestamp, 1237000u);
+ EXPECT_EQ(pointer_events[4].x, 7.0);
+ EXPECT_EQ(pointer_events[4].y, 8.0);
+ EXPECT_EQ(pointer_events[4].buttons, 0);
+ EXPECT_EQ(pointer_events[4].view_id, 42);
+ EXPECT_EQ(pointer_events[5].timestamp, 1238000u);
+ EXPECT_EQ(pointer_events[5].x, 9.0);
+ EXPECT_EQ(pointer_events[5].y, 10.0);
+ EXPECT_EQ(pointer_events[5].buttons, 0);
+ EXPECT_EQ(pointer_events[5].view_id, 42);
+}
+
+TEST(FlPointerManagerTest, DeviceKind) {
+ g_autoptr(FlEngine) engine = make_mock_engine();
+ std::vector<FlutterPointerEvent> pointer_events;
+ log_pointer_events(engine, pointer_events);
+
+ g_autoptr(FlPointerManager) manager = fl_pointer_manager_new(42, engine);
+ fl_pointer_manager_handle_enter(manager, 1234,
+ kFlutterPointerDeviceKindTrackpad, 1.0, 2.0);
+ fl_pointer_manager_handle_button_press(
+ manager, 1235, kFlutterPointerDeviceKindTrackpad, 1.0, 2.0,
+ kFlutterPointerButtonMousePrimary);
+ fl_pointer_manager_handle_motion(manager, 1238,
+ kFlutterPointerDeviceKindTrackpad, 3.0, 4.0);
+ fl_pointer_manager_handle_button_release(
+ manager, 1237, kFlutterPointerDeviceKindTrackpad, 3.0, 4.0,
+ kFlutterPointerButtonMousePrimary);
+ fl_pointer_manager_handle_leave(manager, 1235,
+ kFlutterPointerDeviceKindTrackpad, 3.0, 4.0);
+
+ EXPECT_EQ(pointer_events.size(), 5u);
+
+ EXPECT_EQ(pointer_events[0].device_kind, kFlutterPointerDeviceKindTrackpad);
+ EXPECT_EQ(pointer_events[1].device_kind, kFlutterPointerDeviceKindTrackpad);
+ EXPECT_EQ(pointer_events[2].device_kind, kFlutterPointerDeviceKindTrackpad);
+ EXPECT_EQ(pointer_events[3].device_kind, kFlutterPointerDeviceKindTrackpad);
+ EXPECT_EQ(pointer_events[4].device_kind, kFlutterPointerDeviceKindTrackpad);
+}
diff --git a/shell/platform/linux/fl_view.cc b/shell/platform/linux/fl_view.cc
index 6a07c3c..92a172b 100644
--- a/shell/platform/linux/fl_view.cc
+++ b/shell/platform/linux/fl_view.cc
@@ -18,6 +18,7 @@
#include "flutter/shell/platform/linux/fl_keyboard_view_delegate.h"
#include "flutter/shell/platform/linux/fl_mouse_cursor_handler.h"
#include "flutter/shell/platform/linux/fl_plugin_registrar_private.h"
+#include "flutter/shell/platform/linux/fl_pointer_manager.h"
#include "flutter/shell/platform/linux/fl_renderer_gdk.h"
#include "flutter/shell/platform/linux/fl_scrolling_manager.h"
#include "flutter/shell/platform/linux/fl_socket_accessible.h"
@@ -28,8 +29,6 @@
#include "flutter/shell/platform/linux/public/flutter_linux/fl_engine.h"
#include "flutter/shell/platform/linux/public/flutter_linux/fl_plugin_registry.h"
-static constexpr int kMicrosecondsPerMillisecond = 1000;
-
struct _FlView {
GtkBox parent_instance;
@@ -54,15 +53,15 @@
// TRUE if have got the first frame to render.
gboolean have_first_frame;
- // Pointer button state recorded for sending status updates.
- int64_t button_state;
-
// Monitor to track window state.
FlWindowStateMonitor* window_state_monitor;
// Manages scrolling events.
FlScrollingManager* scrolling_manager;
+ // Manages pointer events.
+ FlPointerManager* pointer_manager;
+
// Manages keyboard events.
FlKeyboardManager* keyboard_manager;
@@ -71,9 +70,6 @@
FlTextInputHandler* text_input_handler;
FlMouseCursorHandler* mouse_cursor_handler;
- // TRUE if the mouse pointer is inside the view.
- gboolean pointer_inside;
-
// Accessible tree from Flutter, exposed as an AtkPlug.
FlViewAccessible* view_accessible;
@@ -169,85 +165,23 @@
}
}
-// Converts a GDK button event into a Flutter event and sends it to the engine.
-static gboolean send_pointer_button_event(FlView* self, GdkEvent* event) {
- guint event_time = gdk_event_get_time(event);
- GdkEventType event_type = gdk_event_get_event_type(event);
- GdkModifierType event_state = static_cast<GdkModifierType>(0);
- gdk_event_get_state(event, &event_state);
+static gboolean get_mouse_button(GdkEvent* event, int64_t* button) {
guint event_button = 0;
gdk_event_get_button(event, &event_button);
- gdouble event_x = 0.0, event_y = 0.0;
- gdk_event_get_coords(event, &event_x, &event_y);
- int64_t button;
switch (event_button) {
case 1:
- button = kFlutterPointerButtonMousePrimary;
- break;
+ *button = kFlutterPointerButtonMousePrimary;
+ return TRUE;
case 2:
- button = kFlutterPointerButtonMouseMiddle;
- break;
+ *button = kFlutterPointerButtonMouseMiddle;
+ return TRUE;
case 3:
- button = kFlutterPointerButtonMouseSecondary;
- break;
+ *button = kFlutterPointerButtonMouseSecondary;
+ return TRUE;
default:
return FALSE;
}
- int old_button_state = self->button_state;
- FlutterPointerPhase phase = kMove;
- if (event_type == GDK_BUTTON_PRESS) {
- // Drop the event if Flutter already thinks the button is down.
- if ((self->button_state & button) != 0) {
- return FALSE;
- }
- self->button_state ^= button;
-
- phase = old_button_state == 0 ? kDown : kMove;
- } else if (event_type == GDK_BUTTON_RELEASE) {
- // Drop the event if Flutter already thinks the button is up.
- if ((self->button_state & button) == 0) {
- return FALSE;
- }
- self->button_state ^= button;
-
- phase = self->button_state == 0 ? kUp : kMove;
- }
-
- if (self->engine == nullptr) {
- return FALSE;
- }
-
- gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(self));
- fl_scrolling_manager_set_last_mouse_position(
- self->scrolling_manager, event_x * scale_factor, event_y * scale_factor);
- fl_keyboard_manager_sync_modifier_if_needed(self->keyboard_manager,
- event_state, event_time);
- fl_engine_send_mouse_pointer_event(
- self->engine, self->view_id, phase,
- event_time * kMicrosecondsPerMillisecond, event_x * scale_factor,
- event_y * scale_factor, get_device_kind((GdkEvent*)event), 0, 0,
- self->button_state);
-
- return TRUE;
-}
-
-// Generates a mouse pointer event if the pointer appears inside the window.
-static void check_pointer_inside(FlView* self, GdkEvent* event) {
- if (!self->pointer_inside) {
- self->pointer_inside = TRUE;
-
- gdouble x, y;
- if (gdk_event_get_coords(event, &x, &y)) {
- gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(self));
-
- fl_engine_send_mouse_pointer_event(
- self->engine, self->view_id, kAdd,
- gdk_event_get_time(event) * kMicrosecondsPerMillisecond,
- x * scale_factor, y * scale_factor, get_device_kind(event), 0, 0,
- self->button_state);
- }
- }
}
// Updates the engine with the current window metrics.
@@ -374,6 +308,20 @@
};
}
+static void sync_modifier_if_needed(FlView* self, GdkEvent* event) {
+ guint event_time = gdk_event_get_time(event);
+ GdkModifierType event_state = static_cast<GdkModifierType>(0);
+ gdk_event_get_state(event, &event_state);
+ fl_keyboard_manager_sync_modifier_if_needed(self->keyboard_manager,
+ event_state, event_time);
+}
+
+static void set_scrolling_position(FlView* self, gdouble x, gdouble y) {
+ gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(self));
+ fl_scrolling_manager_set_last_mouse_position(
+ self->scrolling_manager, x * scale_factor, y * scale_factor);
+}
+
// Signal handler for GtkWidget::button-press-event
static gboolean button_press_event_cb(FlView* self,
GdkEventButton* button_event) {
@@ -386,16 +334,43 @@
return FALSE;
}
- check_pointer_inside(self, event);
+ int64_t button;
+ if (!get_mouse_button(event, &button)) {
+ return FALSE;
+ }
- return send_pointer_button_event(self, event);
+ gdouble x = 0.0, y = 0.0;
+ gdk_event_get_coords(event, &x, &y);
+
+ set_scrolling_position(self, x, y);
+ sync_modifier_if_needed(self, event);
+
+ gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(self));
+ return fl_pointer_manager_handle_button_press(
+ self->pointer_manager, gdk_event_get_time(event), get_device_kind(event),
+ x * scale_factor, y * scale_factor, button);
}
// Signal handler for GtkWidget::button-release-event
static gboolean button_release_event_cb(FlView* self,
GdkEventButton* button_event) {
GdkEvent* event = reinterpret_cast<GdkEvent*>(button_event);
- return send_pointer_button_event(self, event);
+
+ int64_t button;
+ if (!get_mouse_button(event, &button)) {
+ return FALSE;
+ }
+
+ gdouble x = 0.0, y = 0.0;
+ gdk_event_get_coords(event, &x, &y);
+
+ set_scrolling_position(self, x, y);
+ sync_modifier_if_needed(self, event);
+
+ gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(self));
+ return fl_pointer_manager_handle_button_release(
+ self->pointer_manager, gdk_event_get_time(event), get_device_kind(event),
+ x * scale_factor, y * scale_factor, button);
}
// Signal handler for GtkWidget::scroll-event
@@ -413,77 +388,42 @@
static gboolean motion_notify_event_cb(FlView* self,
GdkEventMotion* motion_event) {
GdkEvent* event = reinterpret_cast<GdkEvent*>(motion_event);
+ sync_modifier_if_needed(self, event);
- if (self->engine == nullptr) {
- return FALSE;
- }
-
- guint event_time = gdk_event_get_time(event);
- GdkModifierType event_state = static_cast<GdkModifierType>(0);
- gdk_event_get_state(event, &event_state);
- gdouble event_x = 0.0, event_y = 0.0;
- gdk_event_get_coords(event, &event_x, &event_y);
-
- check_pointer_inside(self, event);
-
+ gdouble x = 0.0, y = 0.0;
+ gdk_event_get_coords(event, &x, &y);
gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(self));
-
- fl_keyboard_manager_sync_modifier_if_needed(self->keyboard_manager,
- event_state, event_time);
- fl_engine_send_mouse_pointer_event(
- self->engine, self->view_id, self->button_state != 0 ? kMove : kHover,
- event_time * kMicrosecondsPerMillisecond, event_x * scale_factor,
- event_y * scale_factor, get_device_kind((GdkEvent*)event), 0, 0,
- self->button_state);
-
- return TRUE;
+ return fl_pointer_manager_handle_motion(
+ self->pointer_manager, gdk_event_get_time(event), get_device_kind(event),
+ x * scale_factor, y * scale_factor);
}
// Signal handler for GtkWidget::enter-notify-event
static gboolean enter_notify_event_cb(FlView* self,
GdkEventCrossing* crossing_event) {
GdkEvent* event = reinterpret_cast<GdkEvent*>(crossing_event);
-
- if (self->engine == nullptr) {
- return FALSE;
- }
-
- check_pointer_inside(self, event);
-
- return TRUE;
+ gdouble x = 0.0, y = 0.0;
+ gdk_event_get_coords(event, &x, &y);
+ gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(self));
+ return fl_pointer_manager_handle_enter(
+ self->pointer_manager, gdk_event_get_time(event), get_device_kind(event),
+ x * scale_factor, y * scale_factor);
}
// Signal handler for GtkWidget::leave-notify-event
static gboolean leave_notify_event_cb(FlView* self,
GdkEventCrossing* crossing_event) {
- GdkEvent* event = reinterpret_cast<GdkEvent*>(crossing_event);
-
- guint event_time = gdk_event_get_time(event);
- gdouble event_x = 0.0, event_y = 0.0;
- gdk_event_get_coords(event, &event_x, &event_y);
-
if (crossing_event->mode != GDK_CROSSING_NORMAL) {
return FALSE;
}
- if (self->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) {
- gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(self));
- fl_engine_send_mouse_pointer_event(
- self->engine, self->view_id, kRemove,
- event_time * kMicrosecondsPerMillisecond, event_x * scale_factor,
- event_y * scale_factor, get_device_kind((GdkEvent*)event), 0, 0,
- self->button_state);
- self->pointer_inside = FALSE;
- }
-
- return TRUE;
+ GdkEvent* event = reinterpret_cast<GdkEvent*>(crossing_event);
+ gdouble x = 0.0, y = 0.0;
+ gdk_event_get_coords(event, &x, &y);
+ gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(self));
+ return fl_pointer_manager_handle_leave(
+ self->pointer_manager, gdk_event_get_time(event), get_device_kind(event),
+ x * scale_factor, y * scale_factor);
}
static void gesture_rotation_begin_cb(FlView* self) {
@@ -646,6 +586,7 @@
g_clear_pointer(&self->background_color, gdk_rgba_free);
g_clear_object(&self->window_state_monitor);
g_clear_object(&self->scrolling_manager);
+ g_clear_object(&self->pointer_manager);
g_clear_object(&self->keyboard_manager);
g_clear_object(&self->keyboard_handler);
g_clear_object(&self->mouse_cursor_handler);
@@ -666,21 +607,24 @@
}
// Implements GtkWidget::key_press_event.
-static gboolean fl_view_key_press_event(GtkWidget* widget, GdkEventKey* event) {
+static gboolean fl_view_key_press_event(GtkWidget* widget,
+ GdkEventKey* key_event) {
FlView* self = FL_VIEW(widget);
+ GdkEvent* event = reinterpret_cast<GdkEvent*>(key_event);
return fl_keyboard_manager_handle_event(
- self->keyboard_manager, fl_key_event_new_from_gdk_event(gdk_event_copy(
- reinterpret_cast<GdkEvent*>(event))));
+ self->keyboard_manager,
+ fl_key_event_new_from_gdk_event(gdk_event_copy(event)));
}
// Implements GtkWidget::key_release_event.
static gboolean fl_view_key_release_event(GtkWidget* widget,
- GdkEventKey* event) {
+ GdkEventKey* key_event) {
FlView* self = FL_VIEW(widget);
+ GdkEvent* event = reinterpret_cast<GdkEvent*>(key_event);
return fl_keyboard_manager_handle_event(
- self->keyboard_manager, fl_key_event_new_from_gdk_event(gdk_event_copy(
- reinterpret_cast<GdkEvent*>(event))));
+ self->keyboard_manager,
+ fl_key_event_new_from_gdk_event(gdk_event_copy(event)));
}
static void fl_view_class_init(FlViewClass* klass) {
@@ -769,6 +713,8 @@
g_assert(FL_IS_RENDERER_GDK(renderer));
self->renderer = FL_RENDERER_GDK(g_object_ref(renderer));
+ self->pointer_manager = fl_pointer_manager_new(self->view_id, engine);
+
fl_engine_set_update_semantics_handler(self->engine, update_semantics_cb,
self, nullptr);
self->on_pre_engine_restart_cb_id =
@@ -802,6 +748,8 @@
fl_renderer_add_renderable(FL_RENDERER(self->renderer), self->view_id,
FL_RENDERABLE(self));
+ self->pointer_manager = fl_pointer_manager_new(self->view_id, engine);
+
return self;
}