Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -42351,6 +42351,8 @@ ORIGIN: ../../../flutter/shell/platform/linux/fl_settings_plugin_test.cc + ../..
ORIGIN: ../../../flutter/shell/platform/linux/fl_settings_portal.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/linux/fl_settings_portal.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/linux/fl_settings_portal_test.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/linux/fl_socket_accessible.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/linux/fl_socket_accessible.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/linux/fl_standard_message_codec.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/linux/fl_standard_message_codec_test.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/linux/fl_standard_method_codec.cc + ../../../flutter/LICENSE
Expand Down Expand Up @@ -45264,6 +45266,8 @@ FILE: ../../../flutter/shell/platform/linux/fl_settings_plugin_test.cc
FILE: ../../../flutter/shell/platform/linux/fl_settings_portal.cc
FILE: ../../../flutter/shell/platform/linux/fl_settings_portal.h
FILE: ../../../flutter/shell/platform/linux/fl_settings_portal_test.cc
FILE: ../../../flutter/shell/platform/linux/fl_socket_accessible.cc
FILE: ../../../flutter/shell/platform/linux/fl_socket_accessible.h
FILE: ../../../flutter/shell/platform/linux/fl_standard_message_codec.cc
FILE: ../../../flutter/shell/platform/linux/fl_standard_message_codec_test.cc
FILE: ../../../flutter/shell/platform/linux/fl_standard_method_codec.cc
Expand Down
1 change: 1 addition & 0 deletions shell/platform/linux/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ source_set("flutter_linux_sources") {
"fl_settings.cc",
"fl_settings_plugin.cc",
"fl_settings_portal.cc",
"fl_socket_accessible.cc",
"fl_standard_message_codec.cc",
"fl_standard_method_codec.cc",
"fl_string_codec.cc",
Expand Down
57 changes: 57 additions & 0 deletions shell/platform/linux/fl_socket_accessible.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// 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_socket_accessible.h"

// This is a copy of GtkSocketAccessible, which requires GTK 3.24.30

struct _FlSocketAccessible {
GtkContainerAccessible parent;
AtkObject* accessible_socket;
};

G_DEFINE_TYPE(FlSocketAccessible,
fl_socket_accessible,
GTK_TYPE_CONTAINER_ACCESSIBLE)

static AtkObject* fl_socket_accessible_ref_child(AtkObject* object, int i) {
FlSocketAccessible* self = FL_SOCKET_ACCESSIBLE(object);
return i == 0 ? ATK_OBJECT(g_object_ref(self->accessible_socket)) : nullptr;
}

static int fl_socket_accessible_get_n_children(AtkObject* object) {
return 1;
}

static void fl_socket_accessible_finalize(GObject* object) {
FlSocketAccessible* self = FL_SOCKET_ACCESSIBLE(object);

g_clear_object(&self->accessible_socket);

G_OBJECT_CLASS(fl_socket_accessible_parent_class)->finalize(object);
}

static void fl_socket_accessible_initialize(AtkObject* object, gpointer data) {
FlSocketAccessible* self = FL_SOCKET_ACCESSIBLE(object);

ATK_OBJECT_CLASS(fl_socket_accessible_parent_class)->initialize(object, data);

self->accessible_socket = atk_socket_new();
}

static void fl_socket_accessible_class_init(FlSocketAccessibleClass* klass) {
GObjectClass* object_class = G_OBJECT_CLASS(klass);
object_class->finalize = fl_socket_accessible_finalize;

AtkObjectClass* atk_class = ATK_OBJECT_CLASS(klass);
atk_class->initialize = fl_socket_accessible_initialize;
atk_class->get_n_children = fl_socket_accessible_get_n_children;
atk_class->ref_child = fl_socket_accessible_ref_child;
}

static void fl_socket_accessible_init(FlSocketAccessible* self) {}

void fl_socket_accessible_embed(FlSocketAccessible* self, gchar* id) {
atk_socket_embed(ATK_SOCKET(self->accessible_socket), id);
}
22 changes: 22 additions & 0 deletions shell/platform/linux/fl_socket_accessible.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// 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.

#ifndef FLUTTER_SHELL_PLATFORM_LINUX_FL_SOCKET_ACCESSIBLE_H_
#define FLUTTER_SHELL_PLATFORM_LINUX_FL_SOCKET_ACCESSIBLE_H_

#include <gtk/gtk-a11y.h>

G_BEGIN_DECLS

G_DECLARE_FINAL_TYPE(FlSocketAccessible,
fl_socket_accessible,
FL,
SOCKET_ACCESSIBLE,
GtkContainerAccessible);

void fl_socket_accessible_embed(FlSocketAccessible* self, gchar* id);

G_END_DECLS

#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_SOCKET_ACCESSIBLE_H_
20 changes: 16 additions & 4 deletions shell/platform/linux/fl_view.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,12 @@

#include "flutter/shell/platform/linux/fl_view_private.h"

#include <atk/atk.h>
#include <gtk/gtk-a11y.h>

#include <cstring>

#include "flutter/shell/platform/linux/fl_accessible_node.h"
#include "flutter/shell/platform/linux/fl_backing_store_provider.h"
#include "flutter/shell/platform/linux/fl_engine_private.h"
#include "flutter/shell/platform/linux/fl_key_event.h"
Expand All @@ -19,6 +23,7 @@
#include "flutter/shell/platform/linux/fl_renderer_gdk.h"
#include "flutter/shell/platform/linux/fl_scrolling_manager.h"
#include "flutter/shell/platform/linux/fl_scrolling_view_delegate.h"
#include "flutter/shell/platform/linux/fl_socket_accessible.h"
#include "flutter/shell/platform/linux/fl_text_input_plugin.h"
#include "flutter/shell/platform/linux/fl_text_input_view_delegate.h"
#include "flutter/shell/platform/linux/fl_view_accessible.h"
Expand Down Expand Up @@ -64,6 +69,9 @@ struct _FlView {
gulong keymap_keys_changed_cb_id; // Signal connection ID for
// keymap-keys-changed
gulong window_state_cb_id; // Signal connection ID for window-state-changed

// Accessible tree from Flutter, exposed as an AtkPlug.
FlViewAccessible* view_accessible;
};

enum { kPropFlutterProject = 1, kPropLast };
Expand Down Expand Up @@ -231,9 +239,7 @@ static void update_semantics_cb(FlEngine* engine,
gpointer user_data) {
FlView* self = FL_VIEW(user_data);

AtkObject* accessible = gtk_widget_get_accessible(GTK_WIDGET(self));
fl_view_accessible_handle_update_semantics(FL_VIEW_ACCESSIBLE(accessible),
update);
fl_view_accessible_handle_update_semantics(self->view_accessible, update);
}

// Invoked by the engine right before the engine is restarted.
Expand Down Expand Up @@ -586,6 +592,11 @@ static void realize_cb(FlView* self) {
}

handle_geometry_changed(self);

self->view_accessible = fl_view_accessible_new(self->engine);
fl_socket_accessible_embed(
FL_SOCKET_ACCESSIBLE(gtk_widget_get_accessible(GTK_WIDGET(self))),
atk_plug_get_id(ATK_PLUG(self->view_accessible)));
}

