[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:
Balint Rozgonyi 2024-12-11 23:05:04 +01:00 committed by GitHub
parent 8cfbdd7174
commit 41009bcda1
8 changed files with 593 additions and 1 deletions

View File

@ -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

View File

@ -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",

View File

@ -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,

View File

@ -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.

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

View 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_

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

View File

@ -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);