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 "flutter/shell/platform/linux/fl_view_private.h"
8 
9 #include <cstring>
10 
11 #include "flutter/shell/platform/linux/fl_accessibility_plugin.h"
12 #include "flutter/shell/platform/linux/fl_engine_private.h"
13 #include "flutter/shell/platform/linux/fl_key_channel_responder.h"
14 #include "flutter/shell/platform/linux/fl_key_embedder_responder.h"
15 #include "flutter/shell/platform/linux/fl_key_event.h"
16 #include "flutter/shell/platform/linux/fl_keyboard_manager.h"
17 #include "flutter/shell/platform/linux/fl_mouse_cursor_plugin.h"
18 #include "flutter/shell/platform/linux/fl_platform_plugin.h"
19 #include "flutter/shell/platform/linux/fl_plugin_registrar_private.h"
20 #include "flutter/shell/platform/linux/fl_renderer_gl.h"
21 #include "flutter/shell/platform/linux/fl_view_accessible.h"
22 #include "flutter/shell/platform/linux/public/flutter_linux/fl_engine.h"
23 #include "flutter/shell/platform/linux/public/flutter_linux/fl_plugin_registry.h"
24 
25 static constexpr int kMicrosecondsPerMillisecond = 1000;
26 
27 struct _FlView {
28  GtkContainer parent_instance;
29 
30  // Project being run.
31  FlDartProject* project;
32 
33  // Rendering output.
34  FlRenderer* renderer;
35 
36  // Engine running @project.
37  FlEngine* engine;
38 
39  // Pointer button state recorded for sending status updates.
40  int64_t button_state;
41 
42  // Flutter system channel handlers.
43  FlAccessibilityPlugin* accessibility_plugin;
45  FlMouseCursorPlugin* mouse_cursor_plugin;
46  FlPlatformPlugin* platform_plugin;
47 
48  GList* gl_area_list;
50 
51  GtkWidget* event_box;
52 
53  GList* children_list;
55 
56  // Tracks whether mouse pointer is inside the view.
57  gboolean pointer_inside;
58 };
59 
60 typedef struct _FlViewChild {
61  GtkWidget* widget;
62  GdkRectangle geometry;
63 } FlViewChild;
64 
66 
68  FlPluginRegistryInterface* iface);
69 
70 static gboolean text_input_im_filter_by_gtk(GtkIMContext* im_context,
71  gpointer gdk_event);
72 
73 static void redispatch_key_event_by_gtk(gpointer gdk_event);
74 
76  FlView,
77  fl_view,
78  GTK_TYPE_CONTAINER,
79  G_IMPLEMENT_INTERFACE(fl_plugin_registry_get_type(),
81 
82 static void fl_view_update_semantics_node_cb(FlEngine* engine,
84  gpointer user_data) {
85  FlView* self = FL_VIEW(user_data);
86 
88  self->accessibility_plugin, node);
89 }
90 
91 static void fl_view_init_keyboard(FlView* self) {
92  FlBinaryMessenger* messenger = fl_engine_get_binary_messenger(self->engine);
93  self->keyboard_manager = fl_keyboard_manager_new(
96  // The embedder responder must be added before the channel responder.
98  self->keyboard_manager,
99  FL_KEY_RESPONDER(fl_key_embedder_responder_new(self->engine)));
101  self->keyboard_manager,
102  FL_KEY_RESPONDER(fl_key_channel_responder_new(messenger)));
103 }
104 
105 // Called when the engine is restarted.
106 //
107 // This method should reset states to be as if the engine had just been started,
108 // which usually indicates the user has requested a hot restart (Shift-R in the
109 // Flutter CLI.)
111  gpointer user_data) {
112  FlView* self = FL_VIEW(user_data);
113 
114  g_clear_object(&self->keyboard_manager);
115  fl_view_init_keyboard(self);
116 }
117 
118 // Converts a GDK button event into a Flutter event and sends it to the engine.
119 static gboolean fl_view_send_pointer_button_event(FlView* self,
120  GdkEventButton* event) {
121  int64_t button;
122  switch (event->button) {
123  case 1:
125  break;
126  case 2:
128  break;
129  case 3:
131  break;
132  default:
133  return FALSE;
134  }
135  int old_button_state = self->button_state;
136  FlutterPointerPhase phase = kMove;
137  if (event->type == GDK_BUTTON_PRESS) {
138  // Drop the event if Flutter already thinks the button is down.
139  if ((self->button_state & button) != 0) {
140  return FALSE;
141  }
142  self->button_state ^= button;
143 
144  phase = old_button_state == 0 ? kDown : kMove;
145  } else if (event->type == GDK_BUTTON_RELEASE) {
146  // Drop the event if Flutter already thinks the button is up.
147  if ((self->button_state & button) == 0) {
148  return FALSE;
149  }
150  self->button_state ^= button;
151 
152  phase = self->button_state == 0 ? kUp : kMove;
153  }
154 
155  if (self->engine == nullptr) {
156  return FALSE;
157  }
158 
159  gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(self));
161  self->engine, phase, event->time * kMicrosecondsPerMillisecond,
162  event->x * scale_factor, event->y * scale_factor, 0, 0,
163  self->button_state);
164 
165  return TRUE;
166 }
167 
168 // Updates the engine with the current window metrics.
169 static void fl_view_geometry_changed(FlView* self) {
170  GtkAllocation allocation;
171  gtk_widget_get_allocation(GTK_WIDGET(self), &allocation);
172  gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(self));
174  self->engine, allocation.width * scale_factor,
175  allocation.height * scale_factor, scale_factor);
176 
177  fl_renderer_wait_for_frame(self->renderer, allocation.width * scale_factor,
178  allocation.height * scale_factor);
179 }
180 
181 // Implements FlPluginRegistry::get_registrar_for_plugin.
182 static FlPluginRegistrar* fl_view_get_registrar_for_plugin(
183  FlPluginRegistry* registry,
184  const gchar* name) {
185  FlView* self = FL_VIEW(registry);
186 
187  return fl_plugin_registrar_new(self,
188  fl_engine_get_binary_messenger(self->engine),
189  fl_engine_get_texture_registrar(self->engine));
190 }
191 
193  FlPluginRegistryInterface* iface) {
194  iface->get_registrar_for_plugin = fl_view_get_registrar_for_plugin;
195 }
196 
197 static gboolean event_box_button_release_event(GtkWidget* widget,
198  GdkEventButton* event,
199  FlView* view);
200 
201 static gboolean event_box_button_press_event(GtkWidget* widget,
202  GdkEventButton* event,
203  FlView* view);
204 
205 static gboolean event_box_scroll_event(GtkWidget* widget,
206  GdkEventScroll* event,
207  FlView* view);
208 
209 static gboolean event_box_motion_notify_event(GtkWidget* widget,
210  GdkEventMotion* event,
211  FlView* view);
212 
213 static gboolean event_box_enter_notify_event(GtkWidget* widget,
214  GdkEventCrossing* event,
215  FlView* view);
216 
217 static gboolean event_box_leave_notify_event(GtkWidget* widget,
218  GdkEventCrossing* event,
219  FlView* view);
220 
221 static void fl_view_constructed(GObject* object) {
222  FlView* self = FL_VIEW(object);
223 
224  self->renderer = FL_RENDERER(fl_renderer_gl_new());
225  self->engine = fl_engine_new(self->project, self->renderer);
227  self->engine, fl_view_update_semantics_node_cb, self, nullptr);
229  self->engine, fl_view_on_pre_engine_restart_cb, self, nullptr);
230 
231  // Create system channel handlers.
232  FlBinaryMessenger* messenger = fl_engine_get_binary_messenger(self->engine);
233  self->accessibility_plugin = fl_accessibility_plugin_new(self);
234  fl_view_init_keyboard(self);
235  self->mouse_cursor_plugin = fl_mouse_cursor_plugin_new(messenger, self);
236  self->platform_plugin = fl_platform_plugin_new(messenger);
237 
238  self->event_box = gtk_event_box_new();
239  gtk_widget_set_parent(self->event_box, GTK_WIDGET(self));
240  gtk_widget_show(self->event_box);
241  gtk_widget_add_events(self->event_box,
242  GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK |
243  GDK_BUTTON_RELEASE_MASK | GDK_SCROLL_MASK |
244  GDK_SMOOTH_SCROLL_MASK);
245 
246  g_signal_connect(self->event_box, "button-press-event",
247  G_CALLBACK(event_box_button_press_event), self);
248  g_signal_connect(self->event_box, "button-release-event",
249  G_CALLBACK(event_box_button_release_event), self);
250  g_signal_connect(self->event_box, "scroll-event",
251  G_CALLBACK(event_box_scroll_event), self);
252  g_signal_connect(self->event_box, "motion-notify-event",
253  G_CALLBACK(event_box_motion_notify_event), self);
254  g_signal_connect(self->event_box, "enter-notify-event",
255  G_CALLBACK(event_box_enter_notify_event), self);
256  g_signal_connect(self->event_box, "leave-notify-event",
257  G_CALLBACK(event_box_leave_notify_event), self);
258 }
259 
260 static void fl_view_set_property(GObject* object,
261  guint prop_id,
262  const GValue* value,
263  GParamSpec* pspec) {
264  FlView* self = FL_VIEW(object);
265 
266  switch (prop_id) {
268  g_set_object(&self->project,
269  static_cast<FlDartProject*>(g_value_get_object(value)));
270  break;
271  default:
272  G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
273  break;
274  }
275 }
276 
277 static void fl_view_get_property(GObject* object,
278  guint prop_id,
279  GValue* value,
280  GParamSpec* pspec) {
281  FlView* self = FL_VIEW(object);
282 
283  switch (prop_id) {
285  g_value_set_object(value, self->project);
286  break;
287  default:
288  G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
289  break;
290  }
291 }
292 
293 static void fl_view_notify(GObject* object, GParamSpec* pspec) {
294  FlView* self = FL_VIEW(object);
295 
296  if (strcmp(pspec->name, "scale-factor") == 0) {
298  }
299 
300  if (G_OBJECT_CLASS(fl_view_parent_class)->notify != nullptr) {
301  G_OBJECT_CLASS(fl_view_parent_class)->notify(object, pspec);
302  }
303 }
304 
305 static void fl_view_dispose(GObject* object) {
306  FlView* self = FL_VIEW(object);
307 
308  if (self->engine != nullptr) {
309  fl_engine_set_update_semantics_node_handler(self->engine, nullptr, nullptr,
310  nullptr);
311  fl_engine_set_on_pre_engine_restart_handler(self->engine, nullptr, nullptr,
312  nullptr);
313  }
314 
315  g_clear_object(&self->project);
316  g_clear_object(&self->renderer);
317  g_clear_object(&self->engine);
318  g_clear_object(&self->accessibility_plugin);
319  g_clear_object(&self->keyboard_manager);
320  g_clear_object(&self->mouse_cursor_plugin);
321  g_clear_object(&self->platform_plugin);
322  g_list_free_full(self->gl_area_list, g_object_unref);
323  self->gl_area_list = nullptr;
324 
325  G_OBJECT_CLASS(fl_view_parent_class)->dispose(object);
326 }
327 
328 // Implements GtkWidget::realize.
329 static void fl_view_realize(GtkWidget* widget) {
330  FlView* self = FL_VIEW(widget);
331  g_autoptr(GError) error = nullptr;
332 
333  GtkAllocation allocation;
334  gtk_widget_get_allocation(widget, &allocation);
335 
336  gtk_widget_set_realized(widget, TRUE);
337 
338  GdkWindowAttr attributes;
339  attributes.window_type = GDK_WINDOW_CHILD;
340  attributes.x = allocation.x;
341  attributes.y = allocation.y;
342  attributes.width = allocation.width;
343  attributes.height = allocation.height;
344  attributes.wclass = GDK_INPUT_OUTPUT;
345  attributes.visual = gtk_widget_get_visual(widget);
346  attributes.event_mask =
347  gtk_widget_get_events(widget) | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK;
348  gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
349  GdkWindow* window = gdk_window_new(gtk_widget_get_parent_window(widget),
350  &attributes, attributes_mask);
351  gtk_widget_set_window(widget, window);
352  gtk_widget_register_window(widget, window);
353 
354  if (!fl_renderer_start(self->renderer, self, &error)) {
355  g_warning("Failed to start Flutter renderer: %s", error->message);
356  return;
357  }
358 
359  if (!fl_engine_start(self->engine, &error)) {
360  g_warning("Failed to start Flutter engine: %s", error->message);
361  return;
362  }
363 }
364 
365 static void fl_view_get_preferred_width(GtkWidget* widget,
366  gint* minimum,
367  gint* natural) {
368  FlView* self = FL_VIEW(widget);
369  gint child_min, child_nat;
370 
371  *minimum = 0;
372  *natural = 0;
373 
374  for (GList* iterator = self->children_list; iterator;
375  iterator = iterator->next) {
376  FlViewChild* child = reinterpret_cast<FlViewChild*>(iterator->data);
377 
378  if (!gtk_widget_get_visible(child->widget))
379  continue;
380 
381  gtk_widget_get_preferred_width(child->widget, &child_min, &child_nat);
382 
383  *minimum = MAX(*minimum, child->geometry.x + child_min);
384  *natural = MAX(*natural, child->geometry.x + child_nat);
385  }
386 }
387 
388 static void fl_view_get_preferred_height(GtkWidget* widget,
389  gint* minimum,
390  gint* natural) {
391  FlView* self = FL_VIEW(widget);
392  gint child_min, child_nat;
393 
394  *minimum = 0;
395  *natural = 0;
396 
397  for (GList* iterator = self->children_list; iterator;
398  iterator = iterator->next) {
399  FlViewChild* child = reinterpret_cast<FlViewChild*>(iterator->data);
400 
401  if (!gtk_widget_get_visible(child->widget))
402  continue;
403 
404  gtk_widget_get_preferred_height(child->widget, &child_min, &child_nat);
405 
406  *minimum = MAX(*minimum, child->geometry.y + child_min);
407  *natural = MAX(*natural, child->geometry.y + child_nat);
408  }
409 }
410 
411 // Implements GtkWidget::size-allocate.
412 static void fl_view_size_allocate(GtkWidget* widget,
413  GtkAllocation* allocation) {
414  FlView* self = FL_VIEW(widget);
415 
416  gtk_widget_set_allocation(widget, allocation);
417 
418  if (gtk_widget_get_has_window(widget)) {
419  if (gtk_widget_get_realized(widget))
420  gdk_window_move_resize(gtk_widget_get_window(widget), allocation->x,
421  allocation->y, allocation->width,
422  allocation->height);
423  }
424 
425  for (GList* iterator = self->children_list; iterator;
426  iterator = iterator->next) {
427  FlViewChild* child = reinterpret_cast<FlViewChild*>(iterator->data);
428  if (!gtk_widget_get_visible(child->widget))
429  continue;
430 
431  GtkAllocation child_allocation = child->geometry;
432  GtkRequisition child_requisition;
433  gtk_widget_get_preferred_size(child->widget, &child_requisition, NULL);
434 
435  if (!gtk_widget_get_has_window(widget)) {
436  child_allocation.x += allocation->x;
437  child_allocation.y += allocation->y;
438  }
439 
440  if (child_allocation.width == 0 && child_allocation.height == 0) {
441  child_allocation.width = allocation->width;
442  child_allocation.height = allocation->height;
443  }
444 
445  gtk_widget_size_allocate(child->widget, &child_allocation);
446  }
447 
448  GtkAllocation event_box_allocation = {
449  .x = 0,
450  .y = 0,
451  .width = allocation->width,
452  .height = allocation->height,
453  };
454  if (!gtk_widget_get_has_window(self->event_box)) {
455  event_box_allocation.x += allocation->x;
456  event_box_allocation.y += allocation->y;
457  }
458  gtk_widget_size_allocate(self->event_box, &event_box_allocation);
459 
461 }
462 
463 struct _ReorderData {
464  GdkWindow* parent_window;
465  GdkWindow* last_window;
466 };
467 
468 static void fl_view_reorder_forall(GtkWidget* widget, gpointer user_data) {
469  _ReorderData* data = reinterpret_cast<_ReorderData*>(user_data);
470  GdkWindow* window = gtk_widget_get_window(widget);
471  if (window && window != data->parent_window) {
472  if (data->last_window) {
473  gdk_window_restack(window, data->last_window, TRUE);
474  }
475  data->last_window = window;
476  }
477 }
478 
479 static gboolean event_box_button_press_event(GtkWidget* widget,
480  GdkEventButton* event,
481  FlView* view) {
482  // Flutter doesn't handle double and triple click events.
483  if (event->type == GDK_DOUBLE_BUTTON_PRESS ||
484  event->type == GDK_TRIPLE_BUTTON_PRESS) {
485  return FALSE;
486  }
487 
488  if (!gtk_widget_has_focus(GTK_WIDGET(view))) {
489  gtk_widget_grab_focus(GTK_WIDGET(view));
490  }
491 
492  return fl_view_send_pointer_button_event(view, event);
493 }
494 
495 static gboolean event_box_button_release_event(GtkWidget* widget,
496  GdkEventButton* event,
497  FlView* view) {
498  return fl_view_send_pointer_button_event(view, event);
499 }
500 
501 static gboolean event_box_scroll_event(GtkWidget* widget,
502  GdkEventScroll* event,
503  FlView* view) {
504  // TODO(robert-ancell): Update to use GtkEventControllerScroll when we can
505  // depend on GTK 3.24.
506 
507  gdouble scroll_delta_x = 0.0, scroll_delta_y = 0.0;
508  if (event->direction == GDK_SCROLL_SMOOTH) {
509  scroll_delta_x = event->delta_x;
510  scroll_delta_y = event->delta_y;
511  } else if (event->direction == GDK_SCROLL_UP) {
512  scroll_delta_y = -1;
513  } else if (event->direction == GDK_SCROLL_DOWN) {
514  scroll_delta_y = 1;
515  } else if (event->direction == GDK_SCROLL_LEFT) {
516  scroll_delta_x = -1;
517  } else if (event->direction == GDK_SCROLL_RIGHT) {
518  scroll_delta_x = 1;
519  }
520 
521  // The multiplier is taken from the Chromium source
522  // (ui/events/x/events_x_utils.cc).
523  const int kScrollOffsetMultiplier = 53;
524  scroll_delta_x *= kScrollOffsetMultiplier;
525  scroll_delta_y *= kScrollOffsetMultiplier;
526 
527  gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(view));
529  view->engine, view->button_state != 0 ? kMove : kHover,
530  event->time * kMicrosecondsPerMillisecond, event->x * scale_factor,
531  event->y * scale_factor, scroll_delta_x, scroll_delta_y,
532  view->button_state);
533 
534  return TRUE;
535 }
536 
537 static void check_pointer_inside(FlView* view, GdkEvent* event) {
538  if (!view->pointer_inside) {
539  view->pointer_inside = TRUE;
540 
541  gdouble x, y;
542  if (gdk_event_get_coords(event, &x, &y)) {
543  gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(view));
544 
546  view->engine, kAdd,
547  gdk_event_get_time(event) * kMicrosecondsPerMillisecond,
548  x * scale_factor, y * scale_factor, 0, 0, view->button_state);
549  }
550  }
551 }
552 
553 static gboolean event_box_motion_notify_event(GtkWidget* widget,
554  GdkEventMotion* event,
555  FlView* view) {
556  if (view->engine == nullptr) {
557  return FALSE;
558  }
559 
560  check_pointer_inside(view, reinterpret_cast<GdkEvent*>(event));
561 
562  gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(view));
564  view->engine, view->button_state != 0 ? kMove : kHover,
565  event->time * kMicrosecondsPerMillisecond, event->x * scale_factor,
566  event->y * scale_factor, 0, 0, view->button_state);
567 
568  return TRUE;
569 }
570 
571 static gboolean event_box_enter_notify_event(GtkWidget* widget,
572  GdkEventCrossing* event,
573  FlView* view) {
574  if (view->engine == nullptr) {
575  return FALSE;
576  }
577 
578  check_pointer_inside(view, reinterpret_cast<GdkEvent*>(event));
579 
580  return TRUE;
581 }
582 
583 static gboolean event_box_leave_notify_event(GtkWidget* widget,
584  GdkEventCrossing* event,
585  FlView* view) {
586  if (view->engine == nullptr) {
587  return FALSE;
588  }
589 
590  // Don't remove pointer while button is down; In case of dragging outside of
591  // window with mouse grab active Gtk will send another leave notify on
592  // release.
593  if (view->pointer_inside && view->button_state == 0) {
594  gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(view));
596  view->engine, kRemove, event->time * kMicrosecondsPerMillisecond,
597  event->x * scale_factor, event->y * scale_factor, 0, 0,
598  view->button_state);
599  view->pointer_inside = FALSE;
600  }
601 
602  return TRUE;
603 }
604 
605 // Implements GtkWidget::key_press_event.
606 static gboolean fl_view_key_press_event(GtkWidget* widget, GdkEventKey* event) {
607  FlView* self = FL_VIEW(widget);
608 
610  self->keyboard_manager, fl_key_event_new_from_gdk_event(gdk_event_copy(
611  reinterpret_cast<GdkEvent*>(event))));
612 }
613 
614 // Implements GtkWidget::key_release_event.
615 static gboolean fl_view_key_release_event(GtkWidget* widget,
616  GdkEventKey* event) {
617  FlView* self = FL_VIEW(widget);
619  self->keyboard_manager, fl_key_event_new_from_gdk_event(gdk_event_copy(
620  reinterpret_cast<GdkEvent*>(event))));
621 }
622 
623 static void fl_view_put(FlView* self,
624  GtkWidget* widget,
625  GdkRectangle* geometry) {
626  FlViewChild* child = g_new(FlViewChild, 1);
627  child->widget = widget;
628  child->geometry = *geometry;
629 
630  gtk_widget_set_parent(widget, GTK_WIDGET(self));
631  self->children_list = g_list_append(self->children_list, child);
632 }
633 
634 static void fl_view_add(GtkContainer* container, GtkWidget* widget) {
635  GdkRectangle geometry = {
636  .x = 0,
637  .y = 0,
638  .width = 0,
639  .height = 0,
640  };
641  fl_view_put(FL_VIEW(container), widget, &geometry);
642 }
643 
644 static void fl_view_remove(GtkContainer* container, GtkWidget* widget) {
645  FlView* self = FL_VIEW(container);
646  for (GList* iterator = self->children_list; iterator;
647  iterator = iterator->next) {
648  FlViewChild* child = reinterpret_cast<FlViewChild*>(iterator->data);
649  if (child->widget == widget) {
650  g_object_ref(widget);
651  gtk_widget_unparent(widget);
652  self->children_list = g_list_remove_link(self->children_list, iterator);
653  g_list_free(iterator);
654  g_free(child);
655 
656  break;
657  }
658  }
659 
660  if (widget == GTK_WIDGET(self->event_box)) {
661  g_clear_object(&self->event_box);
662  }
663 }
664 
665 static void fl_view_forall(GtkContainer* container,
666  gboolean include_internals,
667  GtkCallback callback,
668  gpointer callback_data) {
669  FlView* self = FL_VIEW(container);
670  for (GList* iterator = self->children_list; iterator;
671  iterator = iterator->next) {
672  FlViewChild* child = reinterpret_cast<FlViewChild*>(iterator->data);
673  (*callback)(child->widget, callback_data);
674  }
675 
676  if (include_internals) {
677  (*callback)(self->event_box, callback_data);
678  }
679 }
680 
681 static GType fl_view_child_type(GtkContainer* container) {
682  return GTK_TYPE_WIDGET;
683 }
684 
685 static void fl_view_set_child_property(GtkContainer* container,
686  GtkWidget* child,
687  guint property_id,
688  const GValue* value,
689  GParamSpec* pspec) {}
690 
691 static void fl_view_get_child_property(GtkContainer* container,
692  GtkWidget* child,
693  guint property_id,
694  GValue* value,
695  GParamSpec* pspec) {}
696 
697 static void fl_view_class_init(FlViewClass* klass) {
698  GObjectClass* object_class = G_OBJECT_CLASS(klass);
699  object_class->constructed = fl_view_constructed;
700  object_class->set_property = fl_view_set_property;
701  object_class->get_property = fl_view_get_property;
702  object_class->notify = fl_view_notify;
703  object_class->dispose = fl_view_dispose;
704 
705  GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(klass);
706  widget_class->realize = fl_view_realize;
707  widget_class->get_preferred_width = fl_view_get_preferred_width;
708  widget_class->get_preferred_height = fl_view_get_preferred_height;
709  widget_class->size_allocate = fl_view_size_allocate;
710  widget_class->key_press_event = fl_view_key_press_event;
711  widget_class->key_release_event = fl_view_key_release_event;
712 
713  GtkContainerClass* container_class = GTK_CONTAINER_CLASS(klass);
714  container_class->add = fl_view_add;
715  container_class->remove = fl_view_remove;
716  container_class->forall = fl_view_forall;
717  container_class->child_type = fl_view_child_type;
718  container_class->set_child_property = fl_view_set_child_property;
719  container_class->get_child_property = fl_view_get_child_property;
720 
721  g_object_class_install_property(
722  G_OBJECT_CLASS(klass), PROP_FLUTTER_PROJECT,
723  g_param_spec_object(
724  "flutter-project", "flutter-project", "Flutter project in use",
725  fl_dart_project_get_type(),
726  static_cast<GParamFlags>(G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
727  G_PARAM_STATIC_STRINGS)));
728 
729  gtk_widget_class_set_accessible_type(GTK_WIDGET_CLASS(klass),
730  fl_view_accessible_get_type());
731 }
732 
733 static void fl_view_init(FlView* self) {
734  gtk_widget_set_can_focus(GTK_WIDGET(self), TRUE);
735 }
736 
737 G_MODULE_EXPORT FlView* fl_view_new(FlDartProject* project) {
738  return static_cast<FlView*>(
739  g_object_new(fl_view_get_type(), "flutter-project", project, nullptr));
740 }
741 
742 G_MODULE_EXPORT FlEngine* fl_view_get_engine(FlView* view) {
743  g_return_val_if_fail(FL_IS_VIEW(view), nullptr);
744  return view->engine;
745 }
746 
747 void fl_view_begin_frame(FlView* view) {
748  g_return_if_fail(FL_IS_VIEW(view));
749  FlView* self = FL_VIEW(view);
750 
751  self->used_area_list = self->gl_area_list;
752  g_list_free_full(self->pending_children_list, g_free);
753  self->pending_children_list = nullptr;
754 }
755 
756 static void fl_view_add_pending_child(FlView* self,
757  GtkWidget* widget,
758  GdkRectangle* geometry) {
759  FlViewChild* child = g_new(FlViewChild, 1);
760  child->widget = widget;
761  if (geometry)
762  child->geometry = *geometry;
763  else
764  child->geometry = {0, 0, 0, 0};
765 
766  self->pending_children_list =
767  g_list_append(self->pending_children_list, child);
768 }
769 
771  GdkGLContext* context,
772  FlBackingStoreProvider* texture) {
773  g_return_if_fail(FL_IS_VIEW(view));
774 
775  FlGLArea* area;
776  if (view->used_area_list) {
777  area = reinterpret_cast<FlGLArea*>(view->used_area_list->data);
778  view->used_area_list = view->used_area_list->next;
779  } else {
780  area = FL_GL_AREA(fl_gl_area_new(context));
781  view->gl_area_list = g_list_append(view->gl_area_list, area);
782  }
783 
784  gtk_widget_show(GTK_WIDGET(area));
785  fl_view_add_pending_child(view, GTK_WIDGET(area), nullptr);
786  fl_gl_area_queue_render(area, texture);
787 }
788 
790  GtkWidget* widget,
791  GdkRectangle* geometry) {
792  gtk_widget_show(widget);
793  fl_view_add_pending_child(view, widget, geometry);
794 }
795 
796 GList* find_child(GList* list, GtkWidget* widget) {
797  for (GList* i = list; i; i = i->next) {
798  FlViewChild* child = reinterpret_cast<FlViewChild*>(i->data);
799  if (child && child->widget == widget)
800  return i;
801  }
802  return nullptr;
803 }
804 
805 void fl_view_end_frame(FlView* view) {
806  for (GList* pending_child = view->pending_children_list; pending_child;
807  pending_child = pending_child->next) {
808  FlViewChild* pending_view_child =
809  reinterpret_cast<FlViewChild*>(pending_child->data);
810  GList* child = find_child(view->children_list, pending_view_child->widget);
811 
812  if (child) {
813  // existing child
814  g_free(child->data);
815  child->data = nullptr;
816  } else {
817  // newly added child
818  gtk_widget_set_parent(pending_view_child->widget, GTK_WIDGET(view));
819  }
820  }
821 
822  for (GList* child = view->children_list; child; child = child->next) {
823  FlViewChild* view_child = reinterpret_cast<FlViewChild*>(child->data);
824  if (view_child) {
825  // removed child
826  g_object_ref(view_child->widget);
827  gtk_widget_unparent(view_child->widget);
828  g_free(view_child);
829  child->data = nullptr;
830  }
831  }
832 
833  g_list_free(view->children_list);
834  view->children_list = view->pending_children_list;
835  view->pending_children_list = nullptr;
836 
837  struct _ReorderData data = {
838  .parent_window = gtk_widget_get_window(GTK_WIDGET(view)),
839  .last_window = nullptr,
840  };
841 
842  gtk_container_forall(GTK_CONTAINER(view), fl_view_reorder_forall, &data);
843 
844  gtk_widget_queue_draw(GTK_WIDGET(view));
845 }
846 
847 static void redispatch_key_event_by_gtk(gpointer raw_event) {
848  GdkEvent* gdk_event = reinterpret_cast<GdkEvent*>(raw_event);
849  GdkEventType type = gdk_event->type;
850  g_return_if_fail(type == GDK_KEY_PRESS || type == GDK_KEY_RELEASE);
851  gdk_event_put(gdk_event);
852 }
853 
854 static gboolean text_input_im_filter_by_gtk(GtkIMContext* im_context,
855  gpointer gdk_event) {
856  GdkEventKey* event = reinterpret_cast<GdkEventKey*>(gdk_event);
857  GdkEventType type = event->type;
858  g_return_val_if_fail(type == GDK_KEY_PRESS || type == GDK_KEY_RELEASE, false);
859  return gtk_im_context_filter_keypress(im_context, event);
860 }
static void fl_view_get_child_property(GtkContainer *container, GtkWidget *child, guint property_id, GValue *value, GParamSpec *pspec)
Definition: fl_view.cc:691
Definition: embedder.h:595
KeyCallType type
static void fl_view_dispose(GObject *object)
Definition: fl_view.cc:305
static void fl_view_reorder_forall(GtkWidget *widget, gpointer user_data)
Definition: fl_view.cc:468
GdkWindow * parent_window
Definition: fl_view.cc:464
G_BEGIN_DECLS FlTexture * texture
static void fl_view_constructed(GObject *object)
Definition: fl_view.cc:221
static void fl_view_on_pre_engine_restart_cb(FlEngine *engine, gpointer user_data)
Definition: fl_view.cc:110
const uint8_t uint32_t uint32_t GError ** error
FlPluginRegistrar * fl_plugin_registrar_new(FlView *view, FlBinaryMessenger *messenger, FlTextureRegistrar *texture_registrar)
gboolean pointer_inside
Definition: fl_view.cc:57
static gboolean event_box_leave_notify_event(GtkWidget *widget, GdkEventCrossing *event, FlView *view)
Definition: fl_view.cc:583
static void fl_view_geometry_changed(FlView *self)
Definition: fl_view.cc:169
static void fl_view_init(FlView *self)
Definition: fl_view.cc:733
static void fl_view_put(FlView *self, GtkWidget *widget, GdkRectangle *geometry)
Definition: fl_view.cc:623
FlutterPointerPhase
The phase of the pointer event.
Definition: embedder.h:587
FlAccessibilityPlugin * accessibility_plugin
Definition: fl_view.cc:43
FlMouseCursorPlugin * fl_mouse_cursor_plugin_new(FlBinaryMessenger *messenger, FlView *view)
static gboolean text_input_im_filter_by_gtk(GtkIMContext *im_context, gpointer gdk_event)
Definition: fl_view.cc:854
static gboolean event_box_motion_notify_event(GtkWidget *widget, GdkEventMotion *event, FlView *view)
Definition: fl_view.cc:553
typedefG_BEGIN_DECLS struct _FlKeyboardManager FlKeyboardManager
GtkContainer parent_instance
Definition: fl_view.cc:28
GList * used_area_list
Definition: fl_view.cc:49
G_MODULE_EXPORT FlView * fl_view_new(FlDartProject *project)
Definition: fl_view.cc:737
G_DEFINE_TYPE_WITH_CODE(FlView, fl_view, GTK_TYPE_CONTAINER, G_IMPLEMENT_INTERFACE(fl_plugin_registry_get_type(), fl_view_plugin_registry_iface_init)) static void fl_view_update_semantics_node_cb(FlEngine *engine
gboolean fl_keyboard_manager_handle_event(FlKeyboardManager *self, FlKeyEvent *event)
static void fl_view_notify(GObject *object, GParamSpec *pspec)
Definition: fl_view.cc:293
GList * children_list
Definition: fl_view.cc:53
GtkWidget * fl_gl_area_new(GdkGLContext *context)
Definition: fl_gl_area.cc:117
static void fl_view_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
Definition: fl_view.cc:260
FlRendererGL * fl_renderer_gl_new()
static void fl_view_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
Definition: fl_view.cc:277
G_MODULE_EXPORT FlEngine * fl_view_get_engine(FlView *view)
Definition: fl_view.cc:742
static gboolean event_box_scroll_event(GtkWidget *widget, GdkEventScroll *event, FlView *view)
Definition: fl_view.cc:501
FlKeyEvent FlKeyResponderAsyncCallback callback
static void fl_view_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
Definition: fl_view.cc:412
GtkWidget * event_box
Definition: fl_view.cc:51
FlKeyEvent * event
static constexpr int kMicrosecondsPerMillisecond
Definition: fl_view.cc:25
void fl_keyboard_manager_add_responder(FlKeyboardManager *self, FlKeyResponder *responder)
static void fl_view_plugin_registry_iface_init(FlPluginRegistryInterface *iface)
Definition: fl_view.cc:192
FlEngine * engine
Definition: fl_view.cc:37
void fl_view_add_widget(FlView *view, GtkWidget *widget, GdkRectangle *geometry)
Definition: fl_view.cc:789
void fl_gl_area_queue_render(FlGLArea *self, FlBackingStoreProvider *texture)
Definition: fl_gl_area.cc:125
FlKeyboardManager * fl_keyboard_manager_new(FlTextInputPlugin *text_input_plugin, FlKeyboardManagerRedispatcher redispatch_callback)
static gboolean event_box_button_release_event(GtkWidget *widget, GdkEventButton *event, FlView *view)
Definition: fl_view.cc:495
uint8_t value
void fl_engine_set_on_pre_engine_restart_handler(FlEngine *self, FlEngineOnPreEngineRestartHandler handler, gpointer user_data, GDestroyNotify destroy_notify)
Definition: fl_engine.cc:549
const FlutterSemanticsNode * node
Definition: fl_view.cc:83
gboolean fl_engine_start(FlEngine *self, GError **error)
Definition: fl_engine.cc:403
int64_t button_state
Definition: fl_view.cc:40
static void fl_view_forall(GtkContainer *container, gboolean include_internals, GtkCallback callback, gpointer callback_data)
Definition: fl_view.cc:665
static void fl_view_realize(GtkWidget *widget)
Definition: fl_view.cc:329
fl_accessibility_plugin_handle_update_semantics_node(self->accessibility_plugin, node)
static void fl_view_get_preferred_height(GtkWidget *widget, gint *minimum, gint *natural)
Definition: fl_view.cc:388
GList * find_child(GList *list, GtkWidget *widget)
Definition: fl_view.cc:796
gboolean fl_renderer_start(FlRenderer *self, FlView *view, GError **error)
Definition: fl_renderer.cc:59
The pointer moved while up.
Definition: embedder.h:619
GdkWindow * last_window
Definition: fl_view.cc:465
static void fl_view_init_keyboard(FlView *self)
Definition: fl_view.cc:91
GtkWidget * widget
Definition: fl_view.cc:61
void fl_engine_send_window_metrics_event(FlEngine *self, size_t width, size_t height, double pixel_ratio)
Definition: fl_engine.cc:666
GdkRectangle geometry
Definition: fl_view.cc:62
void fl_view_add_gl_area(FlView *view, GdkGLContext *context, FlBackingStoreProvider *texture)
Definition: fl_view.cc:770
const FlutterSemanticsNode gpointer user_data
Definition: fl_view.cc:84
static gboolean fl_view_key_release_event(GtkWidget *widget, GdkEventKey *event)
Definition: fl_view.cc:615
static void fl_view_class_init(FlViewClass *klass)
Definition: fl_view.cc:697
void fl_engine_send_mouse_pointer_event(FlEngine *self, FlutterPointerPhase phase, size_t timestamp, double x, double y, double scroll_delta_x, double scroll_delta_y, int64_t buttons)
Definition: fl_engine.cc:684
static gboolean fl_view_send_pointer_button_event(FlView *self, GdkEventButton *event)
Definition: fl_view.cc:119
Definition: embedder.h:612
const char * name
Definition: fuchsia.cc:50
FlEngine * fl_engine_new(FlDartProject *project, FlRenderer *renderer)
Definition: fl_engine.cc:388
FlView * view
FlKeyChannelResponder * fl_key_channel_responder_new(FlBinaryMessenger *messenger, FlKeyChannelResponderMock *mock)
static void redispatch_key_event_by_gtk(gpointer gdk_event)
Definition: fl_view.cc:847
static GType fl_view_child_type(GtkContainer *container)
Definition: fl_view.cc:681
FlMouseCursorPlugin * mouse_cursor_plugin
Definition: fl_view.cc:45
void fl_view_end_frame(FlView *view)
Definition: fl_view.cc:805
FlPlatformPlugin * fl_platform_plugin_new(FlBinaryMessenger *messenger)
GList * gl_area_list
Definition: fl_view.cc:48
G_MODULE_EXPORT FlBinaryMessenger * fl_engine_get_binary_messenger(FlEngine *self)
Definition: fl_engine.cc:769
FlKeyEvent * fl_key_event_new_from_gdk_event(GdkEvent *raw_event)
Definition: fl_key_event.cc:22
FlTextInputPlugin * fl_text_input_plugin_new(FlBinaryMessenger *messenger, FlView *view, FlTextInputPluginImFilter im_filter)
struct _FlViewChild FlViewChild
FlDartProject * project
Definition: fl_view.cc:31
void fl_engine_set_update_semantics_node_handler(FlEngine *self, FlEngineUpdateSemanticsNodeHandler handler, gpointer user_data, GDestroyNotify destroy_notify)
Definition: fl_engine.cc:532
GList * pending_children_list
Definition: fl_view.cc:54
static void check_pointer_inside(FlView *view, GdkEvent *event)
Definition: fl_view.cc:537
static gboolean event_box_button_press_event(GtkWidget *widget, GdkEventButton *event, FlView *view)
Definition: fl_view.cc:479
FlAccessibilityPlugin * fl_accessibility_plugin_new(FlView *view)
static gboolean event_box_enter_notify_event(GtkWidget *widget, GdkEventCrossing *event, FlView *view)
Definition: fl_view.cc:571
static void fl_view_set_child_property(GtkContainer *container, GtkWidget *child, guint property_id, const GValue *value, GParamSpec *pspec)
Definition: fl_view.cc:685
static void fl_view_add(GtkContainer *container, GtkWidget *widget)
Definition: fl_view.cc:634
void fl_renderer_wait_for_frame(FlRenderer *self, int target_width, int target_height)
Definition: fl_renderer.cc:143
return FALSE
G_MODULE_EXPORT FlTextureRegistrar * fl_engine_get_texture_registrar(FlEngine *self)
Definition: fl_engine.cc:785
static void fl_view_add_pending_child(FlView *self, GtkWidget *widget, GdkRectangle *geometry)
Definition: fl_view.cc:756
static FlPluginRegistrar * fl_view_get_registrar_for_plugin(FlPluginRegistry *registry, const gchar *name)
Definition: fl_view.cc:182
void fl_view_begin_frame(FlView *view)
Definition: fl_view.cc:747
FlRenderer * renderer
Definition: fl_view.cc:34
static void fl_view_remove(GtkContainer *container, GtkWidget *widget)
Definition: fl_view.cc:644
FlKeyboardManager * keyboard_manager
Definition: fl_view.cc:44
G_MODULE_EXPORT GType fl_plugin_registry_get_type()
static void fl_view_get_preferred_width(GtkWidget *widget, gint *minimum, gint *natural)
Definition: fl_view.cc:365
FlKeyEmbedderResponder * fl_key_embedder_responder_new(FlEngine *engine)
static gboolean fl_view_key_press_event(GtkWidget *widget, GdkEventKey *event)
Definition: fl_view.cc:606
FlPlatformPlugin * platform_plugin
Definition: fl_view.cc:46