static gboolean render_cb(FlView* self, GdkGLContext* context) {
Expand Down Expand Up @@ -691,6 +702,7 @@ static void fl_view_dispose(GObject* object) {
}
g_clear_object(&self->mouse_cursor_plugin);
g_clear_object(&self->platform_plugin);
g_clear_object(&self->view_accessible);

G_OBJECT_CLASS(fl_view_parent_class)->dispose(object);
}
Expand Down Expand Up @@ -733,7 +745,7 @@ static void fl_view_class_init(FlViewClass* klass) {
G_PARAM_STATIC_STRINGS)));

gtk_widget_class_set_accessible_type(GTK_WIDGET_CLASS(klass),
fl_view_accessible_get_type());
fl_socket_accessible_get_type());
}

static void fl_view_init(FlView* self) {
Expand Down
79 changes: 31 additions & 48 deletions shell/platform/linux/fl_view_accessible.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,45 +8,31 @@
#include "flutter/shell/platform/linux/public/flutter_linux/fl_value.h"
#include "flutter/shell/platform/linux/public/flutter_linux/fl_view.h"

static constexpr int32_t kRootSemanticsNodeId = 0;

struct _FlViewAccessible {
GtkContainerAccessible parent_instance;
AtkPlug parent_instance;

FlEngine* engine;

// Semantics nodes keyed by ID
GHashTable* semantics_nodes_by_id;

// Flag to track when root node is created.
gboolean root_node_created;
};

enum { kProp0, kPropEngine, kPropLast };

G_DEFINE_TYPE(FlViewAccessible,
fl_view_accessible,
GTK_TYPE_CONTAINER_ACCESSIBLE)

static void init_engine(FlViewAccessible* self, FlEngine* engine) {
g_assert(self->engine == nullptr);
self->engine = engine;
g_object_add_weak_pointer(G_OBJECT(self),
reinterpret_cast<gpointer*>(&self->engine));
}

static FlEngine* get_engine(FlViewAccessible* self) {
if (self->engine == nullptr) {
FlView* view = FL_VIEW(gtk_accessible_get_widget(GTK_ACCESSIBLE(self)));
init_engine(self, fl_view_get_engine(view));
}
return self->engine;
}
G_DEFINE_TYPE(FlViewAccessible, fl_view_accessible, ATK_TYPE_PLUG)

static FlAccessibleNode* create_node(FlViewAccessible* self,
FlutterSemanticsNode2* semantics) {
FlEngine* engine = get_engine(self);

if (semantics->flags & kFlutterSemanticsFlagIsTextField) {
return fl_accessible_text_field_new(engine, semantics->id);
return fl_accessible_text_field_new(self->engine, semantics->id);
}

return fl_accessible_node_new(engine, semantics->id);
return fl_accessible_node_new(self->engine, semantics->id);
}

static FlAccessibleNode* lookup_node(FlViewAccessible* self, int32_t id) {
Expand All @@ -64,14 +50,19 @@ static FlAccessibleNode* get_node(FlViewAccessible* self,
}

node = create_node(self, semantics);
if (semantics->id == 0) {
if (semantics->id == kRootSemanticsNodeId) {
fl_accessible_node_set_parent(node, ATK_OBJECT(self), 0);
g_signal_emit_by_name(self, "children-changed::add", 0, node, nullptr);
}
g_hash_table_insert(self->semantics_nodes_by_id,
GINT_TO_POINTER(semantics->id),
reinterpret_cast<gpointer>(node));

// Update when root node is created.
if (!self->root_node_created && semantics->id == kRootSemanticsNodeId) {
g_signal_emit_by_name(self, "children-changed::add", 0, node, nullptr);
self->root_node_created = true;
}

return node;
}

Expand Down Expand Up @@ -104,20 +95,11 @@ static AtkRole fl_view_accessible_get_role(AtkObject* accessible) {
return ATK_ROLE_PANEL;
}

// Implements GObject::set_property
static void fl_view_accessible_set_property(GObject* object,
guint prop_id,
const GValue* value,
GParamSpec* pspec) {
FlViewAccessible* self = FL_VIEW_ACCESSIBLE(object);
switch (prop_id) {
case kPropEngine:
init_engine(self, FL_ENGINE(g_value_get_object(value)));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
}
// Implements AtkObject::ref_state_set
static AtkStateSet* fl_view_accessible_ref_state_set(AtkObject* accessible) {
FlViewAccessible* self = FL_VIEW_ACCESSIBLE(accessible);
FlAccessibleNode* node = lookup_node(self, 0);
return node != nullptr ? atk_object_ref_state_set(ATK_OBJECT(node)) : nullptr;
}

static void fl_view_accessible_dispose(GObject* object) {
Expand All @@ -138,30 +120,31 @@ static void fl_view_accessible_class_init(FlViewAccessibleClass* klass) {
ATK_OBJECT_CLASS(klass)->get_n_children = fl_view_accessible_get_n_children;
ATK_OBJECT_CLASS(klass)->ref_child = fl_view_accessible_ref_child;
ATK_OBJECT_CLASS(klass)->get_role = fl_view_accessible_get_role;
ATK_OBJECT_CLASS(klass)->ref_state_set = fl_view_accessible_ref_state_set;

G_OBJECT_CLASS(klass)->dispose = fl_view_accessible_dispose;
G_OBJECT_CLASS(klass)->set_property = fl_view_accessible_set_property;

g_object_class_install_property(
G_OBJECT_CLASS(klass), kPropEngine,
g_param_spec_object(
"engine", "engine", "Flutter engine", fl_engine_get_type(),
static_cast<GParamFlags>(G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS)));
}

static void fl_view_accessible_init(FlViewAccessible* self) {
self->semantics_nodes_by_id = g_hash_table_new_full(
g_direct_hash, g_direct_equal, nullptr, g_object_unref);
}

FlViewAccessible* fl_view_accessible_new(FlEngine* engine) {
FlViewAccessible* self =
FL_VIEW_ACCESSIBLE(g_object_new(fl_view_accessible_get_type(), nullptr));
self->engine = engine;
g_object_add_weak_pointer(G_OBJECT(self),
reinterpret_cast<gpointer*>(&self->engine));
return self;
}

void fl_view_accessible_handle_update_semantics(
FlViewAccessible* self,
const FlutterSemanticsUpdate2* update) {
g_autoptr(GHashTable) pending_children =
g_hash_table_new_full(g_direct_hash, g_direct_equal, nullptr,
reinterpret_cast<GDestroyNotify>(fl_value_unref));

for (size_t i = 0; i < update->node_count; i++) {
FlutterSemanticsNode2* node = update->nodes[i];
FlAccessibleNode* atk_node = get_node(self, node);
Expand Down
21 changes: 19 additions & 2 deletions shell/platform/linux/fl_view_accessible.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,24 @@
#error "Only <flutter_linux/flutter_linux.h> can be included directly."
#endif

#include <gtk/gtk-a11y.h>
#include <atk/atk.h>

#include "flutter/shell/platform/embedder/embedder.h"
#include "flutter/shell/platform/linux/public/flutter_linux/fl_engine.h"

G_BEGIN_DECLS

// ATK g_autoptr macros weren't added until 2.37. Add them manually.
// https://gitlab.gnome.org/GNOME/atk/-/issues/10
#if !ATK_CHECK_VERSION(2, 37, 0)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(AtkPlug, g_object_unref)
#endif

G_DECLARE_FINAL_TYPE(FlViewAccessible,
fl_view_accessible,
FL,
VIEW_ACCESSIBLE,
GtkContainerAccessible)
AtkPlug)

/**
* FlViewAccessible:
Expand All @@ -28,6 +35,16 @@ G_DECLARE_FINAL_TYPE(FlViewAccessible,
* #FlView.
*/

/**
* fl_view_accessible_new:
*
* Creates a new accessibility object that exposes Flutter accessibility
* information to ATK.
*
* Returns: a new #FlViewAccessible.
*/
FlViewAccessible* fl_view_accessible_new(FlEngine* engine);

/**
* fl_view_accessible_handle_update_semantics:
* @accessible: an #FlViewAccessible.
Expand Down
6 changes: 2 additions & 4 deletions shell/platform/linux/fl_view_accessible_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@

TEST(FlViewAccessibleTest, BuildTree) {
g_autoptr(FlEngine) engine = make_mock_engine();
g_autoptr(FlViewAccessible) accessible = FL_VIEW_ACCESSIBLE(
g_object_new(fl_view_accessible_get_type(), "engine", engine, nullptr));
g_autoptr(FlViewAccessible) accessible = fl_view_accessible_new(engine);

int32_t children[] = {111, 222};
FlutterSemanticsNode2 root_node = {
Expand Down Expand Up @@ -49,8 +48,7 @@ TEST(FlViewAccessibleTest, BuildTree) {

TEST(FlViewAccessibleTest, AddRemoveChildren) {
g_autoptr(FlEngine) engine = make_mock_engine();
g_autoptr(FlViewAccessible) accessible = FL_VIEW_ACCESSIBLE(
g_object_new(fl_view_accessible_get_type(), "engine", engine, nullptr));
g_autoptr(FlViewAccessible) accessible = fl_view_accessible_new(engine);

FlutterSemanticsNode2 root_node = {
.id = 0,
Expand Down