| Flutter Linux Embedder |

fl_view_accessible.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 // Workaround missing C code compatibility in ATK header.
6 // Fixed in https://gitlab.gnome.org/GNOME/at-spi2-core/-/merge_requests/219
7 extern "C" {
8 #include <atk/atk.h>
9 }
10
11 #include "flutter/shell/platform/linux/fl_accessible_node.h"
12 #include "flutter/shell/platform/linux/fl_accessible_text_field.h"
13 #include "flutter/shell/platform/linux/fl_view_accessible.h"
14 #include "flutter/shell/platform/linux/public/flutter_linux/fl_value.h"
15 #include "flutter/shell/platform/linux/public/flutter_linux/fl_view.h"
16
17 static constexpr int32_t kRootSemanticsNodeId = 0;
18
19 struct _FlViewAccessible {
20 AtkPlug parent_instance;
21
22 GWeakRef engine;
23
24 FlutterViewId view_id;
25
26 // Semantics nodes keyed by ID
27 GHashTable* semantics_nodes_by_id;
28
29 // Flag to track when root node is created.
30 gboolean root_node_created;
31 };
32
33 G_DEFINE_TYPE(FlViewAccessible, fl_view_accessible, ATK_TYPE_PLUG)
34
35 // Enum copied from ATK 2.50, as the version we are building against doesn't
36 // have this.
37 typedef enum {
38 FL_ATK_LIVE_NONE,
39 FL_ATK_LIVE_POLITE,
40 FL_ATK_LIVE_ASSERTIVE
41 } FlAtkLive;
42
43 static FlAccessibleNode* create_node(FlViewAccessible* self,
44 FlutterSemanticsNode2* semantics) {
45 g_autoptr(FlEngine) engine = FL_ENGINE(g_weak_ref_get(&self->engine));
46 if (engine == nullptr) {
47 return nullptr;
48 }
49
50 if (semantics->flags2->is_text_field) {
51 return fl_accessible_text_field_new(engine, self->view_id, semantics->id);
52 }
53
54 return fl_accessible_node_new(engine, self->view_id, semantics->id);
55 }
56
57 static FlAccessibleNode* lookup_node(FlViewAccessible* self, int32_t id) {
58 return FL_ACCESSIBLE_NODE(
59 g_hash_table_lookup(self->semantics_nodes_by_id, GINT_TO_POINTER(id)));
60 }
61
62 // Gets the ATK node for the given id.
63 // If the node doesn't exist it will be created.
64 static FlAccessibleNode* get_node(FlViewAccessible* self,
65 FlutterSemanticsNode2* semantics) {
66 FlAccessibleNode* node = lookup_node(self, semantics->id);
67 if (node != nullptr) {
68 return node;
69 }
70
71 node = create_node(self, semantics);
72 if (semantics->id == kRootSemanticsNodeId) {
73 fl_accessible_node_set_parent(node, ATK_OBJECT(self), 0);
74 }
75 g_hash_table_insert(self->semantics_nodes_by_id,
76 GINT_TO_POINTER(semantics->id),
77 reinterpret_cast<gpointer>(node));
78
79 // Update when root node is created.
80 if (!self->root_node_created && semantics->id == kRootSemanticsNodeId) {
81 g_signal_emit_by_name(self, "children-changed::add", 0, node, nullptr);
82 self->root_node_created = true;
83 }
84
85 return node;
86 }
87
88 // Implements AtkObject::get_n_children
89 static gint fl_view_accessible_get_n_children(AtkObject* accessible) {
90 FlViewAccessible* self = FL_VIEW_ACCESSIBLE(accessible);
91 FlAccessibleNode* node = lookup_node(self, 0);
92
93 if (node == nullptr) {
94 return 0;
95 }
96
97 return 1;
98 }
99
100 // Implements AtkObject::ref_child
101 static AtkObject* fl_view_accessible_ref_child(AtkObject* accessible, gint i) {
102 FlViewAccessible* self = FL_VIEW_ACCESSIBLE(accessible);
103 FlAccessibleNode* node = lookup_node(self, 0);
104
105 if (i != 0 || node == nullptr) {
106 return nullptr;
107 }
108
109 return ATK_OBJECT(g_object_ref(node));
110 }
111
112 // Implements AtkObject::get_role
113 static AtkRole fl_view_accessible_get_role(AtkObject* accessible) {
114 return ATK_ROLE_PANEL;
115 }
116
117 // Implements AtkObject::ref_state_set
118 static AtkStateSet* fl_view_accessible_ref_state_set(AtkObject* accessible) {
119 FlViewAccessible* self = FL_VIEW_ACCESSIBLE(accessible);
120 FlAccessibleNode* node = lookup_node(self, 0);
121 return node != nullptr ? atk_object_ref_state_set(ATK_OBJECT(node)) : nullptr;
122 }
123
124 static void fl_view_accessible_dispose(GObject* object) {
125 FlViewAccessible* self = FL_VIEW_ACCESSIBLE(object);
126
127 g_clear_pointer(&self->semantics_nodes_by_id, g_hash_table_unref);
128
129 g_weak_ref_clear(&self->engine);
130
131 G_OBJECT_CLASS(fl_view_accessible_parent_class)->dispose(object);
132 }
133
134 static void fl_view_accessible_class_init(FlViewAccessibleClass* klass) {
135 ATK_OBJECT_CLASS(klass)->get_n_children = fl_view_accessible_get_n_children;
136 ATK_OBJECT_CLASS(klass)->ref_child = fl_view_accessible_ref_child;
137 ATK_OBJECT_CLASS(klass)->get_role = fl_view_accessible_get_role;
138 ATK_OBJECT_CLASS(klass)->ref_state_set = fl_view_accessible_ref_state_set;
139
140 G_OBJECT_CLASS(klass)->dispose = fl_view_accessible_dispose;
141 }
142
143 static void fl_view_accessible_init(FlViewAccessible* self) {
144 self->semantics_nodes_by_id = g_hash_table_new_full(
145 g_direct_hash, g_direct_equal, nullptr, g_object_unref);
146 }
147
148 FlViewAccessible* fl_view_accessible_new(FlEngine* engine,
149 FlutterViewId view_id) {
150 FlViewAccessible* self =
151 FL_VIEW_ACCESSIBLE(g_object_new(fl_view_accessible_get_type(), nullptr));
152 g_weak_ref_init(&self->engine, engine);
153 self->view_id = view_id;
154 return self;
155 }
156
157 void fl_view_accessible_handle_update_semantics(
158 FlViewAccessible* self,
159 const FlutterSemanticsUpdate2* update) {
160 g_return_if_fail(FL_IS_VIEW_ACCESSIBLE(self));
161
162 g_autoptr(GHashTable) pending_children =
163 g_hash_table_new_full(g_direct_hash, g_direct_equal, nullptr,
164 reinterpret_cast<GDestroyNotify>(fl_value_unref));
165 for (size_t i = 0; i < update->node_count; i++) {
166 FlutterSemanticsNode2* node = update->nodes[i];
167 FlAccessibleNode* atk_node = get_node(self, node);
168
169 fl_accessible_node_set_flags(atk_node, node->flags2);
170 fl_accessible_node_set_actions(atk_node, node->actions);
171 fl_accessible_node_set_name(atk_node, node->label);
172 fl_accessible_node_set_extents(
173 atk_node, node->rect.left + node->transform.transX,
174 node->rect.top + node->transform.transY,
175 node->rect.right - node->rect.left, node->rect.bottom - node->rect.top);
176 fl_accessible_node_set_value(atk_node, node->value);
177 fl_accessible_node_set_text_selection(atk_node, node->text_selection_base,
178 node->text_selection_extent);
179 fl_accessible_node_set_text_direction(atk_node, node->text_direction);
180
181 FlValue* children = fl_value_new_int32_list(
182 node->children_in_traversal_order, node->child_count);
183 g_hash_table_insert(pending_children, atk_node, children);
184 }
185
186 g_hash_table_foreach_remove(
187 pending_children,
188 [](gpointer key, gpointer value, gpointer user_data) -> gboolean {
189 FlViewAccessible* self = FL_VIEW_ACCESSIBLE(user_data);
190
191 FlAccessibleNode* parent = FL_ACCESSIBLE_NODE(key);
192
193 size_t child_count = fl_value_get_length(static_cast<FlValue*>(value));
194 const int32_t* children_in_traversal_order =
195 fl_value_get_int32_list(static_cast<FlValue*>(value));
196
197 g_autoptr(GPtrArray) children = g_ptr_array_new();
198 for (size_t i = 0; i < child_count; i++) {
199 FlAccessibleNode* child =
200 lookup_node(self, children_in_traversal_order[i]);
201 if (child == nullptr) {
202 continue;
203 }
204 fl_accessible_node_set_parent(child, ATK_OBJECT(parent), i);
205 g_ptr_array_add(children, child);
206 }
207 fl_accessible_node_set_children(parent, children);
208
209 return TRUE;
210 },
211 self);
212 }
213
214 void fl_view_accessible_send_announcement(FlViewAccessible* self,
215 const char* message,
216 gboolean assertive) {
217 g_return_if_fail(FL_IS_VIEW_ACCESSIBLE(self));
218 if (atk_get_major_version() == 2 && atk_get_minor_version() >= 50) {
219 g_signal_emit_by_name(
220 self, "notification", message,
221 assertive ? FL_ATK_LIVE_ASSERTIVE : FL_ATK_LIVE_POLITE);
222 } else if (atk_get_major_version() == 2 && atk_get_minor_version() >= 46) {
223 g_signal_emit_by_name(self, "announcement", message);
224 }
225 }
accessible
FlViewAccessible * accessible
Definition: fl_accessibility_handler.cc:41
message
const char * message
Definition: fl_accessibility_handler.cc:21
g_autoptr
g_autoptr(FlEngine) engine
user_data
const char FlTextDirection FlAssertiveness gpointer user_data
Definition: fl_accessibility_handler.cc:24
fl_accessible_node_set_name
void fl_accessible_node_set_name(FlAccessibleNode *self, const gchar *name)
Definition: fl_accessible_node.cc:607
fl_accessible_node_set_text_direction
void fl_accessible_node_set_text_direction(FlAccessibleNode *self, FlutterTextDirection direction)
Definition: fl_accessible_node.cc:653
fl_accessible_node_set_text_selection
void fl_accessible_node_set_text_selection(FlAccessibleNode *self, gint base, gint extent)
Definition: fl_accessible_node.cc:644
fl_accessible_node_set_actions
void fl_accessible_node_set_actions(FlAccessibleNode *self, FlutterSemanticsAction actions)
Definition: fl_accessible_node.cc:631
fl_accessible_node_set_children
void fl_accessible_node_set_children(FlAccessibleNode *self, GPtrArray *children)
Definition: fl_accessible_node.cc:580
fl_accessible_node_set_extents
void fl_accessible_node_set_extents(FlAccessibleNode *self, gint x, gint y, gint width, gint height)
Definition: fl_accessible_node.cc:613
fl_accessible_node_set_value
void fl_accessible_node_set_value(FlAccessibleNode *self, const gchar *value)
Definition: fl_accessible_node.cc:638
fl_accessible_node_set_parent
void fl_accessible_node_set_parent(FlAccessibleNode *self, AtkObject *parent, gint index)
Definition: fl_accessible_node.cc:569
fl_accessible_node_set_flags
void fl_accessible_node_set_flags(FlAccessibleNode *self, FlutterSemanticsFlags *flags)
Definition: fl_accessible_node.cc:624
fl_accessible_node_new
FlAccessibleNode * fl_accessible_node_new(FlEngine *engine, FlutterViewId view_id, int32_t node_id)
Definition: fl_accessible_node.cc:560
fl_accessible_node.h
fl_accessible_text_field_new
FlAccessibleNode * fl_accessible_text_field_new(FlEngine *engine, FlutterViewId view_id, int32_t id)
Definition: fl_accessible_text_field.cc:618
fl_accessible_text_field.h
G_DEFINE_TYPE
G_DEFINE_TYPE(FlBasicMessageChannelResponseHandle, fl_basic_message_channel_response_handle, G_TYPE_OBJECT) static void fl_basic_message_channel_response_handle_dispose(GObject *object)
Definition: fl_basic_message_channel.cc:37
TRUE
return TRUE
Definition: fl_compositor_software.cc:68
i
int i
Definition: fl_socket_accessible.cc:18
value
uint8_t value
Definition: fl_standard_message_codec.cc:36
fl_value_new_int32_list
G_MODULE_EXPORT FlValue * fl_value_new_int32_list(const int32_t *data, size_t data_length)
Definition: fl_value.cc:309
fl_value_unref
G_MODULE_EXPORT void fl_value_unref(FlValue *self)
Definition: fl_value.cc:400
fl_value_get_int32_list
G_MODULE_EXPORT const int32_t * fl_value_get_int32_list(FlValue *self)
Definition: fl_value.cc:696
fl_value_get_length
G_MODULE_EXPORT size_t fl_value_get_length(FlValue *self)
Definition: fl_value.cc:724
fl_value.h
FlValue
typedefG_BEGIN_DECLS struct _FlValue FlValue
Definition: fl_value.h:42
fl_view.h
fl_view_accessible_dispose
static void fl_view_accessible_dispose(GObject *object)
Definition: fl_view_accessible.cc:124
create_node
static FlAccessibleNode * create_node(FlViewAccessible *self, FlutterSemanticsNode2 *semantics)
Definition: fl_view_accessible.cc:43
fl_view_accessible_ref_child
static AtkObject * fl_view_accessible_ref_child(AtkObject *accessible, gint i)
Definition: fl_view_accessible.cc:101
fl_view_accessible_send_announcement
void fl_view_accessible_send_announcement(FlViewAccessible *self, const char *message, gboolean assertive)
Definition: fl_view_accessible.cc:214
fl_view_accessible_new
FlViewAccessible * fl_view_accessible_new(FlEngine *engine, FlutterViewId view_id)
Definition: fl_view_accessible.cc:148
fl_view_accessible_init
static void fl_view_accessible_init(FlViewAccessible *self)
Definition: fl_view_accessible.cc:143
fl_view_accessible_get_role
static AtkRole fl_view_accessible_get_role(AtkObject *accessible)
Definition: fl_view_accessible.cc:113
FlAtkLive
FlAtkLive
Definition: fl_view_accessible.cc:37
FL_ATK_LIVE_ASSERTIVE
@ FL_ATK_LIVE_ASSERTIVE
Definition: fl_view_accessible.cc:40
FL_ATK_LIVE_NONE
@ FL_ATK_LIVE_NONE
Definition: fl_view_accessible.cc:38
FL_ATK_LIVE_POLITE
@ FL_ATK_LIVE_POLITE
Definition: fl_view_accessible.cc:39
lookup_node
static FlAccessibleNode * lookup_node(FlViewAccessible *self, int32_t id)
Definition: fl_view_accessible.cc:57
get_node
static FlAccessibleNode * get_node(FlViewAccessible *self, FlutterSemanticsNode2 *semantics)
Definition: fl_view_accessible.cc:64
fl_view_accessible_get_n_children
static gint fl_view_accessible_get_n_children(AtkObject *accessible)
Definition: fl_view_accessible.cc:89
fl_view_accessible_class_init
static void fl_view_accessible_class_init(FlViewAccessibleClass *klass)
Definition: fl_view_accessible.cc:134
kRootSemanticsNodeId
static constexpr int32_t kRootSemanticsNodeId
Definition: fl_view_accessible.cc:17
fl_view_accessible_handle_update_semantics
void fl_view_accessible_handle_update_semantics(FlViewAccessible *self, const FlutterSemanticsUpdate2 *update)
Definition: fl_view_accessible.cc:157
fl_view_accessible_ref_state_set
static AtkStateSet * fl_view_accessible_ref_state_set(AtkObject *accessible)
Definition: fl_view_accessible.cc:118
fl_view_accessible.h
view_id
G_BEGIN_DECLS FlutterViewId view_id
Definition: fl_view_accessible.h:49
_FlViewAccessible
Definition: fl_view_accessible.cc:19
_FlViewAccessible::semantics_nodes_by_id
GHashTable * semantics_nodes_by_id
Definition: fl_view_accessible.cc:27
_FlViewAccessible::parent_instance
AtkPlug parent_instance
Definition: fl_view_accessible.cc:20
_FlViewAccessible::engine
GWeakRef engine
Definition: fl_view_accessible.cc:22
_FlViewAccessible::view_id
FlutterViewId view_id
Definition: fl_view_accessible.cc:24
_FlViewAccessible::root_node_created
gboolean root_node_created
Definition: fl_view_accessible.cc:30