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