8 #include <gtk/gtk-a11y.h>
12 #include "flutter/common/constants.h"
91 FlPluginRegistryInterface* iface);
94 FlKeyboardViewDelegateInterface* iface);
97 FlTextInputViewDelegateInterface* iface);
104 G_IMPLEMENT_INTERFACE(fl_plugin_registry_get_type(),
106 G_IMPLEMENT_INTERFACE(fl_keyboard_view_delegate_get_type(),
108 G_IMPLEMENT_INTERFACE(fl_text_input_view_delegate_get_type(),
112 static gboolean first_frame_idle_cb(gpointer
user_data) {
132 gtk_widget_get_window(gtk_widget_get_toplevel(GTK_WIDGET(
self)));
133 g_return_if_fail(GDK_IS_WINDOW(window));
134 g_autoptr(GtkIMContext) im_context = gtk_im_multicontext_new();
135 gtk_im_context_set_client_window(im_context, window);
137 g_clear_object(&self->text_input_handler);
139 messenger, im_context, FL_TEXT_INPUT_VIEW_DELEGATE(
self));
140 g_clear_object(&self->keyboard_manager);
141 self->keyboard_manager =
143 g_clear_object(&self->keyboard_handler);
144 self->keyboard_handler =
149 g_clear_object(&self->scrolling_manager);
150 self->scrolling_manager =
155 g_clear_object(&self->touch_manager);
160 GdkDevice* device = gdk_event_get_source_device(event);
161 GdkInputSource source = gdk_device_get_source(device);
164 case GDK_SOURCE_ERASER:
165 case GDK_SOURCE_CURSOR:
166 case GDK_SOURCE_TABLET_PAD:
167 return kFlutterPointerDeviceKindStylus;
168 case GDK_SOURCE_TOUCHSCREEN:
169 return kFlutterPointerDeviceKindTouch;
170 case GDK_SOURCE_TOUCHPAD:
171 case GDK_SOURCE_TRACKPOINT:
172 case GDK_SOURCE_KEYBOARD:
173 case GDK_SOURCE_MOUSE:
174 return kFlutterPointerDeviceKindMouse;
179 guint event_button = 0;
180 gdk_event_get_button(event, &event_button);
182 switch (event_button) {
184 *button = kFlutterPointerButtonMousePrimary;
187 *button = kFlutterPointerButtonMouseMiddle;
190 *button = kFlutterPointerButtonMouseSecondary;
199 FlMouseCursorHandler* handler =
203 gtk_widget_get_window(gtk_widget_get_toplevel(GTK_WIDGET(
self)));
204 g_autoptr(GdkCursor) cursor =
205 gdk_cursor_new_from_name(gdk_window_get_display(window), cursor_name);
206 gdk_window_set_cursor(window, cursor);
211 FlMouseCursorHandler* handler =
214 self->cursor_changed_cb_id = g_signal_connect_swapped(
221 GtkAllocation allocation;
222 gtk_widget_get_allocation(GTK_WIDGET(
self), &allocation);
223 gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(
self));
231 gtk_widget_get_window(gtk_widget_get_toplevel(GTK_WIDGET(
self)));
232 GdkMonitor* monitor = gdk_display_get_monitor_at_window(
233 gtk_widget_get_display(GTK_WIDGET(
self)), window);
238 self->view_id, allocation.width * scale_factor,
239 allocation.height * scale_factor, scale_factor);
247 if (allocation.width > 1 && allocation.height > 1 &&
248 gtk_widget_get_realized(GTK_WIDGET(
self))) {
250 allocation.width * scale_factor,
251 allocation.height * scale_factor);
256 GAsyncResult* result,
260 g_autoptr(GError)
error =
nullptr;
262 if (g_error_matches(
error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
266 g_warning(
"Failed to add view: %s",
error->message);
276 const FlutterSemanticsUpdate2* update,
296 FlView*
self = FL_VIEW(renderable);
298 gtk_widget_queue_draw(GTK_WIDGET(self->gl_area));
300 if (!self->have_first_frame) {
301 self->have_first_frame =
TRUE;
304 g_idle_add(first_frame_idle_cb,
self);
310 FlView*
self = FL_VIEW(renderable);
311 gtk_gl_area_make_current(self->gl_area);
316 FlPluginRegistry* registry,
318 FlView*
self = FL_VIEW(registry);
331 FlPluginRegistryInterface* iface) {
336 FlKeyboardViewDelegateInterface* iface) {
337 iface->text_filter_key_press = [](FlKeyboardViewDelegate* view_delegate,
339 FlView*
self = FL_VIEW(view_delegate);
346 FlTextInputViewDelegateInterface* iface) {
347 iface->translate_coordinates = [](FlTextInputViewDelegate* delegate,
348 gint view_x, gint view_y, gint* window_x,
350 FlView*
self = FL_VIEW(delegate);
351 gtk_widget_translate_coordinates(GTK_WIDGET(
self),
352 gtk_widget_get_toplevel(GTK_WIDGET(
self)),
353 view_x, view_y, window_x, window_y);
358 guint event_time = gdk_event_get_time(event);
359 GdkModifierType event_state =
static_cast<GdkModifierType
>(0);
360 gdk_event_get_state(event, &event_state);
362 event_state, event_time);
366 gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(
self));
368 self->scrolling_manager, x * scale_factor, y * scale_factor);
373 GdkEventButton* button_event) {
374 GdkEvent*
event =
reinterpret_cast<GdkEvent*
>(button_event);
377 GdkEventType event_type = gdk_event_get_event_type(event);
378 if (event_type == GDK_DOUBLE_BUTTON_PRESS ||
379 event_type == GDK_TRIPLE_BUTTON_PRESS) {
388 gdouble x = 0.0, y = 0.0;
389 gdk_event_get_coords(event, &x, &y);
394 gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(
self));
396 self->pointer_manager, gdk_event_get_time(event),
get_device_kind(event),
397 x * scale_factor, y * scale_factor, button);
402 GdkEventButton* button_event) {
403 GdkEvent*
event =
reinterpret_cast<GdkEvent*
>(button_event);
410 gdouble x = 0.0, y = 0.0;
411 gdk_event_get_coords(event, &x, &y);
416 gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(
self));
418 self->pointer_manager, gdk_event_get_time(event),
get_device_kind(event),
419 x * scale_factor, y * scale_factor, button);
428 self->scrolling_manager, event,
429 gtk_widget_get_scale_factor(GTK_WIDGET(
self)));
435 self->touch_manager, event,
436 gtk_widget_get_scale_factor(GTK_WIDGET(
self)));
442 GdkEventMotion* motion_event) {
443 GdkEvent*
event =
reinterpret_cast<GdkEvent*
>(motion_event);
447 auto event_type = gdk_event_get_event_type(event);
448 if (event_type == GDK_TOUCH_BEGIN || event_type == GDK_TOUCH_UPDATE ||
449 event_type == GDK_TOUCH_END || event_type == GDK_TOUCH_CANCEL) {
453 gdouble x = 0.0, y = 0.0;
454 gdk_event_get_coords(event, &x, &y);
455 gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(
self));
457 self->pointer_manager, gdk_event_get_time(event),
get_device_kind(event),
458 x * scale_factor, y * scale_factor);
463 GdkEventCrossing* crossing_event) {
464 GdkEvent*
event =
reinterpret_cast<GdkEvent*
>(crossing_event);
465 gdouble x = 0.0, y = 0.0;
466 gdk_event_get_coords(event, &x, &y);
467 gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(
self));
469 self->pointer_manager, gdk_event_get_time(event),
get_device_kind(event),
470 x * scale_factor, y * scale_factor);
475 GdkEventCrossing* crossing_event) {
476 if (crossing_event->mode != GDK_CROSSING_NORMAL) {
480 GdkEvent*
event =
reinterpret_cast<GdkEvent*
>(crossing_event);
481 gdouble x = 0.0, y = 0.0;
482 gdk_event_get_coords(event, &x, &y);
483 gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(
self));
485 self->pointer_manager, gdk_event_get_time(event),
get_device_kind(event),
486 x * scale_factor, y * scale_factor);
518 gtk_widget_get_parent_window(GTK_WIDGET(
self)));
523 g_autoptr(GError)
error =
nullptr;
525 gtk_gl_area_set_error(self->gl_area,
error);
529 return GDK_GL_CONTEXT(
534 g_autoptr(GError)
error =
nullptr;
538 GError* gl_error = gtk_gl_area_get_error(self->gl_area);
539 if (gl_error != NULL) {
540 g_warning(
"Failed to initialize GLArea: %s", gl_error->message);
546 GtkWidget* toplevel_window = gtk_widget_get_toplevel(GTK_WIDGET(
self));
548 self->window_state_monitor =
550 GTK_WINDOW(toplevel_window));
553 g_signal_connect_swapped(toplevel_window,
"delete-event",
559 FL_RENDERABLE(
self));
562 g_warning(
"Failed to start Flutter engine: %s",
error->message);
572 FL_SOCKET_ACCESSIBLE(gtk_widget_get_accessible(GTK_WIDGET(
self))),
573 atk_plug_get_id(ATK_PLUG(self->view_accessible)));
576 static gboolean
render_cb(FlView*
self, GdkGLContext* context) {
577 if (gtk_gl_area_get_error(self->gl_area) != NULL) {
581 int width = gtk_widget_get_allocated_width(GTK_WIDGET(self->gl_area));
582 int height = gtk_widget_get_allocated_height(GTK_WIDGET(self->gl_area));
583 gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(self->gl_area));
586 self->background_color);
592 g_autoptr(GError)
error =
nullptr;
596 GError* gl_error = gtk_gl_area_get_error(self->gl_area);
597 if (gl_error != NULL) {
598 g_warning(
"Failed to uninitialize GLArea: %s", gl_error->message);
610 FlView*
self = FL_VIEW(
object);
612 if (strcmp(
pspec->name,
"scale-factor") == 0) {
616 if (G_OBJECT_CLASS(fl_view_parent_class)->notify !=
nullptr) {
617 G_OBJECT_CLASS(fl_view_parent_class)->notify(
object,
pspec);
622 FlView*
self = FL_VIEW(
object);
624 g_cancellable_cancel(self->cancellable);
626 if (self->engine !=
nullptr) {
630 FlMouseCursorHandler* handler =
632 if (self->cursor_changed_cb_id != 0) {
633 g_signal_handler_disconnect(handler, self->cursor_changed_cb_id);
634 self->cursor_changed_cb_id = 0;
645 if (self->on_pre_engine_restart_cb_id != 0) {
646 g_signal_handler_disconnect(self->engine,
647 self->on_pre_engine_restart_cb_id);
648 self->on_pre_engine_restart_cb_id = 0;
651 g_clear_object(&self->engine);
652 g_clear_object(&self->renderer);
653 g_clear_pointer(&self->background_color, gdk_rgba_free);
654 g_clear_object(&self->window_state_monitor);
655 g_clear_object(&self->scrolling_manager);
656 g_clear_object(&self->pointer_manager);
657 g_clear_object(&self->touch_manager);
658 g_clear_object(&self->keyboard_manager);
659 g_clear_object(&self->keyboard_handler);
660 g_clear_object(&self->view_accessible);
661 g_clear_object(&self->cancellable);
663 G_OBJECT_CLASS(fl_view_parent_class)->dispose(
object);
668 FlView*
self = FL_VIEW(widget);
670 GTK_WIDGET_CLASS(fl_view_parent_class)->realize(widget);
673 gtk_widget_realize(GTK_WIDGET(self->gl_area));
678 GdkEventKey* key_event) {
679 FlView*
self = FL_VIEW(widget);
681 GdkEvent*
event =
reinterpret_cast<GdkEvent*
>(key_event);
683 self->keyboard_manager,
689 GdkEventKey* key_event) {
690 FlView*
self = FL_VIEW(widget);
691 GdkEvent*
event =
reinterpret_cast<GdkEvent*
>(key_event);
693 self->keyboard_manager,
698 GObjectClass* object_class = G_OBJECT_CLASS(klass);
702 GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(klass);
708 g_signal_new(
"first-frame", fl_view_get_type(), G_SIGNAL_RUN_LAST, 0,
709 NULL, NULL, NULL, G_TYPE_NONE, 0);
711 gtk_widget_class_set_accessible_type(GTK_WIDGET_CLASS(klass),
712 fl_socket_accessible_get_type());
716 self->cancellable = g_cancellable_new();
718 gtk_widget_set_can_focus(GTK_WIDGET(
self),
TRUE);
722 GdkRGBA default_background = {
723 .red = 0.0, .green = 0.0, .blue = 0.0, .alpha = 1.0};
724 self->background_color = gdk_rgba_copy(&default_background);
726 GtkWidget* event_box = gtk_event_box_new();
727 gtk_widget_set_hexpand(event_box,
TRUE);
728 gtk_widget_set_vexpand(event_box,
TRUE);
729 gtk_container_add(GTK_CONTAINER(
self), event_box);
730 gtk_widget_show(event_box);
731 gtk_widget_add_events(event_box,
732 GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK |
733 GDK_BUTTON_RELEASE_MASK | GDK_SCROLL_MASK |
734 GDK_SMOOTH_SCROLL_MASK | GDK_TOUCH_MASK);
736 g_signal_connect_swapped(event_box,
"button-press-event",
738 g_signal_connect_swapped(event_box,
"button-release-event",
740 g_signal_connect_swapped(event_box,
"scroll-event",
742 g_signal_connect_swapped(event_box,
"motion-notify-event",
744 g_signal_connect_swapped(event_box,
"enter-notify-event",
746 g_signal_connect_swapped(event_box,
"leave-notify-event",
748 GtkGesture* zoom = gtk_gesture_zoom_new(event_box);
751 g_signal_connect_swapped(zoom,
"scale-changed",
754 GtkGesture* rotate = gtk_gesture_rotate_new(event_box);
755 g_signal_connect_swapped(rotate,
"begin",
757 g_signal_connect_swapped(rotate,
"angle-changed",
761 g_signal_connect_swapped(event_box,
"touch-event", G_CALLBACK(
touch_event_cb),
764 self->gl_area = GTK_GL_AREA(gtk_gl_area_new());
765 gtk_gl_area_set_has_alpha(self->gl_area,
TRUE);
766 gtk_widget_show(GTK_WIDGET(self->gl_area));
767 gtk_container_add(GTK_CONTAINER(event_box), GTK_WIDGET(self->gl_area));
768 g_signal_connect_swapped(self->gl_area,
"render", G_CALLBACK(
render_cb),
771 g_signal_connect_swapped(
self,
"size-allocate", G_CALLBACK(
size_allocate_cb),
777 FlView*
self = FL_VIEW(g_object_new(fl_view_get_type(),
nullptr));
779 self->view_id = flutter::kFlutterImplicitViewId;
780 self->engine = FL_ENGINE(g_object_ref(engine));
782 g_assert(FL_IS_RENDERER_GDK(renderer));
783 self->renderer = FL_RENDERER_GDK(g_object_ref(renderer));
789 self->on_pre_engine_restart_cb_id =
790 g_signal_connect_swapped(engine,
"on-pre-engine-restart",
793 g_signal_connect_swapped(self->gl_area,
"create-context",
795 g_signal_connect_swapped(self->gl_area,
"realize", G_CALLBACK(
realize_cb),
797 g_signal_connect_swapped(self->gl_area,
"unrealize", G_CALLBACK(
unrealize_cb),
804 FlView*
self = FL_VIEW(g_object_new(fl_view_get_type(),
nullptr));
806 self->engine = FL_ENGINE(g_object_ref(engine));
808 g_assert(FL_IS_RENDERER_GDK(renderer));
809 self->renderer = FL_RENDERER_GDK(g_object_ref(renderer));
811 self->on_pre_engine_restart_cb_id =
812 g_signal_connect_swapped(engine,
"on-pre-engine-restart",
818 FL_RENDERABLE(
self));
828 g_return_val_if_fail(FL_IS_VIEW(
self),
nullptr);
834 g_return_val_if_fail(FL_IS_VIEW(
self), -1);
835 return self->view_id;
839 const GdkRGBA* color) {
840 g_return_if_fail(FL_IS_VIEW(
self));
841 gdk_rgba_free(self->background_color);
842 self->background_color = gdk_rgba_copy(color);