AtspiAccessibleWatcher: use thread context instead of default context.
[platform/core/uifw/aurum.git] / libaurum / src / Impl / Accessibility / AtspiAccessibleWatcher.cc
1 /*
2  * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *  Licensed under the Apache License, Version 2.0 (the "License");
5  *  you may not use this file except in compliance with the License.
6  *  You may obtain a copy of the License at
7  *
8  *               http://www.apache.org/licenses/LICENSE-2.0
9  *
10  *  Unless required by applicable law or agreed to in writing, software
11  *  distributed under the License is distributed on an "AS IS" BASIS,
12  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *  See the License for the specific language governing permissions and
14  *  limitations under the License.
15  *
16  */
17
18 #include "Aurum.h"
19
20 #include "AtspiAccessibleWatcher.h"
21 #include "AtspiAccessibleApplication.h"
22 #include "AtspiAccessibleWindow.h"
23 #include "AtspiAccessibleNode.h"
24 #include "AtspiWrapper.h"
25
26 #include <algorithm>
27 #include <chrono>
28 #include <thread>
29 #include <iostream>
30
31 using namespace Aurum;
32 using namespace AurumInternal;
33
34 #define COMPARE(A, B) \
35     (B != A11yEvent::EVENT_NONE) && ((A & B) == B)
36
37 std::vector<std::shared_ptr<A11yEventInfo>> AtspiAccessibleWatcher::mEventQueue;
38 GThread *AtspiAccessibleWatcher::mEventThread = nullptr;
39 std::mutex AtspiAccessibleWatcher::mMutex = std::mutex{};
40 GMainLoop *AtspiAccessibleWatcher::mLoop = nullptr;
41
42 static bool iShowingNode(AtspiAccessible *node)
43 {
44     char *name = NULL;
45     if (node) name = AtspiWrapper::Atspi_accessible_get_name(node, NULL);
46     else return false;
47
48     LOGI("isShowing %s", name);
49     auto stateSet = AtspiWrapper::Atspi_accessible_get_state_set(node);
50
51     if (AtspiWrapper::Atspi_state_set_contains(stateSet, ATSPI_STATE_ACTIVE)
52         && AtspiWrapper::Atspi_state_set_contains(stateSet, ATSPI_STATE_SHOWING)) {
53         LOGI("active and showing %p %s", node, name);
54         free(name);
55         g_object_unref(stateSet);
56         return true;
57     }
58     free(name);
59     g_object_unref(stateSet);
60     return false;
61 }
62
63 static std::vector<AtspiAccessible *>
64 findActiveNode(AtspiAccessible *node, int depth,
65                                        int max_depth)
66 {
67     LOGI("findActiveNode %p %d/%d", node, depth, max_depth);
68
69     std::vector<AtspiAccessible *> ret{};
70
71     if (iShowingNode(node)) {
72         char *name = AtspiWrapper::Atspi_accessible_get_name(node, NULL);
73         if (name) {
74             LOGI("%s", name);
75             free(name);
76         }
77         ret.push_back(node);
78         return ret;
79     }
80
81     if (depth >= max_depth) return ret;
82
83     int nchild = AtspiWrapper::Atspi_accessible_get_child_count(node, NULL);
84     if (nchild <= 0) return ret;
85
86     LOGI("findActiveNode node %p has %d children", node, nchild);
87     for (int i = 0; i < nchild; i++) {
88         AtspiAccessible *child = AtspiWrapper::Atspi_accessible_get_child_at_index(node, i, NULL);
89         LOGI("a child found @ %d : %p", i, child);
90         std::vector<AtspiAccessible *> childRet = findActiveNode(child, depth + 1, max_depth);
91         ret.insert(ret.end(), childRet.begin(), childRet.end());
92         g_object_unref(child);
93     }
94
95     return ret;
96 }
97
98 gpointer AtspiAccessibleWatcher::eventThreadLoop(gpointer data)
99 {
100     AtspiAccessibleWatcher *instance = (AtspiAccessibleWatcher *)data;
101     GMainContext *mContext = nullptr;
102
103     LOGI("event thread start");
104     AtspiEventListener *listener =
105         atspi_event_listener_new(AtspiAccessibleWatcher::onAtspiEvents, instance, NULL);
106
107     atspi_event_listener_register(listener, "window:", NULL);
108     atspi_event_listener_register(listener, "object:", NULL);
109
110     mContext = g_main_context_new();
111     g_main_context_push_thread_default(mContext);
112     atspi_set_main_context (mContext);
113     instance->mLoop = g_main_loop_new(mContext, FALSE);
114
115     g_main_loop_run(instance->mLoop);
116     LOGI("event thread end");
117     atspi_event_listener_deregister(listener, "object:", NULL);
118     atspi_event_listener_deregister(listener, "window:", NULL);
119
120     g_object_unref(listener);
121
122     return NULL;
123 }
124
125 AtspiAccessibleWatcher::AtspiAccessibleWatcher()
126 : mDbusProxy{nullptr}
127 {
128     GVariant *result = nullptr;
129     GError *error = nullptr;
130
131     atspi_init();
132
133     mEventThread = g_thread_new("AtspiEventThread", eventThreadLoop, this);
134
135     mDbusProxy = g_dbus_proxy_new_for_bus_sync(
136         G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_NONE,
137         NULL, /* GDBusInterfaceInfo */
138         "org.a11y.Bus", "/org/a11y/bus", "org.freedesktop.DBus.Properties",
139         NULL, &error);
140
141     result = g_dbus_proxy_call_sync(
142         mDbusProxy, "Set",
143         g_variant_new("(ssv)", "org.a11y.Status", "IsEnabled", g_variant_new_boolean(true)),
144         G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
145
146     g_variant_unref(result);
147     if (error) g_error_free(error);
148 }
149
150 AtspiAccessibleWatcher::~AtspiAccessibleWatcher()
151 {
152     GVariant *result = nullptr;
153     GError *error = nullptr;
154
155     result = g_dbus_proxy_call_sync(
156         mDbusProxy, "Set",
157         g_variant_new("(ssv)", "org.a11y.Status", "IsEnabled", g_variant_new_boolean(false)),
158         G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
159
160     g_object_unref(mDbusProxy);
161     g_variant_unref(result);
162     if (error) g_error_free(error);
163
164     g_main_loop_quit(mLoop);
165     g_thread_join(mEventThread);
166     atspi_exit();
167 }
168
169 void AtspiAccessibleWatcher::onAtspiEvents(AtspiEvent *event, void *watcher)
170 {
171     if (!event->source)
172     {
173         return;
174     }
175     char *name = NULL, *pkg = NULL;
176     AtspiAccessibleWatcher *instance = (AtspiAccessibleWatcher *)watcher;
177     name = AtspiWrapper::Atspi_accessible_get_name(event->source, NULL);
178
179     AtspiAccessible *app = AtspiWrapper::Atspi_accessible_get_application(event->source, NULL);
180     if (app)
181     {
182         pkg = AtspiWrapper::Atspi_accessible_get_name(app, NULL);
183         if (!strncmp(event->type, "window:activate", 15)) {
184             LOGI("window activated in app(%s)", pkg);
185             if (!instance->mActiveAppMap.count(app)) {
186                 LOGI("add activated window's app in map");
187                 instance->mActiveAppMap.insert(std::pair<AtspiAccessible *, std::shared_ptr<AccessibleApplication>>(app,
188                                      std::make_shared<AtspiAccessibleApplication>(std::make_shared<AtspiAccessibleNode>(app))));
189             }
190             else {
191                 LOGI("app(%s) is already in map", pkg);
192             }
193         }
194         else if (!strncmp(event->type, "window:deactivate", 16)) {
195             LOGI("window deactivate in app(%s)", pkg);
196             if (instance->mActiveAppMap.count(app)) {
197                 LOGI("window deactivated delete app(%s) in map", pkg);
198                 instance->mActiveAppMap.erase(app);
199             }
200             else {
201                 LOGE("deactivated window's app(%s) is not in map", pkg);
202             }
203
204             g_object_unref(app);
205         }
206     }
207     else
208         pkg = strdup("");
209
210     mMutex.lock();
211     mEventQueue.push_back(std::make_shared<A11yEventInfo>(std::string(event->type), std::string(name), std::string(pkg)));
212     mMutex.unlock();
213
214     if (!strcmp(event->type, "object:state-changed:defunct")) {
215          instance->onObjectDefunct(
216             static_cast<AtspiAccessible *>(event->source));
217     }
218     if (name) free(name);
219     if (pkg) free(pkg);
220 }
221
222 void AtspiAccessibleWatcher::onObjectDefunct(AtspiAccessible *node)
223 {
224     LOGI("onObjectDefunct obj:%p", node);
225     notifyAll((int)EventType::Object, (int)ObjectEventType::ObjectStateDefunct, node);
226 }
227
228 int AtspiAccessibleWatcher::getApplicationCount(void) const
229 {
230     AtspiAccessible *root = AtspiWrapper::Atspi_get_desktop(0);
231     int nchild = AtspiWrapper::Atspi_accessible_get_child_count(root, NULL);
232     g_object_unref(root);
233
234     if (nchild <= 0) return 0;
235     return nchild;
236 }
237
238 std::shared_ptr<AccessibleApplication> AtspiAccessibleWatcher::getApplicationAt(int index) const
239 {
240     AtspiAccessible *root = AtspiWrapper::Atspi_get_desktop(0);
241     AtspiAccessible *child = AtspiWrapper::Atspi_accessible_get_child_at_index(root, index, NULL);
242     g_object_unref(root);
243     return std::make_shared<AtspiAccessibleApplication>(std::make_shared<AtspiAccessibleNode>(child));
244 }
245
246 std::vector<std::shared_ptr<AccessibleApplication>> AtspiAccessibleWatcher::getApplications(void) const
247 {
248     std::vector<std::shared_ptr<AccessibleApplication>> ret{};
249     AtspiAccessible *root = AtspiWrapper::Atspi_get_desktop(0);
250     int nchild = AtspiWrapper::Atspi_accessible_get_child_count(root, NULL);
251     if (nchild <= 0) {
252         g_object_unref(root);
253         return ret;
254     }
255
256     for (int i = 0; i < nchild; i++){
257         AtspiAccessible *child = AtspiWrapper::Atspi_accessible_get_child_at_index(root, i, NULL);
258         if (child) {
259             ret.push_back(std::make_shared<AtspiAccessibleApplication>(std::make_shared<AtspiAccessibleNode>(child)));
260         }
261     }
262     g_object_unref(root);
263     return ret;
264 }
265
266 bool AtspiAccessibleWatcher::executeAndWaitForEvents(const Runnable *cmd, const A11yEvent type, const int timeout)
267 {
268     mMutex.lock();
269     mEventQueue.clear();
270     mMutex.unlock();
271     if (cmd)
272         cmd->run();
273
274     std::chrono::system_clock::time_point start =
275         std::chrono::system_clock::now();
276         while (true)
277     {
278         std::vector<std::shared_ptr<A11yEventInfo>> localEvents;
279         mMutex.lock();
280         localEvents.assign(mEventQueue.begin(), mEventQueue.end());
281         mEventQueue.clear();
282         mMutex.unlock();
283
284         if (!localEvents.empty())
285         {
286             for (const auto &event : localEvents) {
287                 if (COMPARE(type, event->getEvent()))
288                 {
289                     LOGI("type %d == %d name %s pkg %s",static_cast<int>(type), static_cast<int>(event->getEvent()), event->getName().c_str(), event->getPkg().c_str()); 
290                     return true;
291                 }
292             }
293         }
294         if ((std::chrono::system_clock::now() - start) >
295             std::chrono::milliseconds{timeout})
296             break;
297         std::this_thread::sleep_for(
298             std::chrono::milliseconds{100});
299     }
300
301     return false;
302 }
303
304 std::map<AtspiAccessible *, std::shared_ptr<AccessibleApplication>> AtspiAccessibleWatcher::getActiveAppMap(void)
305 {
306     return mActiveAppMap;
307 }
308
309 bool AtspiAccessibleWatcher::removeFromActivatedList(AtspiAccessible *node)
310 {
311     LOGI("remove from activelist node %p", node);
312     mActivatedWindowList.remove_if([&](auto &n) { return n == node; });
313
314     AtspiAccessible *app = AtspiWrapper::Atspi_accessible_get_application(node, NULL);
315     LOGI("node:%p, app:%p", node, app);
316     if (app) {
317         mActivatedApplicationList.remove_if([&](auto &n) { return n == app; });
318         g_object_unref(app);
319     }
320     return true;
321 }
322
323 bool AtspiAccessibleWatcher::addToActivatedList(AtspiAccessible *node)
324 {
325     LOGI("add to activelist node %p", node);
326     mActivatedWindowList.remove_if([&](auto &n) { return n == node; });
327     mActivatedWindowList.push_front(node);
328
329     auto iter = mWindowSet.find(node);
330     if ( iter == mWindowSet.end()) mWindowSet.insert(node);
331
332     AtspiAccessible *app = AtspiWrapper::Atspi_accessible_get_application(node, NULL);
333     LOGI("node:%p, app:%p", node, app);
334     if (app) {
335         mActivatedApplicationList.remove_if([&](auto &n) { if(n == app) { g_object_unref(app); return true;} else return false; });
336         mActivatedApplicationList.push_front(app);
337     }
338
339     return true;
340 }
341
342 bool AtspiAccessibleWatcher::removeFromWindowSet(AtspiAccessible *node)
343 {
344     removeFromActivatedList(node);
345     auto iter = mWindowSet.find(node);
346     if ( iter != mWindowSet.end()){
347         mWindowSet.erase(node);
348         return true;
349     }
350     return false;
351 }
352
353 bool AtspiAccessibleWatcher::addToWindowSet(AtspiAccessible *node)
354 {
355     auto iter = mWindowSet.find(node);
356     if ( iter == mWindowSet.end()){
357         mWindowSet.insert(node);
358         return true;
359     }
360     return false;
361 }