8 #include <gtk/gtk-a11y.h>
12 #include "flutter/common/constants.h"
82 FlPluginRegistryInterface* iface);
89 G_IMPLEMENT_INTERFACE(fl_plugin_registry_get_type(),
93 static gboolean first_frame_idle_cb(gpointer
user_data) {
109 g_clear_object(&self->scrolling_manager);
110 self->scrolling_manager =
115 g_clear_object(&self->touch_manager);
120 GdkDevice* device = gdk_event_get_source_device(event);
121 GdkInputSource source = gdk_device_get_source(device);
124 case GDK_SOURCE_ERASER:
125 case GDK_SOURCE_CURSOR:
126 case GDK_SOURCE_TABLET_PAD:
127 return kFlutterPointerDeviceKindStylus;
128 case GDK_SOURCE_TOUCHSCREEN:
129 return kFlutterPointerDeviceKindTouch;
130 case GDK_SOURCE_TOUCHPAD:
131 case GDK_SOURCE_TRACKPOINT:
132 case GDK_SOURCE_KEYBOARD:
133 case GDK_SOURCE_MOUSE:
134 return kFlutterPointerDeviceKindMouse;
140 FlMouseCursorHandler* handler =
144 gtk_widget_get_window(gtk_widget_get_toplevel(GTK_WIDGET(
self)));
145 g_autoptr(GdkCursor) cursor =
146 gdk_cursor_new_from_name(gdk_window_get_display(
window), cursor_name);
147 gdk_window_set_cursor(
window, cursor);
152 FlMouseCursorHandler* handler =
155 self->cursor_changed_cb_id = g_signal_connect_swapped(
162 GtkAllocation allocation;
163 gtk_widget_get_allocation(GTK_WIDGET(
self), &allocation);
164 gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(
self));
172 gtk_widget_get_window(gtk_widget_get_toplevel(GTK_WIDGET(
self)));
180 FlutterEngineDisplayId display_id = 0;
182 GdkMonitor* monitor = gdk_display_get_monitor_at_window(
183 gtk_widget_get_display(GTK_WIDGET(
self)),
window);
188 self->engine, display_id, self->view_id, allocation.width * scale_factor,
189 allocation.height * scale_factor, scale_factor);
197 if (allocation.width > 1 && allocation.height > 1 &&
198 gtk_widget_get_realized(GTK_WIDGET(
self))) {
200 allocation.width * scale_factor,
201 allocation.height * scale_factor);
206 GAsyncResult* result,
210 g_autoptr(GError)
error =
nullptr;
212 if (g_error_matches(
error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
216 g_warning(
"Failed to add view: %s",
error->message);
226 const FlutterSemanticsUpdate2* update) {
242 FlView*
self = FL_VIEW(renderable);
244 gtk_widget_queue_draw(GTK_WIDGET(self->gl_area));
246 if (!self->have_first_frame) {
247 self->have_first_frame =
TRUE;
250 g_idle_add(first_frame_idle_cb,
self);
256 FlView*
self = FL_VIEW(renderable);
257 gtk_gl_area_make_current(self->gl_area);
262 FlPluginRegistry* registry,
264 FlView*
self = FL_VIEW(registry);
277 FlPluginRegistryInterface* iface) {
282 guint event_time = gdk_event_get_time(event);
283 GdkModifierType event_state =
static_cast<GdkModifierType
>(0);
284 gdk_event_get_state(event, &event_state);
290 gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(
self));
292 self->scrolling_manager, x * scale_factor, y * scale_factor);
297 GdkEventButton* button_event) {
298 GdkEvent*
event =
reinterpret_cast<GdkEvent*
>(button_event);
301 GdkEventType event_type = gdk_event_get_event_type(event);
302 if (event_type == GDK_DOUBLE_BUTTON_PRESS ||
303 event_type == GDK_TRIPLE_BUTTON_PRESS) {
308 gdk_event_get_button(event, &button);
310 gdouble x = 0.0, y = 0.0;
311 gdk_event_get_coords(event, &x, &y);
316 gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(
self));
318 self->pointer_manager, gdk_event_get_time(event),
get_device_kind(event),
319 x * scale_factor, y * scale_factor, button);
324 GdkEventButton* button_event) {
325 GdkEvent*
event =
reinterpret_cast<GdkEvent*
>(button_event);
328 gdk_event_get_button(event, &button);
330 gdouble x = 0.0, y = 0.0;
331 gdk_event_get_coords(event, &x, &y);
336 gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(
self));
338 self->pointer_manager, gdk_event_get_time(event),
get_device_kind(event),
339 x * scale_factor, y * scale_factor, button);
348 self->scrolling_manager, event,
349 gtk_widget_get_scale_factor(GTK_WIDGET(
self)));
355 self->touch_manager, event,
356 gtk_widget_get_scale_factor(GTK_WIDGET(
self)));
362 GdkEventMotion* motion_event) {
363 GdkEvent*
event =
reinterpret_cast<GdkEvent*
>(motion_event);
367 auto event_type = gdk_event_get_event_type(event);
368 if (event_type == GDK_TOUCH_BEGIN || event_type == GDK_TOUCH_UPDATE ||
369 event_type == GDK_TOUCH_END || event_type == GDK_TOUCH_CANCEL) {
373 gdouble x = 0.0, y = 0.0;
374 gdk_event_get_coords(event, &x, &y);
375 gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(
self));
377 self->pointer_manager, gdk_event_get_time(event),
get_device_kind(event),
378 x * scale_factor, y * scale_factor);
383 GdkEventCrossing* crossing_event) {
384 GdkEvent*
event =
reinterpret_cast<GdkEvent*
>(crossing_event);
385 gdouble x = 0.0, y = 0.0;
386 gdk_event_get_coords(event, &x, &y);
387 gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(
self));
389 self->pointer_manager, gdk_event_get_time(event),
get_device_kind(event),
390 x * scale_factor, y * scale_factor);
395 GdkEventCrossing* crossing_event) {
396 if (crossing_event->mode != GDK_CROSSING_NORMAL) {
400 GdkEvent*
event =
reinterpret_cast<GdkEvent*
>(crossing_event);
401 gdouble x = 0.0, y = 0.0;
402 gdk_event_get_coords(event, &x, &y);
403 gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(
self));
405 self->pointer_manager, gdk_event_get_time(event),
get_device_kind(event),
406 x * scale_factor, y * scale_factor);
438 gtk_widget_get_parent_window(GTK_WIDGET(
self)));
443 g_autoptr(GError)
error =
nullptr;
445 gtk_gl_area_set_error(self->gl_area,
error);
449 return GDK_GL_CONTEXT(
454 g_autoptr(GError)
error =
nullptr;
458 GError* gl_error = gtk_gl_area_get_error(self->gl_area);
459 if (gl_error != NULL) {
460 g_warning(
"Failed to initialize GLArea: %s", gl_error->message);
466 GtkWidget* toplevel_window = gtk_widget_get_toplevel(GTK_WIDGET(
self));
468 self->window_state_monitor =
470 GTK_WINDOW(toplevel_window));
473 g_signal_connect_swapped(toplevel_window,
"delete-event",
477 FL_RENDERABLE(
self));
480 g_warning(
"Failed to start Flutter engine: %s",
error->message);
490 FL_SOCKET_ACCESSIBLE(gtk_widget_get_accessible(GTK_WIDGET(
self))),
491 atk_plug_get_id(ATK_PLUG(self->view_accessible)));
498 static gboolean
render_cb(FlView*
self, GdkGLContext* context) {
499 if (gtk_gl_area_get_error(self->gl_area) != NULL) {
503 int width = gtk_widget_get_allocated_width(GTK_WIDGET(self->gl_area));
504 int height = gtk_widget_get_allocated_height(GTK_WIDGET(self->gl_area));
505 gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(self->gl_area));
508 self->background_color);
514 g_autoptr(GError)
error =
nullptr;
518 GError* gl_error = gtk_gl_area_get_error(self->gl_area);
519 if (gl_error != NULL) {
520 g_warning(
"Failed to uninitialize GLArea: %s", gl_error->message);
532 FlView*
self = FL_VIEW(
object);
534 if (strcmp(
pspec->name,
"scale-factor") == 0) {
538 if (G_OBJECT_CLASS(fl_view_parent_class)->notify !=
nullptr) {
539 G_OBJECT_CLASS(fl_view_parent_class)->notify(
object,
pspec);
544 FlView*
self = FL_VIEW(
object);
546 g_cancellable_cancel(self->cancellable);
548 if (self->engine !=
nullptr) {
549 FlMouseCursorHandler* handler =
551 if (self->cursor_changed_cb_id != 0) {
552 g_signal_handler_disconnect(handler, self->cursor_changed_cb_id);
553 self->cursor_changed_cb_id = 0;
564 if (self->on_pre_engine_restart_cb_id != 0) {
565 g_signal_handler_disconnect(self->engine,
566 self->on_pre_engine_restart_cb_id);
567 self->on_pre_engine_restart_cb_id = 0;
570 if (self->update_semantics_cb_id != 0) {
571 g_signal_handler_disconnect(self->engine, self->update_semantics_cb_id);
572 self->update_semantics_cb_id = 0;
575 g_clear_object(&self->engine);
576 g_clear_object(&self->renderer);
577 g_clear_pointer(&self->background_color, gdk_rgba_free);
578 g_clear_object(&self->window_state_monitor);
579 g_clear_object(&self->scrolling_manager);
580 g_clear_object(&self->pointer_manager);
581 g_clear_object(&self->touch_manager);
582 g_clear_object(&self->view_accessible);
583 g_clear_object(&self->cancellable);
585 G_OBJECT_CLASS(fl_view_parent_class)->dispose(
object);
590 FlView*
self = FL_VIEW(widget);
592 GTK_WIDGET_CLASS(fl_view_parent_class)->realize(widget);
595 gtk_widget_realize(GTK_WIDGET(self->gl_area));
600 gdk_event_copy(
reinterpret_cast<GdkEvent*
>(key_event)));
604 [](GObject*
object, GAsyncResult* result, gpointer
user_data) {
605 FlView* self = FL_VIEW(user_data);
607 g_autoptr(FlKeyEvent) redispatch_event = nullptr;
608 g_autoptr(GError) error = nullptr;
609 if (!fl_keyboard_manager_handle_event_finish(
610 FL_KEYBOARD_MANAGER(object), result, &redispatch_event,
612 if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
616 g_warning(
"Failed to handle key event: %s", error->message);
619 if (redispatch_event !=
nullptr) {
623 fl_keyboard_manager_add_redispatched_event(
624 fl_engine_get_keyboard_manager(self->engine), redispatch_event);
625 gdk_event_put(fl_key_event_get_origin(redispatch_event));
636 GdkEventFocus* event) {
637 FlView*
self = FL_VIEW(widget);
645 GdkEventKey* key_event) {
646 FlView*
self = FL_VIEW(widget);
652 GdkEventKey* key_event) {
653 FlView*
self = FL_VIEW(widget);
658 GObjectClass* object_class = G_OBJECT_CLASS(klass);
662 GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(klass);
669 g_signal_new(
"first-frame", fl_view_get_type(), G_SIGNAL_RUN_LAST, 0,
670 NULL, NULL, NULL, G_TYPE_NONE, 0);
672 gtk_widget_class_set_accessible_type(GTK_WIDGET_CLASS(klass),
673 fl_socket_accessible_get_type());
677 self->cancellable = g_cancellable_new();
679 gtk_widget_set_can_focus(GTK_WIDGET(
self),
TRUE);
683 GdkRGBA default_background = {
684 .red = 0.0, .green = 0.0, .blue = 0.0, .alpha = 1.0};
685 self->background_color = gdk_rgba_copy(&default_background);
687 GtkWidget* event_box = gtk_event_box_new();
688 gtk_widget_set_hexpand(event_box,
TRUE);
689 gtk_widget_set_vexpand(event_box,
TRUE);
690 gtk_container_add(GTK_CONTAINER(
self), event_box);
691 gtk_widget_show(event_box);
692 gtk_widget_add_events(event_box,
693 GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK |
694 GDK_BUTTON_RELEASE_MASK | GDK_SCROLL_MASK |
695 GDK_SMOOTH_SCROLL_MASK | GDK_TOUCH_MASK);
697 g_signal_connect_swapped(event_box,
"button-press-event",
699 g_signal_connect_swapped(event_box,
"button-release-event",
701 g_signal_connect_swapped(event_box,
"scroll-event",
703 g_signal_connect_swapped(event_box,
"motion-notify-event",
705 g_signal_connect_swapped(event_box,
"enter-notify-event",
707 g_signal_connect_swapped(event_box,
"leave-notify-event",
709 GtkGesture* zoom = gtk_gesture_zoom_new(event_box);
712 g_signal_connect_swapped(zoom,
"scale-changed",
715 GtkGesture* rotate = gtk_gesture_rotate_new(event_box);
716 g_signal_connect_swapped(rotate,
"begin",
718 g_signal_connect_swapped(rotate,
"angle-changed",
722 g_signal_connect_swapped(event_box,
"touch-event", G_CALLBACK(
touch_event_cb),
725 self->gl_area = GTK_GL_AREA(gtk_gl_area_new());
726 gtk_gl_area_set_has_alpha(self->gl_area,
TRUE);
727 gtk_widget_show(GTK_WIDGET(self->gl_area));
728 gtk_container_add(GTK_CONTAINER(event_box), GTK_WIDGET(self->gl_area));
729 g_signal_connect_swapped(self->gl_area,
"render", G_CALLBACK(
render_cb),
732 g_signal_connect_swapped(
self,
"size-allocate", G_CALLBACK(
size_allocate_cb),
738 FlView*
self = FL_VIEW(g_object_new(fl_view_get_type(),
nullptr));
740 self->view_id = flutter::kFlutterImplicitViewId;
741 self->engine = FL_ENGINE(g_object_ref(engine));
743 g_assert(FL_IS_RENDERER_GDK(renderer));
744 self->renderer = FL_RENDERER_GDK(g_object_ref(renderer));
748 self->on_pre_engine_restart_cb_id =
749 g_signal_connect_swapped(self->engine,
"on-pre-engine-restart",
751 self->update_semantics_cb_id = g_signal_connect_swapped(
754 g_signal_connect_swapped(self->gl_area,
"create-context",
756 g_signal_connect_swapped(self->gl_area,
"realize", G_CALLBACK(
realize_cb),
758 g_signal_connect_swapped(self->gl_area,
"unrealize", G_CALLBACK(
unrealize_cb),
765 FlView*
self = FL_VIEW(g_object_new(fl_view_get_type(),
nullptr));
767 self->engine = FL_ENGINE(g_object_ref(engine));
769 g_assert(FL_IS_RENDERER_GDK(renderer));
770 self->renderer = FL_RENDERER_GDK(g_object_ref(renderer));
772 self->on_pre_engine_restart_cb_id =
773 g_signal_connect_swapped(engine,
"on-pre-engine-restart",
779 FL_RENDERABLE(
self));
783 g_signal_connect_swapped(self->gl_area,
"realize",
790 g_return_val_if_fail(FL_IS_VIEW(
self),
nullptr);
796 g_return_val_if_fail(FL_IS_VIEW(
self), -1);
797 return self->view_id;
801 const GdkRGBA* color) {
802 g_return_if_fail(FL_IS_VIEW(
self));
803 gdk_rgba_free(self->background_color);
804 self->background_color = gdk_rgba_copy(color);