Flutter Engine
fl_view.cc
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "flutter/shell/platform/linux/public/flutter_linux/fl_view.h"
6 
7 #include <gdk/gdkwayland.h>
8 #ifdef GDK_WINDOWING_X11
9 #include <gdk/gdkx.h>
10 #endif
11 #include <cstring>
12 
13 #include "flutter/shell/platform/linux/fl_engine_private.h"
14 #include "flutter/shell/platform/linux/fl_key_event_plugin.h"
15 #include "flutter/shell/platform/linux/fl_mouse_cursor_plugin.h"
16 #include "flutter/shell/platform/linux/fl_platform_plugin.h"
17 #include "flutter/shell/platform/linux/fl_plugin_registrar_private.h"
18 #include "flutter/shell/platform/linux/fl_renderer_wayland.h"
19 #include "flutter/shell/platform/linux/fl_renderer_x11.h"
20 #include "flutter/shell/platform/linux/fl_text_input_plugin.h"
21 #include "flutter/shell/platform/linux/public/flutter_linux/fl_engine.h"
22 #include "flutter/shell/platform/linux/public/flutter_linux/fl_plugin_registry.h"
23 
24 static constexpr int kMicrosecondsPerMillisecond = 1000;
25 
26 struct _FlView {
27  GtkWidget parent_instance;
28 
29  // Project being run.
30  FlDartProject* project;
31 
32  // Rendering output.
33  FlRenderer* renderer;
34 
35  // Engine running @project.
36  FlEngine* engine;
37 
38  // Pointer button state recorded for sending status updates.
39  int64_t button_state;
40 
41  // Flutter system channel handlers.
42  FlKeyEventPlugin* key_event_plugin;
43  FlMouseCursorPlugin* mouse_cursor_plugin;
44  FlPlatformPlugin* platform_plugin;
45  FlTextInputPlugin* text_input_plugin;
46 };
47 
49 
51  FlPluginRegistryInterface* iface);
52 
54  FlView,
55  fl_view,
56  GTK_TYPE_WIDGET,
57  G_IMPLEMENT_INTERFACE(fl_plugin_registry_get_type(),
59 
60 // Converts a GDK button event into a Flutter event and sends it to the engine.
61 static gboolean fl_view_send_pointer_button_event(FlView* self,
62  GdkEventButton* event) {
63  int64_t button;
64  switch (event->button) {
65  case 1:
67  break;
68  case 2:
70  break;
71  case 3:
73  break;
74  default:
75  return FALSE;
76  }
77  int old_button_state = self->button_state;
79  if (event->type == GDK_BUTTON_PRESS) {
80  // Drop the event if Flutter already thinks the button is down.
81  if ((self->button_state & button) != 0) {
82  return FALSE;
83  }
84  self->button_state ^= button;
85 
86  phase = old_button_state == 0 ? kDown : kMove;
87  } else if (event->type == GDK_BUTTON_RELEASE) {
88  // Drop the event if Flutter already thinks the button is up.
89  if ((self->button_state & button) == 0) {
90  return FALSE;
91  }
92  self->button_state ^= button;
93 
94  phase = self->button_state == 0 ? kUp : kMove;
95  }
96 
97  if (self->engine == nullptr) {
98  return FALSE;
99  }
100 
101  gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(self));
103  self->engine, phase, event->time * kMicrosecondsPerMillisecond,
104  event->x * scale_factor, event->y * scale_factor, 0, 0,
105  self->button_state);
106 
107  return TRUE;
108 }
109 
110 // Updates the engine with the current window metrics.
111 static void fl_view_geometry_changed(FlView* self) {
112  GtkAllocation allocation;
113  gtk_widget_get_allocation(GTK_WIDGET(self), &allocation);
114  gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(self));
116  self->engine, allocation.width * scale_factor,
117  allocation.height * scale_factor, scale_factor);
118  fl_renderer_set_geometry(self->renderer, &allocation, scale_factor);
119 }
120 
121 // Implements FlPluginRegistry::get_registrar_for_plugin.
122 static FlPluginRegistrar* fl_view_get_registrar_for_plugin(
123  FlPluginRegistry* registry,
124  const gchar* name) {
125  FlView* self = FL_VIEW(registry);
126 
127  return fl_plugin_registrar_new(self,
128  fl_engine_get_binary_messenger(self->engine));
129 }
130 
132  FlPluginRegistryInterface* iface) {
133  iface->get_registrar_for_plugin = fl_view_get_registrar_for_plugin;
134 }
135 
136 static FlRenderer* fl_view_get_renderer_for_display(GdkDisplay* display) {
137 #ifdef GDK_WINDOWING_X11
138  if (GDK_IS_X11_DISPLAY(display)) {
139  return FL_RENDERER(fl_renderer_x11_new());
140  }
141 #endif
142 
143  if (GDK_IS_WAYLAND_DISPLAY(display)) {
144  return FL_RENDERER(fl_renderer_wayland_new());
145  }
146 
147  g_error("Unsupported GDK backend");
148 
149  return nullptr;
150 }
151 
152 static void fl_view_constructed(GObject* object) {
153  FlView* self = FL_VIEW(object);
154 
155  GdkDisplay* display = gtk_widget_get_display(GTK_WIDGET(self));
156  self->renderer = fl_view_get_renderer_for_display(display);
157  self->engine = fl_engine_new(self->project, self->renderer);
158 
159  // Create system channel handlers.
160  FlBinaryMessenger* messenger = fl_engine_get_binary_messenger(self->engine);
161  self->key_event_plugin = fl_key_event_plugin_new(messenger);
162  self->mouse_cursor_plugin = fl_mouse_cursor_plugin_new(messenger, self);
163  self->platform_plugin = fl_platform_plugin_new(messenger);
164  self->text_input_plugin = fl_text_input_plugin_new(messenger, self);
165 }
166 
167 static void fl_view_set_property(GObject* object,
168  guint prop_id,
169  const GValue* value,
170  GParamSpec* pspec) {
171  FlView* self = FL_VIEW(object);
172 
173  switch (prop_id) {
175  g_set_object(&self->project,
176  static_cast<FlDartProject*>(g_value_get_object(value)));
177  break;
178  default:
179  G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
180  break;
181  }
182 }
183 
184 static void fl_view_get_property(GObject* object,
185  guint prop_id,
186  GValue* value,
187  GParamSpec* pspec) {
188  FlView* self = FL_VIEW(object);
189 
190  switch (prop_id) {
192  g_value_set_object(value, self->project);
193  break;
194  default:
195  G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
196  break;
197  }
198 }
199 
200 static void fl_view_notify(GObject* object, GParamSpec* pspec) {
201  FlView* self = FL_VIEW(object);
202 
203  if (strcmp(pspec->name, "scale-factor") == 0) {
205  }
206 
207  if (G_OBJECT_CLASS(fl_view_parent_class)->notify != nullptr) {
208  G_OBJECT_CLASS(fl_view_parent_class)->notify(object, pspec);
209  }
210 }
211 
212 static void fl_view_dispose(GObject* object) {
213  FlView* self = FL_VIEW(object);
214 
215  g_clear_object(&self->project);
216  g_clear_object(&self->renderer);
217  g_clear_object(&self->engine);
218  g_clear_object(&self->key_event_plugin);
219  g_clear_object(&self->mouse_cursor_plugin);
220  g_clear_object(&self->platform_plugin);
221  g_clear_object(&self->text_input_plugin);
222 
223  G_OBJECT_CLASS(fl_view_parent_class)->dispose(object);
224 }
225 
226 // Implements GtkWidget::realize.
227 static void fl_view_realize(GtkWidget* widget) {
228  FlView* self = FL_VIEW(widget);
229  g_autoptr(GError) error = nullptr;
230 
231  gtk_widget_set_realized(widget, TRUE);
232 
233  if (!fl_renderer_start(self->renderer, widget, &error)) {
234  g_warning("Failed to start Flutter renderer: %s", error->message);
235  return;
236  }
237 
238  if (!fl_engine_start(self->engine, &error)) {
239  g_warning("Failed to start Flutter engine: %s", error->message);
240  return;
241  }
242 }
243 
244 // Implements GtkWidget::size-allocate.
245 static void fl_view_size_allocate(GtkWidget* widget,
246  GtkAllocation* allocation) {
247  FlView* self = FL_VIEW(widget);
248 
249  gtk_widget_set_allocation(widget, allocation);
250 
251  if (gtk_widget_get_realized(widget) && gtk_widget_get_has_window(widget)) {
252  gdk_window_move_resize(gtk_widget_get_window(widget), allocation->x,
253  allocation->y, allocation->width,
254  allocation->height);
255  }
256 
258 }
259 
260 // Implements GtkWidget::button_press_event.
261 static gboolean fl_view_button_press_event(GtkWidget* widget,
262  GdkEventButton* event) {
263  FlView* self = FL_VIEW(widget);
264 
265  // Flutter doesn't handle double and triple click events.
266  if (event->type == GDK_DOUBLE_BUTTON_PRESS ||
267  event->type == GDK_TRIPLE_BUTTON_PRESS) {
268  return FALSE;
269  }
270 
271  return fl_view_send_pointer_button_event(self, event);
272 }
273 
274 // Implements GtkWidget::button_release_event.
275 static gboolean fl_view_button_release_event(GtkWidget* widget,
276  GdkEventButton* event) {
277  FlView* self = FL_VIEW(widget);
278 
279  return fl_view_send_pointer_button_event(self, event);
280 }
281 
282 // Implements GtkWidget::scroll_event.
283 static gboolean fl_view_scroll_event(GtkWidget* widget, GdkEventScroll* event) {
284  FlView* self = FL_VIEW(widget);
285 
286  // TODO(robert-ancell): Update to use GtkEventControllerScroll when we can
287  // depend on GTK 3.24.
288 
289  gdouble scroll_delta_x = 0.0, scroll_delta_y = 0.0;
290  if (event->direction == GDK_SCROLL_SMOOTH) {
291  scroll_delta_x = event->delta_x;
292  scroll_delta_y = event->delta_y;
293  } else if (event->direction == GDK_SCROLL_UP) {
294  scroll_delta_y = -1;
295  } else if (event->direction == GDK_SCROLL_DOWN) {
296  scroll_delta_y = 1;
297  } else if (event->direction == GDK_SCROLL_LEFT) {
298  scroll_delta_x = -1;
299  } else if (event->direction == GDK_SCROLL_RIGHT) {
300  scroll_delta_x = 1;
301  }
302 
303  // TODO(robert-ancell): See if this can be queried from the OS; this value is
304  // chosen arbitrarily to get something that feels reasonable.
305  const int kScrollOffsetMultiplier = 20;
306  scroll_delta_x *= kScrollOffsetMultiplier;
307  scroll_delta_y *= kScrollOffsetMultiplier;
308 
309  gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(self));
311  self->engine, self->button_state != 0 ? kMove : kHover,
312  event->time * kMicrosecondsPerMillisecond, event->x * scale_factor,
313  event->y * scale_factor, scroll_delta_x, scroll_delta_y,
314  self->button_state);
315 
316  return TRUE;
317 }
318 
319 // Implements GtkWidget::motion_notify_event.
320 static gboolean fl_view_motion_notify_event(GtkWidget* widget,
321  GdkEventMotion* event) {
322  FlView* self = FL_VIEW(widget);
323 
324  if (self->engine == nullptr) {
325  return FALSE;
326  }
327 
328  gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(self));
330  self->engine, self->button_state != 0 ? kMove : kHover,
331  event->time * kMicrosecondsPerMillisecond, event->x * scale_factor,
332  event->y * scale_factor, 0, 0, self->button_state);
333 
334  return TRUE;
335 }
336 
337 // Implements GtkWidget::key_press_event.
338 static gboolean fl_view_key_press_event(GtkWidget* widget, GdkEventKey* event) {
339  FlView* self = FL_VIEW(widget);
340 
341  fl_key_event_plugin_send_key_event(self->key_event_plugin, event);
342  fl_text_input_plugin_filter_keypress(self->text_input_plugin, event);
343 
344  return TRUE;
345 }
346 
347 // Implements GtkWidget::key_release_event.
348 static gboolean fl_view_key_release_event(GtkWidget* widget,
349  GdkEventKey* event) {
350  FlView* self = FL_VIEW(widget);
351 
352  fl_key_event_plugin_send_key_event(self->key_event_plugin, event);
353  fl_text_input_plugin_filter_keypress(self->text_input_plugin, event);
354 
355  return TRUE;
356 }
357 
358 static void fl_view_class_init(FlViewClass* klass) {
359  G_OBJECT_CLASS(klass)->constructed = fl_view_constructed;
360  G_OBJECT_CLASS(klass)->set_property = fl_view_set_property;
361  G_OBJECT_CLASS(klass)->get_property = fl_view_get_property;
362  G_OBJECT_CLASS(klass)->notify = fl_view_notify;
363  G_OBJECT_CLASS(klass)->dispose = fl_view_dispose;
364  GTK_WIDGET_CLASS(klass)->realize = fl_view_realize;
365  GTK_WIDGET_CLASS(klass)->size_allocate = fl_view_size_allocate;
366  GTK_WIDGET_CLASS(klass)->button_press_event = fl_view_button_press_event;
367  GTK_WIDGET_CLASS(klass)->button_release_event = fl_view_button_release_event;
368  GTK_WIDGET_CLASS(klass)->scroll_event = fl_view_scroll_event;
369  GTK_WIDGET_CLASS(klass)->motion_notify_event = fl_view_motion_notify_event;
370  GTK_WIDGET_CLASS(klass)->key_press_event = fl_view_key_press_event;
371  GTK_WIDGET_CLASS(klass)->key_release_event = fl_view_key_release_event;
372 
373  g_object_class_install_property(
374  G_OBJECT_CLASS(klass), PROP_FLUTTER_PROJECT,
375  g_param_spec_object(
376  "flutter-project", "flutter-project", "Flutter project in use",
377  fl_dart_project_get_type(),
378  static_cast<GParamFlags>(G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
379  G_PARAM_STATIC_STRINGS)));
380 }
381 
382 static void fl_view_init(FlView* self) {
383  gtk_widget_set_can_focus(GTK_WIDGET(self), TRUE);
384 }
385 
386 G_MODULE_EXPORT FlView* fl_view_new(FlDartProject* project) {
387  return static_cast<FlView*>(
388  g_object_new(fl_view_get_type(), "flutter-project", project, nullptr));
389 }
390 
391 G_MODULE_EXPORT FlEngine* fl_view_get_engine(FlView* view) {
392  g_return_val_if_fail(FL_IS_VIEW(view), nullptr);
393  return view->engine;
394 }
static gboolean fl_view_scroll_event(GtkWidget *widget, GdkEventScroll *event)
Definition: fl_view.cc:283
Definition: embedder.h:479
static void fl_view_dispose(GObject *object)
Definition: fl_view.cc:212
static void fl_view_constructed(GObject *object)
Definition: fl_view.cc:152
FlPluginRegistrar * fl_plugin_registrar_new(FlView *view, FlBinaryMessenger *messenger)
fl_engine_send_mouse_pointer_event(self->engine, phase, event->time *kMicrosecondsPerMillisecond, event->x *scale_factor, event->y *scale_factor, 0, 0, self->button_state)
FlKeyEventPlugin * fl_key_event_plugin_new(FlBinaryMessenger *messenger, GAsyncReadyCallback response_callback, const char *channel_name)
static FlRenderer * fl_view_get_renderer_for_display(GdkDisplay *display)
Definition: fl_view.cc:136
static void fl_view_geometry_changed(FlView *self)
Definition: fl_view.cc:111
static void fl_view_init(FlView *self)
Definition: fl_view.cc:382
FlMethodResponse GError ** error
void fl_renderer_set_geometry(FlRenderer *self, GdkRectangle *geometry, gint scale)
Definition: fl_renderer.cc:202
GtkWidget parent_instance
Definition: fl_view.cc:27
FlutterPointerPhase
The phase of the pointer event.
Definition: embedder.h:471
FlMouseCursorPlugin * fl_mouse_cursor_plugin_new(FlBinaryMessenger *messenger, FlView *view)
int old_button_state
Definition: fl_view.cc:77
G_MODULE_EXPORT FlView * fl_view_new(FlDartProject *project)
Definition: fl_view.cc:386
static void fl_view_notify(GObject *object, GParamSpec *pspec)
Definition: fl_view.cc:200
gboolean fl_text_input_plugin_filter_keypress(FlTextInputPlugin *self, GdkEventKey *event)
gboolean fl_renderer_start(FlRenderer *self, GtkWidget *widget, GError **error)
Definition: fl_renderer.cc:182
static void fl_view_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
Definition: fl_view.cc:167
static void fl_view_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
Definition: fl_view.cc:184
G_MODULE_EXPORT FlEngine * fl_view_get_engine(FlView *view)
Definition: fl_view.cc:391
static void fl_view_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
Definition: fl_view.cc:245
static constexpr int kMicrosecondsPerMillisecond
Definition: fl_view.cc:24
FlKeyEventPlugin * key_event_plugin
Definition: fl_view.cc:42
static void fl_view_plugin_registry_iface_init(FlPluginRegistryInterface *iface)
Definition: fl_view.cc:131
FlEngine * engine
Definition: fl_view.cc:36
FlRendererWayland * fl_renderer_wayland_new()
uint8_t value
gboolean fl_engine_start(FlEngine *self, GError **error)
Definition: fl_engine.cc:352
int64_t button_state
Definition: fl_view.cc:39
static void fl_view_realize(GtkWidget *widget)
Definition: fl_view.cc:227
FlutterPointerPhase phase
Definition: fl_view.cc:78
The pointer moved while up.
Definition: embedder.h:503
FlTextInputPlugin * text_input_plugin
Definition: fl_view.cc:45
void fl_engine_send_window_metrics_event(FlEngine *self, size_t width, size_t height, double pixel_ratio)
Definition: fl_engine.cc:549
FlTextInputPlugin * fl_text_input_plugin_new(FlBinaryMessenger *messenger, FlView *view)
static gboolean fl_view_motion_notify_event(GtkWidget *widget, GdkEventMotion *event)
Definition: fl_view.cc:320
static gboolean fl_view_key_release_event(GtkWidget *widget, GdkEventKey *event)
Definition: fl_view.cc:348
static void fl_view_class_init(FlViewClass *klass)
Definition: fl_view.cc:358
GdkEventButton * event
Definition: fl_view.cc:62
gint scale_factor
Definition: fl_view.cc:101
void fl_key_event_plugin_send_key_event(FlKeyEventPlugin *self, GdkEventKey *event, gpointer user_data)
const char * name
Definition: fuchsia.cc:50
FlEngine * fl_engine_new(FlDartProject *project, FlRenderer *renderer)
Definition: fl_engine.cc:337
return TRUE
Definition: fl_view.cc:107
FlMouseCursorPlugin * mouse_cursor_plugin
Definition: fl_view.cc:43
static gboolean fl_view_button_press_event(GtkWidget *widget, GdkEventButton *event)
Definition: fl_view.cc:261
FlPlatformPlugin * fl_platform_plugin_new(FlBinaryMessenger *messenger)
G_MODULE_EXPORT FlBinaryMessenger * fl_engine_get_binary_messenger(FlEngine *self)
Definition: fl_engine.cc:597
FlDartProject * project
Definition: fl_view.cc:30
static gboolean fl_view_button_release_event(GtkWidget *widget, GdkEventButton *event)
Definition: fl_view.cc:275
static FlPluginRegistrar * fl_view_get_registrar_for_plugin(FlPluginRegistry *registry, const gchar *name)
Definition: fl_view.cc:122
FlRenderer * renderer
Definition: fl_view.cc:33
G_MODULE_EXPORT GType fl_plugin_registry_get_type()
G_DEFINE_TYPE_WITH_CODE(FlView, fl_view, GTK_TYPE_WIDGET, G_IMPLEMENT_INTERFACE(fl_plugin_registry_get_type(), fl_view_plugin_registry_iface_init)) static gboolean fl_view_send_pointer_button_event(FlView *self
static gboolean fl_view_key_press_event(GtkWidget *widget, GdkEventKey *event)
Definition: fl_view.cc:338
FlPlatformPlugin * platform_plugin
Definition: fl_view.cc:44