2c14713717c24e641632c04eafdf1229eb0892fb
[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
41 static bool iShowingNode(AtspiAccessible *node)
42 {
43     char *name = NULL;
44     if (node) name = AtspiWrapper::Atspi_accessible_get_name(node, NULL);
45     else return false;
46
47     LOGI("isShowing %s", name);
48     auto stateSet = AtspiWrapper::Atspi_accessible_get_state_set(node);
49
50     if (AtspiWrapper::Atspi_state_set_contains(stateSet, ATSPI_STATE_ACTIVE)
51         && AtspiWrapper::Atspi_state_set_contains(stateSet, ATSPI_STATE_SHOWING)) {
52         LOGI("active and showing %p %s", node, name);
53         free(name);
54         g_object_unref(stateSet);
55         return true;
56     }
57     free(name);
58     g_object_unref(stateSet);
59     return false;
60 }
61
62 static std::vector<AtspiAccessible *>
63 findActiveNode(AtspiAccessible *node, int depth,
64                                        int max_depth)
65 {
66     LOGI("findActiveNode %p %d/%d", node, depth, max_depth);
67
68     std::vector<AtspiAccessible *> ret{};
69
70     if (iShowingNode(node)) {
71         char *name = AtspiWrapper::Atspi_accessible_get_name(node, NULL);
72         if (name) {
73             LOGI("%s", name);
74             free(name);
75         }
76         ret.push_back(node);
77         return ret;
78     }
79
80     if (depth >= max_depth) return ret;
81
82     int nchild = AtspiWrapper::Atspi_accessible_get_child_count(node, NULL);
83     if (nchild <= 0) return ret;
84
85     LOGI("findActiveNode node %p has %d children", node, nchild);
86     for (int i = 0; i < nchild; i++) {
87         AtspiAccessible *child = AtspiWrapper::Atspi_accessible_get_child_at_index(node, i, NULL);
88         LOGI("a child found @ %d : %p", i, child);
89         std::vector<AtspiAccessible *> childRet = findActiveNode(child, depth + 1, max_depth);
90         ret.insert(ret.end(), childRet.begin(), childRet.end());
91         g_object_unref(child);
92     }
93
94     return ret;
95 }
96
97 static gpointer _event_thread_loop (gpointer data)
98 {
99     LOGI("event thread start");
100     AtspiEventListener *listener =
101         atspi_event_listener_new(AtspiAccessibleWatcher::onAtspiEvents, data, NULL);
102
103     atspi_event_listener_register(listener, "window:", NULL);
104     atspi_event_listener_register(listener, "object:", NULL);
105
106     atspi_event_main();
107
108     LOGI("event thread end");
109     atspi_event_listener_deregister(listener, "object:", NULL);
110     atspi_event_listener_deregister(listener, "window:", NULL);
111
112     g_object_unref(listener);
113
114     return NULL;
115 }
116 AtspiAccessibleWatcher::AtspiAccessibleWatcher()
117 : mDbusProxy{nullptr}
118 {
119     GVariant *result = nullptr;
120     GError *error = nullptr;
121
122     atspi_set_main_context (g_main_context_default ());
123     atspi_init();
124
125     mEventThread = g_thread_new("AtspiEventThread", _event_thread_loop, this);
126
127     mDbusProxy = g_dbus_proxy_new_for_bus_sync(
128         G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_NONE,
129         NULL, /* GDBusInterfaceInfo */
130         "org.a11y.Bus", "/org/a11y/bus", "org.freedesktop.DBus.Properties",
131         NULL, &error);
132
133     result = g_dbus_proxy_call_sync(
134         mDbusProxy, "Set",
135         g_variant_new("(ssv)", "org.a11y.Status", "IsEnabled", g_variant_new_boolean(true)),
136         G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
137
138     g_variant_unref(result);
139     if (error) g_error_free(error);
140 }
141
142 AtspiAccessibleWatcher::~AtspiAccessibleWatcher()
143 {
144     GVariant *result = nullptr;
145     GError *error = nullptr;
146
147     result = g_dbus_proxy_call_sync(
148         mDbusProxy, "Set",
149         g_variant_new("(ssv)", "org.a11y.Status", "IsEnabled", g_variant_new_boolean(false)),
150         G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
151
152     g_object_unref(mDbusProxy);
153     g_variant_unref(result);
154     if (error) g_error_free(error);
155
156     atspi_event_quit();
157     g_thread_join(mEventThread);
158     atspi_exit();
159 }
160
161 void AtspiAccessibleWatcher::onAtspiEvents(AtspiEvent *event, void *watcher)
162 {
163     if (!event->source)
164     {
165         return;
166     }
167     char *name = NULL, *pkg = NULL;
168     AtspiAccessibleWatcher *instance = (AtspiAccessibleWatcher *)watcher;
169     name = AtspiWrapper::Atspi_accessible_get_name(event->source, NULL);
170
171     AtspiAccessible *app = AtspiWrapper::Atspi_accessible_get_application(event->source, NULL);
172     if (app)
173     {
174         pkg = AtspiWrapper::Atspi_accessible_get_name(app, NULL);
175         if (!strncmp(event->type, "window:activate", 15)) {
176             LOGI("window activated in app(%s)", pkg);
177             if (!instance->mActiveAppMap.count(app)) {
178                 LOGI("add activated window's app in map");
179                 instance->mActiveAppMap.insert(std::pair<AtspiAccessible *, std::shared_ptr<AccessibleApplication>>(app,
180                                      std::make_shared<AtspiAccessibleApplication>(std::make_shared<AtspiAccessibleNode>(app))));
181             }
182             else {
183                 LOGI("app(%s) is already in map", pkg);
184             }
185         }
186         else if (!strncmp(event->type, "window:deactivate", 16)) {
187             LOGI("window deactivate in app(%s)", pkg);
188             if (instance->mActiveAppMap.count(app)) {
189                 LOGI("window deactivated delete app(%s) in map", pkg);
190                 instance->mActiveAppMap.erase(app);
191             }
192             else {
193                 LOGE("deactivated window's app(%s) is not in map", pkg);
194             }
195
196             g_object_unref(app);
197         }
198     }
199     else
200         pkg = strdup("");
201
202     mMutex.lock();
203     mEventQueue.push_back(std::make_shared<A11yEventInfo>(std::string(event->type), std::string(name), std::string(pkg)));
204     mMutex.unlock();
205
206     if (!strcmp(event->type, "object:state-changed:defunct")) {
207          instance->onObjectDefunct(
208             static_cast<AtspiAccessible *>(event->source));
209     }
210     if (name) free(name);
211     if (pkg) free(pkg);
212 }
213
214 void AtspiAccessibleWatcher::onObjectDefunct(AtspiAccessible *node)
215 {
216     LOGI("onObjectDefunct obj:%p", node);
217     notifyAll((int)EventType::Object, (int)ObjectEventType::ObjectStateDefunct, node);
218 }
219
220 int AtspiAccessibleWatcher::getApplicationCount(void) const
221 {
222     AtspiAccessible *root = AtspiWrapper::Atspi_get_desktop(0);
223     int nchild = AtspiWrapper::Atspi_accessible_get_child_count(root, NULL);
224     g_object_unref(root);
225
226     if (nchild <= 0) return 0;
227     return nchild;
228 }
229
230 std::shared_ptr<AccessibleApplication> AtspiAccessibleWatcher::getApplicationAt(int index) const
231 {
232     AtspiAccessible *root = AtspiWrapper::Atspi_get_desktop(0);
233     AtspiAccessible *child = AtspiWrapper::Atspi_accessible_get_child_at_index(root, index, NULL);
234     g_object_unref(root);
235     return std::make_shared<AtspiAccessibleApplication>(std::make_shared<AtspiAccessibleNode>(child));
236 }
237
238 std::vector<std::shared_ptr<AccessibleApplication>> AtspiAccessibleWatcher::getApplications(void) const
239 {
240     std::vector<std::shared_ptr<AccessibleApplication>> ret{};
241     AtspiAccessible *root = AtspiWrapper::Atspi_get_desktop(0);
242     int nchild = AtspiWrapper::Atspi_accessible_get_child_count(root, NULL);
243     if (nchild <= 0) {
244         g_object_unref(root);
245         return ret;
246     }
247
248     for (int i = 0; i < nchild; i++){
249         AtspiAccessible *child = AtspiWrapper::Atspi_accessible_get_child_at_index(root, i, NULL);
250         if (child) {
251             ret.push_back(std::make_shared<AtspiAccessibleApplication>(std::make_shared<AtspiAccessibleNode>(child)));
252         }
253     }
254     g_object_unref(root);
255     return ret;
256 }
257
258 bool AtspiAccessibleWatcher::executeAndWaitForEvents(const Runnable *cmd, const A11yEvent type, const int timeout)
259 {
260     mMutex.lock();
261     mEventQueue.clear();
262     mMutex.unlock();
263     if (cmd)
264         cmd->run();
265
266     std::chrono::system_clock::time_point start =
267         std::chrono::system_clock::now();
268         while (true)
269     {
270         std::vector<std::shared_ptr<A11yEventInfo>> localEvents;
271         mMutex.lock();
272         localEvents.assign(mEventQueue.begin(), mEventQueue.end());
273         mEventQueue.clear();
274         mMutex.unlock();
275
276         if (!localEvents.empty())
277         {
278             for (const auto &event : localEvents) {
279                 if (COMPARE(type, event->getEvent()))
280                 {
281                     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()); 
282                     return true;
283                 }
284             }
285         }
286         if ((std::chrono::system_clock::now() - start) >
287             std::chrono::milliseconds{timeout})
288             break;
289         std::this_thread::sleep_for(
290             std::chrono::milliseconds{100});
291     }
292
293     return false;
294 }
295
296 std::map<AtspiAccessible *, std::shared_ptr<AccessibleApplication>> AtspiAccessibleWatcher::getActiveAppMap(void)
297 {
298     return mActiveAppMap;
299 }
300
301 bool AtspiAccessibleWatcher::removeFromActivatedList(AtspiAccessible *node)
302 {
303     LOGI("remove from activelist node %p", node);
304     mActivatedWindowList.remove_if([&](auto &n) { return n == node; });
305
306     AtspiAccessible *app = AtspiWrapper::Atspi_accessible_get_application(node, NULL);
307     LOGI("node:%p, app:%p", node, app);
308     if (app) {
309         mActivatedApplicationList.remove_if([&](auto &n) { return n == app; });
310         g_object_unref(app);
311     }
312     return true;
313 }
314
315 bool AtspiAccessibleWatcher::addToActivatedList(AtspiAccessible *node)
316 {
317     LOGI("add to activelist node %p", node);
318     mActivatedWindowList.remove_if([&](auto &n) { return n == node; });
319     mActivatedWindowList.push_front(node);
320
321     auto iter = mWindowSet.find(node);
322     if ( iter == mWindowSet.end()) mWindowSet.insert(node);
323
324     AtspiAccessible *app = AtspiWrapper::Atspi_accessible_get_application(node, NULL);
325     LOGI("node:%p, app:%p", node, app);
326     if (app) {
327         mActivatedApplicationList.remove_if([&](auto &n) { if(n == app) { g_object_unref(app); return true;} else return false; });
328         mActivatedApplicationList.push_front(app);
329     }
330
331     return true;
332 }
333
334 bool AtspiAccessibleWatcher::removeFromWindowSet(AtspiAccessible *node)
335 {
336     removeFromActivatedList(node);
337     auto iter = mWindowSet.find(node);
338     if ( iter != mWindowSet.end()){
339         mWindowSet.erase(node);
340         return true;
341     }
342     return false;
343 }
344
345 bool AtspiAccessibleWatcher::addToWindowSet(AtspiAccessible *node)
346 {
347     auto iter = mWindowSet.find(node);
348     if ( iter == mWindowSet.end()){
349         mWindowSet.insert(node);
350         return true;
351     }
352     return false;
353 }