blob: 67f360bbd4b2a08a846638c983a517f3b2278447 [file] [log] [blame]
// 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;
}