mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
[Linux] Add Multi-Touch Support for Linux (flutter/engine#54214)
This draft PR aims to address the lack of multi-touch support under Linux, leveraging the existing implementation used for Windows. As I am not an expert in this domain, I would greatly appreciate feedback on the implementation. https://github.com/flutter/flutter/issues/133239 https://github.com/flutter/flutter/issues/52202 [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
This commit is contained in:
parent
8cfbdd7174
commit
41009bcda1
@ -45153,6 +45153,9 @@ ORIGIN: ../../../flutter/shell/platform/linux/fl_texture_private.h + ../../../fl
|
||||
ORIGIN: ../../../flutter/shell/platform/linux/fl_texture_registrar.cc + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/shell/platform/linux/fl_texture_registrar_private.h + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/shell/platform/linux/fl_texture_registrar_test.cc + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/shell/platform/linux/fl_touch_manager.cc + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/shell/platform/linux/fl_touch_manager.h + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/shell/platform/linux/fl_touch_manager_test.cc + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/shell/platform/linux/fl_value.cc + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/shell/platform/linux/fl_value_test.cc + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/shell/platform/linux/fl_view.cc + ../../../flutter/LICENSE
|
||||
@ -48115,6 +48118,9 @@ FILE: ../../../flutter/shell/platform/linux/fl_texture_private.h
|
||||
FILE: ../../../flutter/shell/platform/linux/fl_texture_registrar.cc
|
||||
FILE: ../../../flutter/shell/platform/linux/fl_texture_registrar_private.h
|
||||
FILE: ../../../flutter/shell/platform/linux/fl_texture_registrar_test.cc
|
||||
FILE: ../../../flutter/shell/platform/linux/fl_touch_manager.cc
|
||||
FILE: ../../../flutter/shell/platform/linux/fl_touch_manager.h
|
||||
FILE: ../../../flutter/shell/platform/linux/fl_touch_manager_test.cc
|
||||
FILE: ../../../flutter/shell/platform/linux/fl_value.cc
|
||||
FILE: ../../../flutter/shell/platform/linux/fl_value_test.cc
|
||||
FILE: ../../../flutter/shell/platform/linux/fl_view.cc
|
||||
|
||||
@ -157,6 +157,7 @@ source_set("flutter_linux_sources") {
|
||||
"fl_texture.cc",
|
||||
"fl_texture_gl.cc",
|
||||
"fl_texture_registrar.cc",
|
||||
"fl_touch_manager.cc",
|
||||
"fl_value.cc",
|
||||
"fl_view.cc",
|
||||
"fl_view_accessible.cc",
|
||||
@ -245,6 +246,7 @@ executable("flutter_linux_unittests") {
|
||||
"fl_text_input_handler_test.cc",
|
||||
"fl_texture_gl_test.cc",
|
||||
"fl_texture_registrar_test.cc",
|
||||
"fl_touch_manager_test.cc",
|
||||
"fl_value_test.cc",
|
||||
"fl_view_accessible_test.cc",
|
||||
"fl_view_test.cc",
|
||||
|
||||
@ -933,6 +933,136 @@ void fl_engine_send_mouse_pointer_event(FlEngine* self,
|
||||
self->embedder_api.SendPointerEvent(self->engine, &fl_event, 1);
|
||||
}
|
||||
|
||||
void fl_engine_send_touch_up_event(FlEngine* self,
|
||||
FlutterViewId view_id,
|
||||
size_t timestamp,
|
||||
double x,
|
||||
double y,
|
||||
int32_t device) {
|
||||
g_return_if_fail(FL_IS_ENGINE(self));
|
||||
|
||||
if (self->engine == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
FlutterPointerEvent event;
|
||||
event.timestamp = timestamp;
|
||||
event.x = x;
|
||||
event.y = y;
|
||||
event.device_kind = kFlutterPointerDeviceKindTouch;
|
||||
event.device = device;
|
||||
event.buttons = 0;
|
||||
event.view_id = view_id;
|
||||
event.phase = FlutterPointerPhase::kUp;
|
||||
event.struct_size = sizeof(event);
|
||||
|
||||
self->embedder_api.SendPointerEvent(self->engine, &event, 1);
|
||||
}
|
||||
|
||||
void fl_engine_send_touch_down_event(FlEngine* self,
|
||||
FlutterViewId view_id,
|
||||
size_t timestamp,
|
||||
double x,
|
||||
double y,
|
||||
int32_t device) {
|
||||
g_return_if_fail(FL_IS_ENGINE(self));
|
||||
|
||||
if (self->engine == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
FlutterPointerEvent event;
|
||||
event.timestamp = timestamp;
|
||||
event.x = x;
|
||||
event.y = y;
|
||||
event.device_kind = kFlutterPointerDeviceKindTouch;
|
||||
event.device = device;
|
||||
event.buttons = FlutterPointerMouseButtons::kFlutterPointerButtonMousePrimary;
|
||||
event.view_id = view_id;
|
||||
event.phase = FlutterPointerPhase::kDown;
|
||||
event.struct_size = sizeof(event);
|
||||
|
||||
self->embedder_api.SendPointerEvent(self->engine, &event, 1);
|
||||
}
|
||||
|
||||
void fl_engine_send_touch_move_event(FlEngine* self,
|
||||
FlutterViewId view_id,
|
||||
size_t timestamp,
|
||||
double x,
|
||||
double y,
|
||||
int32_t device) {
|
||||
g_return_if_fail(FL_IS_ENGINE(self));
|
||||
|
||||
if (self->engine == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
FlutterPointerEvent event;
|
||||
event.timestamp = timestamp;
|
||||
event.x = x;
|
||||
event.y = y;
|
||||
event.device_kind = kFlutterPointerDeviceKindTouch;
|
||||
event.device = device;
|
||||
event.buttons = FlutterPointerMouseButtons::kFlutterPointerButtonMousePrimary;
|
||||
event.view_id = view_id;
|
||||
event.phase = FlutterPointerPhase::kMove;
|
||||
event.struct_size = sizeof(event);
|
||||
|
||||
self->embedder_api.SendPointerEvent(self->engine, &event, 1);
|
||||
}
|
||||
|
||||
void fl_engine_send_touch_add_event(FlEngine* self,
|
||||
FlutterViewId view_id,
|
||||
size_t timestamp,
|
||||
double x,
|
||||
double y,
|
||||
int32_t device) {
|
||||
g_return_if_fail(FL_IS_ENGINE(self));
|
||||
|
||||
if (self->engine == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
FlutterPointerEvent event;
|
||||
event.timestamp = timestamp;
|
||||
event.x = x;
|
||||
event.y = y;
|
||||
event.device_kind = kFlutterPointerDeviceKindTouch;
|
||||
event.device = device;
|
||||
event.buttons = 0;
|
||||
event.view_id = view_id;
|
||||
event.phase = FlutterPointerPhase::kAdd;
|
||||
event.struct_size = sizeof(event);
|
||||
|
||||
self->embedder_api.SendPointerEvent(self->engine, &event, 1);
|
||||
}
|
||||
|
||||
void fl_engine_send_touch_remove_event(FlEngine* self,
|
||||
FlutterViewId view_id,
|
||||
size_t timestamp,
|
||||
double x,
|
||||
double y,
|
||||
int32_t device) {
|
||||
g_return_if_fail(FL_IS_ENGINE(self));
|
||||
|
||||
if (self->engine == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
FlutterPointerEvent event;
|
||||
event.timestamp = timestamp;
|
||||
event.x = x;
|
||||
event.y = y;
|
||||
event.device_kind = kFlutterPointerDeviceKindTouch;
|
||||
event.device = device;
|
||||
event.buttons = 0;
|
||||
event.view_id = view_id;
|
||||
event.phase = FlutterPointerPhase::kRemove;
|
||||
event.struct_size = sizeof(event);
|
||||
|
||||
self->embedder_api.SendPointerEvent(self->engine, &event, 1);
|
||||
}
|
||||
|
||||
void fl_engine_send_pointer_pan_zoom_event(FlEngine* self,
|
||||
FlutterViewId view_id,
|
||||
size_t timestamp,
|
||||
|
||||
@ -252,6 +252,95 @@ void fl_engine_send_mouse_pointer_event(FlEngine* engine,
|
||||
double scroll_delta_y,
|
||||
int64_t buttons);
|
||||
|
||||
/**
|
||||
* fl_engine_send_touch_up_event:
|
||||
* @engine: an #FlEngine.
|
||||
* @view_id: the view that the event occured on.
|
||||
* @timestamp: time when event occurred in microseconds.
|
||||
* @x: x location of mouse cursor.
|
||||
* @y: y location of mouse cursor.
|
||||
* @device: device id.
|
||||
*
|
||||
* Sends a touch up event to the engine.
|
||||
*/
|
||||
void fl_engine_send_touch_up_event(FlEngine* engine,
|
||||
FlutterViewId view_id,
|
||||
size_t timestamp,
|
||||
double x,
|
||||
double y,
|
||||
int32_t device);
|
||||
|
||||
/**
|
||||
* fl_engine_send_touch_down_event:
|
||||
* @engine: an #FlEngine.
|
||||
* @view_id: the view that the event occured on.
|
||||
* @timestamp: time when event occurred in microseconds.
|
||||
* @x: x location of mouse cursor.
|
||||
* @y: y location of mouse cursor.
|
||||
* @device: device id.
|
||||
*
|
||||
* Sends a touch down event to the engine.
|
||||
*/
|
||||
void fl_engine_send_touch_down_event(FlEngine* engine,
|
||||
FlutterViewId view_id,
|
||||
size_t timestamp,
|
||||
double x,
|
||||
double y,
|
||||
int32_t device);
|
||||
/**
|
||||
* fl_engine_send_touch_move_event:
|
||||
* @engine: an #FlEngine.
|
||||
* @view_id: the view that the event occured on.
|
||||
* @timestamp: time when event occurred in microseconds.
|
||||
* @x: x location of mouse cursor.
|
||||
* @y: y location of mouse cursor.
|
||||
* @device: device id.
|
||||
*
|
||||
* Sends a touch move event to the engine.
|
||||
*/
|
||||
void fl_engine_send_touch_move_event(FlEngine* engine,
|
||||
FlutterViewId view_id,
|
||||
size_t timestamp,
|
||||
double x,
|
||||
double y,
|
||||
int32_t device);
|
||||
|
||||
/**
|
||||
* fl_engine_send_touch_add_event:
|
||||
* @engine: an #FlEngine.
|
||||
* @view_id: the view that the event occured on.
|
||||
* @timestamp: time when event occurred in microseconds.
|
||||
* @x: x location of mouse cursor.
|
||||
* @y: y location of mouse cursor.
|
||||
* @device: device id.
|
||||
*
|
||||
* Sends a touch add event to the engine.
|
||||
*/
|
||||
void fl_engine_send_touch_add_event(FlEngine* engine,
|
||||
FlutterViewId view_id,
|
||||
size_t timestamp,
|
||||
double x,
|
||||
double y,
|
||||
int32_t device);
|
||||
|
||||
/**
|
||||
* fl_engine_send_touch_remove_event:
|
||||
* @engine: an #FlEngine.
|
||||
* @view_id: the view that the event occured on.
|
||||
* @timestamp: time when event occurred in microseconds.
|
||||
* @x: x location of mouse cursor.
|
||||
* @y: y location of mouse cursor.
|
||||
* @device: device id.
|
||||
*
|
||||
* Sends a touch remove event to the engine.
|
||||
*/
|
||||
void fl_engine_send_touch_remove_event(FlEngine* engine,
|
||||
FlutterViewId view_id,
|
||||
size_t timestamp,
|
||||
double x,
|
||||
double y,
|
||||
int32_t device);
|
||||
|
||||
/**
|
||||
* fl_engine_send_pointer_pan_zoom_event:
|
||||
* @engine: an #FlEngine.
|
||||
|
||||
190
engine/src/flutter/shell/platform/linux/fl_touch_manager.cc
Normal file
190
engine/src/flutter/shell/platform/linux/fl_touch_manager.cc
Normal file
@ -0,0 +1,190 @@
|
||||
// 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_touch_manager.h"
|
||||
#include "flutter/shell/platform/linux/fl_engine_private.h"
|
||||
|
||||
static constexpr int kMicrosecondsPerMillisecond = 1000;
|
||||
static const int kMinTouchDeviceId = 0;
|
||||
static const int kMaxTouchDeviceId = 128;
|
||||
|
||||
struct _FlTouchManager {
|
||||
GObject parent_instance;
|
||||
|
||||
GWeakRef engine;
|
||||
|
||||
FlutterViewId view_id;
|
||||
|
||||
// List of touch device IDs that have been added to the engine.
|
||||
GList* added_touch_devices;
|
||||
|
||||
GHashTable* number_to_id;
|
||||
|
||||
// Minimum touch device ID that can be used.
|
||||
guint min_touch_device_id;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE(FlTouchManager, fl_touch_manager, G_TYPE_OBJECT);
|
||||
|
||||
static void fl_touch_manager_dispose(GObject* object) {
|
||||
FlTouchManager* self = FL_TOUCH_MANAGER(object);
|
||||
|
||||
g_weak_ref_clear(&self->engine);
|
||||
|
||||
g_list_free(self->added_touch_devices);
|
||||
|
||||
g_clear_pointer(&self->number_to_id, g_hash_table_unref);
|
||||
|
||||
G_OBJECT_CLASS(fl_touch_manager_parent_class)->dispose(object);
|
||||
}
|
||||
|
||||
static void fl_touch_manager_class_init(FlTouchManagerClass* klass) {
|
||||
G_OBJECT_CLASS(klass)->dispose = fl_touch_manager_dispose;
|
||||
}
|
||||
|
||||
static void fl_touch_manager_init(FlTouchManager* self) {}
|
||||
|
||||
FlTouchManager* fl_touch_manager_new(FlEngine* engine, FlutterViewId view_id) {
|
||||
g_return_val_if_fail(FL_IS_ENGINE(engine), nullptr);
|
||||
|
||||
FlTouchManager* self =
|
||||
FL_TOUCH_MANAGER(g_object_new(fl_touch_manager_get_type(), nullptr));
|
||||
|
||||
g_weak_ref_init(&self->engine, engine);
|
||||
self->view_id = view_id;
|
||||
|
||||
self->number_to_id =
|
||||
g_hash_table_new_full(g_direct_hash, g_direct_equal, nullptr, nullptr);
|
||||
|
||||
self->min_touch_device_id = kMinTouchDeviceId;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
// Ensures that a touch add event is sent for the given device.
|
||||
static void ensure_touch_added(_FlTouchManager* self,
|
||||
guint event_time,
|
||||
gdouble x,
|
||||
gdouble y,
|
||||
int32_t touch_id,
|
||||
int32_t device_id) {
|
||||
// Check if we need to send a touch add event.
|
||||
if (g_list_find(self->added_touch_devices, GINT_TO_POINTER(touch_id)) !=
|
||||
nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
g_autoptr(FlEngine) engine = FL_ENGINE(g_weak_ref_get(&self->engine));
|
||||
if (engine == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
fl_engine_send_touch_add_event(engine, self->view_id,
|
||||
event_time * kMicrosecondsPerMillisecond, x, y,
|
||||
device_id);
|
||||
|
||||
self->added_touch_devices =
|
||||
g_list_append(self->added_touch_devices, GINT_TO_POINTER(touch_id));
|
||||
}
|
||||
|
||||
// Generates a unique ID to represent |number|. The generated ID is the
|
||||
// smallest available ID greater than or equal to the minimum touch device ID.
|
||||
static uint32_t get_generated_id(_FlTouchManager* self, uint32_t number) {
|
||||
gpointer value;
|
||||
if (g_hash_table_lookup_extended(self->number_to_id, GUINT_TO_POINTER(number),
|
||||
nullptr, &value)) {
|
||||
uint32_t id;
|
||||
if (value == nullptr) {
|
||||
id = 0;
|
||||
} else {
|
||||
id = GPOINTER_TO_UINT(value);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
auto values = g_hash_table_get_values(self->number_to_id);
|
||||
while (values != nullptr &&
|
||||
g_list_find(values, GUINT_TO_POINTER(self->min_touch_device_id)) !=
|
||||
nullptr &&
|
||||
self->min_touch_device_id < kMaxTouchDeviceId) {
|
||||
++self->min_touch_device_id;
|
||||
}
|
||||
if (self->min_touch_device_id >= kMaxTouchDeviceId) {
|
||||
self->min_touch_device_id = kMinTouchDeviceId;
|
||||
}
|
||||
|
||||
g_hash_table_insert(self->number_to_id, GUINT_TO_POINTER(number),
|
||||
GUINT_TO_POINTER(self->min_touch_device_id));
|
||||
return self->min_touch_device_id;
|
||||
}
|
||||
|
||||
static void release_number(_FlTouchManager* self, uint32_t number) {
|
||||
if (g_hash_table_contains(self->number_to_id, GINT_TO_POINTER(number))) {
|
||||
auto id = g_hash_table_lookup(self->number_to_id, GINT_TO_POINTER(number));
|
||||
if (GPOINTER_TO_UINT(id) < self->min_touch_device_id) {
|
||||
self->min_touch_device_id = GPOINTER_TO_UINT(id);
|
||||
}
|
||||
g_hash_table_remove(self->number_to_id, GINT_TO_POINTER(number));
|
||||
}
|
||||
}
|
||||
|
||||
void fl_touch_manager_handle_touch_event(FlTouchManager* self,
|
||||
GdkEventTouch* touch_event,
|
||||
gint scale_factor) {
|
||||
g_return_if_fail(FL_IS_TOUCH_MANAGER(self));
|
||||
|
||||
g_autoptr(FlEngine) engine = FL_ENGINE(g_weak_ref_get(&self->engine));
|
||||
if (engine == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
GdkEvent* event = reinterpret_cast<GdkEvent*>(touch_event);
|
||||
// get sequence id from GdkEvent
|
||||
GdkEventSequence* seq = gdk_event_get_event_sequence(event);
|
||||
// cast pointer to int to get unique id
|
||||
uint32_t id = reinterpret_cast<uint64_t>(seq);
|
||||
// generate touch id from unique id
|
||||
auto touch_id = get_generated_id(self, id);
|
||||
// get device id
|
||||
auto device_id =
|
||||
static_cast<int32_t>(kFlutterPointerDeviceKindTouch) << 28 | touch_id;
|
||||
|
||||
gdouble event_x = 0.0, event_y = 0.0;
|
||||
gdk_event_get_coords(event, &event_x, &event_y);
|
||||
|
||||
double x = event_x * scale_factor;
|
||||
double y = event_y * scale_factor;
|
||||
|
||||
guint event_time = gdk_event_get_time(event);
|
||||
|
||||
ensure_touch_added(self, event_time, x, y, touch_id, device_id);
|
||||
|
||||
GdkEventType touch_event_type = gdk_event_get_event_type(event);
|
||||
|
||||
switch (touch_event_type) {
|
||||
case GDK_TOUCH_BEGIN:
|
||||
fl_engine_send_touch_down_event(engine, self->view_id,
|
||||
event_time * kMicrosecondsPerMillisecond,
|
||||
x, y, device_id);
|
||||
break;
|
||||
case GDK_TOUCH_UPDATE:
|
||||
fl_engine_send_touch_move_event(engine, self->view_id,
|
||||
event_time * kMicrosecondsPerMillisecond,
|
||||
x, y, device_id);
|
||||
break;
|
||||
case GDK_TOUCH_END:
|
||||
fl_engine_send_touch_up_event(engine, self->view_id,
|
||||
event_time * kMicrosecondsPerMillisecond, x,
|
||||
y, device_id);
|
||||
|
||||
fl_engine_send_touch_remove_event(
|
||||
engine, self->view_id, event_time * kMicrosecondsPerMillisecond, x, y,
|
||||
device_id);
|
||||
release_number(self, id);
|
||||
self->added_touch_devices =
|
||||
g_list_remove(self->added_touch_devices, GINT_TO_POINTER(touch_id));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
44
engine/src/flutter/shell/platform/linux/fl_touch_manager.h
Normal file
44
engine/src/flutter/shell/platform/linux/fl_touch_manager.h
Normal file
@ -0,0 +1,44 @@
|
||||
// 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_TOUCH_MANAGER_H_
|
||||
#define FLUTTER_SHELL_PLATFORM_LINUX_FL_TOUCH_MANAGER_H_
|
||||
|
||||
#include <gdk/gdk.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(FlTouchManager,
|
||||
fl_touch_manager,
|
||||
FL,
|
||||
TOUCH_MANAGER,
|
||||
GObject);
|
||||
|
||||
/**
|
||||
* fl_touch_manager_new:
|
||||
* @engine: an #FlEngine.
|
||||
* @view_id: the view being managed.
|
||||
*
|
||||
* Create a new #FlTouchManager.
|
||||
*
|
||||
* Returns: a new #FlTouchManager.
|
||||
*/
|
||||
FlTouchManager* fl_touch_manager_new(FlEngine* engine, FlutterViewId view_id);
|
||||
|
||||
/**
|
||||
* fl_touch_manager_handle_touch_event:
|
||||
* @manager: an #FlTouchManager.
|
||||
* @event: the touch event.
|
||||
* @scale_factor: the GTK scaling factor of the window.
|
||||
*/
|
||||
void fl_touch_manager_handle_touch_event(FlTouchManager* manager,
|
||||
GdkEventTouch* event,
|
||||
gint scale_factor);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_TOUCH_MANAGER_H_
|
||||
103
engine/src/flutter/shell/platform/linux/fl_touch_manager_test.cc
Normal file
103
engine/src/flutter/shell/platform/linux/fl_touch_manager_test.cc
Normal file
@ -0,0 +1,103 @@
|
||||
// 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_touch_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 <cstring>
|
||||
#include <vector>
|
||||
|
||||
#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;
|
||||
}));
|
||||
}
|
||||
|
||||
struct _FakeGdkDevice {
|
||||
GObject parent_instance;
|
||||
gchar* name;
|
||||
GdkInputSource source;
|
||||
};
|
||||
static GdkDevice* makeFakeDevice(GdkInputSource source) {
|
||||
_FakeGdkDevice* device =
|
||||
static_cast<_FakeGdkDevice*>(g_malloc0(sizeof(_FakeGdkDevice)));
|
||||
device->source = source;
|
||||
// Bully the type checker
|
||||
(reinterpret_cast<GTypeInstance*>(device))->g_class =
|
||||
static_cast<GTypeClass*>(g_malloc0(sizeof(GTypeClass)));
|
||||
(reinterpret_cast<GTypeInstance*>(device))->g_class->g_type = GDK_TYPE_DEVICE;
|
||||
return reinterpret_cast<GdkDevice*>(device);
|
||||
}
|
||||
|
||||
TEST(FlTouchManagerTest, TouchEvents) {
|
||||
g_autoptr(FlEngine) engine = make_mock_engine();
|
||||
std::vector<FlutterPointerEvent> pointer_events;
|
||||
log_pointer_events(engine, pointer_events);
|
||||
|
||||
g_autoptr(FlTouchManager) manager = fl_touch_manager_new(engine, 0);
|
||||
|
||||
GdkDevice* touchscreen = makeFakeDevice(GDK_SOURCE_TOUCHSCREEN);
|
||||
GdkEventTouch* event =
|
||||
reinterpret_cast<GdkEventTouch*>(gdk_event_new(GDK_TOUCH_BEGIN));
|
||||
event->time = 1;
|
||||
event->x = 4.0;
|
||||
event->y = 8.0;
|
||||
event->device = touchscreen;
|
||||
fl_touch_manager_handle_touch_event(manager, event, 1.0);
|
||||
EXPECT_EQ(pointer_events.size(), 2u);
|
||||
EXPECT_EQ(pointer_events[0].x, 4.0);
|
||||
EXPECT_EQ(pointer_events[0].y, 8.0);
|
||||
EXPECT_EQ(pointer_events[0].device_kind, kFlutterPointerDeviceKindTouch);
|
||||
EXPECT_EQ(pointer_events[0].timestamp,
|
||||
1000lu); // Milliseconds -> Microseconds
|
||||
EXPECT_EQ(pointer_events[0].phase, kAdd);
|
||||
|
||||
EXPECT_EQ(pointer_events[1].x, 4.0);
|
||||
EXPECT_EQ(pointer_events[1].y, 8.0);
|
||||
EXPECT_EQ(pointer_events[1].device_kind, kFlutterPointerDeviceKindTouch);
|
||||
EXPECT_EQ(pointer_events[1].timestamp,
|
||||
1000lu); // Milliseconds -> Microseconds
|
||||
EXPECT_EQ(pointer_events[1].phase, kDown);
|
||||
|
||||
event->type = GDK_TOUCH_UPDATE;
|
||||
fl_touch_manager_handle_touch_event(manager, event, 1.0);
|
||||
EXPECT_EQ(pointer_events.size(), 3u);
|
||||
EXPECT_EQ(pointer_events[2].x, 4.0);
|
||||
EXPECT_EQ(pointer_events[2].y, 8.0);
|
||||
EXPECT_EQ(pointer_events[2].device_kind, kFlutterPointerDeviceKindTouch);
|
||||
EXPECT_EQ(pointer_events[2].timestamp,
|
||||
1000lu); // Milliseconds -> Microseconds
|
||||
EXPECT_EQ(pointer_events[2].phase, kMove);
|
||||
|
||||
event->type = GDK_TOUCH_END;
|
||||
fl_touch_manager_handle_touch_event(manager, event, 1.0);
|
||||
EXPECT_EQ(pointer_events.size(), 5u);
|
||||
EXPECT_EQ(pointer_events[3].x, 4.0);
|
||||
EXPECT_EQ(pointer_events[3].y, 8.0);
|
||||
EXPECT_EQ(pointer_events[3].device_kind, kFlutterPointerDeviceKindTouch);
|
||||
EXPECT_EQ(pointer_events[3].timestamp,
|
||||
1000lu); // Milliseconds -> Microseconds
|
||||
EXPECT_EQ(pointer_events[3].phase, kUp);
|
||||
|
||||
EXPECT_EQ(pointer_events[4].x, 4.0);
|
||||
EXPECT_EQ(pointer_events[4].y, 8.0);
|
||||
EXPECT_EQ(pointer_events[4].device_kind, kFlutterPointerDeviceKindTouch);
|
||||
EXPECT_EQ(pointer_events[4].timestamp,
|
||||
1000lu); // Milliseconds -> Microseconds
|
||||
EXPECT_EQ(pointer_events[4].phase, kRemove);
|
||||
}
|
||||
@ -23,6 +23,7 @@
|
||||
#include "flutter/shell/platform/linux/fl_socket_accessible.h"
|
||||
#include "flutter/shell/platform/linux/fl_text_input_handler.h"
|
||||
#include "flutter/shell/platform/linux/fl_text_input_view_delegate.h"
|
||||
#include "flutter/shell/platform/linux/fl_touch_manager.h"
|
||||
#include "flutter/shell/platform/linux/fl_view_accessible.h"
|
||||
#include "flutter/shell/platform/linux/fl_window_state_monitor.h"
|
||||
#include "flutter/shell/platform/linux/public/flutter_linux/fl_engine.h"
|
||||
@ -61,6 +62,9 @@ struct _FlView {
|
||||
// Manages pointer events.
|
||||
FlPointerManager* pointer_manager;
|
||||
|
||||
// Manages touch events.
|
||||
FlTouchManager* touch_manager;
|
||||
|
||||
// Manages keyboard events.
|
||||
FlKeyboardManager* keyboard_manager;
|
||||
|
||||
@ -147,6 +151,11 @@ static void init_scrolling(FlView* self) {
|
||||
fl_scrolling_manager_new(self->engine, self->view_id);
|
||||
}
|
||||
|
||||
static void init_touch(FlView* self) {
|
||||
g_clear_object(&self->touch_manager);
|
||||
self->touch_manager = fl_touch_manager_new(self->engine, self->view_id);
|
||||
}
|
||||
|
||||
static FlutterPointerDeviceKind get_device_kind(GdkEvent* event) {
|
||||
GdkDevice* device = gdk_event_get_source_device(event);
|
||||
GdkInputSource source = gdk_device_get_source(device);
|
||||
@ -266,6 +275,7 @@ static void update_semantics_cb(FlEngine* engine,
|
||||
static void on_pre_engine_restart_cb(FlView* self) {
|
||||
init_keyboard(self);
|
||||
init_scrolling(self);
|
||||
init_touch(self);
|
||||
}
|
||||
|
||||
// Implements FlRenderable::redraw
|
||||
@ -407,12 +417,26 @@ static gboolean scroll_event_cb(FlView* self, GdkEventScroll* event) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean touch_event_cb(FlView* self, GdkEventTouch* event) {
|
||||
fl_touch_manager_handle_touch_event(
|
||||
self->touch_manager, event,
|
||||
gtk_widget_get_scale_factor(GTK_WIDGET(self)));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Signal handler for GtkWidget::motion-notify-event
|
||||
static gboolean motion_notify_event_cb(FlView* self,
|
||||
GdkEventMotion* motion_event) {
|
||||
GdkEvent* event = reinterpret_cast<GdkEvent*>(motion_event);
|
||||
sync_modifier_if_needed(self, event);
|
||||
|
||||
// return if touch event
|
||||
auto event_type = gdk_event_get_event_type(event);
|
||||
if (event_type == GDK_TOUCH_BEGIN || event_type == GDK_TOUCH_UPDATE ||
|
||||
event_type == GDK_TOUCH_END || event_type == GDK_TOUCH_CANCEL) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
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));
|
||||
@ -481,6 +505,7 @@ static GdkGLContext* create_context_cb(FlView* self) {
|
||||
gtk_widget_get_parent_window(GTK_WIDGET(self)));
|
||||
|
||||
init_scrolling(self);
|
||||
init_touch(self);
|
||||
|
||||
g_autoptr(GError) error = nullptr;
|
||||
if (!fl_renderer_gdk_create_contexts(self->renderer, &error)) {
|
||||
@ -616,6 +641,7 @@ static void fl_view_dispose(GObject* object) {
|
||||
g_clear_object(&self->window_state_monitor);
|
||||
g_clear_object(&self->scrolling_manager);
|
||||
g_clear_object(&self->pointer_manager);
|
||||
g_clear_object(&self->touch_manager);
|
||||
g_clear_object(&self->keyboard_manager);
|
||||
g_clear_object(&self->keyboard_handler);
|
||||
g_clear_object(&self->view_accessible);
|
||||
@ -692,7 +718,7 @@ static void fl_view_init(FlView* self) {
|
||||
gtk_widget_add_events(event_box,
|
||||
GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK |
|
||||
GDK_BUTTON_RELEASE_MASK | GDK_SCROLL_MASK |
|
||||
GDK_SMOOTH_SCROLL_MASK);
|
||||
GDK_SMOOTH_SCROLL_MASK | GDK_TOUCH_MASK);
|
||||
|
||||
g_signal_connect_swapped(event_box, "button-press-event",
|
||||
G_CALLBACK(button_press_event_cb), self);
|
||||
@ -719,6 +745,8 @@ static void fl_view_init(FlView* self) {
|
||||
G_CALLBACK(gesture_rotation_update_cb), self);
|
||||
g_signal_connect_swapped(rotate, "end", G_CALLBACK(gesture_rotation_end_cb),
|
||||
self);
|
||||
g_signal_connect_swapped(event_box, "touch-event", G_CALLBACK(touch_event_cb),
|
||||
self);
|
||||
|
||||
self->gl_area = GTK_GL_AREA(gtk_gl_area_new());
|
||||
gtk_gl_area_set_has_alpha(self->gl_area, TRUE);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user