2 * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
20 #include "AtspiAccessibleWatcher.h"
21 #include "AtspiAccessibleApplication.h"
22 #include "AtspiAccessibleWindow.h"
23 #include "AtspiAccessibleNode.h"
24 #include "AtspiWrapper.h"
31 using namespace Aurum;
32 using namespace AurumInternal;
34 #define COMPARE(A, B) \
35 (B != A11yEvent::EVENT_NONE) && ((A & B) == B)
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;
42 static bool iShowingNode(AtspiAccessible *node)
45 if (node) name = AtspiWrapper::Atspi_accessible_get_name(node, NULL);
48 LOGI("isShowing %s", name);
49 auto stateSet = AtspiWrapper::Atspi_accessible_get_state_set(node);
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);
55 g_object_unref(stateSet);
59 g_object_unref(stateSet);
63 static std::vector<AtspiAccessible *>
64 findActiveNode(AtspiAccessible *node, int depth,
67 LOGI("findActiveNode %p %d/%d", node, depth, max_depth);
69 std::vector<AtspiAccessible *> ret{};
71 if (iShowingNode(node)) {
72 char *name = AtspiWrapper::Atspi_accessible_get_name(node, NULL);
81 if (depth >= max_depth) return ret;
83 int nchild = AtspiWrapper::Atspi_accessible_get_child_count(node, NULL);
84 if (nchild <= 0) return ret;
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);
98 gpointer AtspiAccessibleWatcher::eventThreadLoop(gpointer data)
100 AtspiAccessibleWatcher *instance = (AtspiAccessibleWatcher *)data;
101 GMainContext *mContext = nullptr;
103 LOGI("event thread start");
104 AtspiEventListener *listener =
105 atspi_event_listener_new(AtspiAccessibleWatcher::onAtspiEvents, instance, NULL);
107 atspi_event_listener_register(listener, "window:", NULL);
108 atspi_event_listener_register(listener, "object:", NULL);
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);
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);
120 g_object_unref(listener);
125 AtspiAccessibleWatcher::AtspiAccessibleWatcher()
126 : mDbusProxy{nullptr}
128 GVariant *result = nullptr;
129 GError *error = nullptr;
133 mEventThread = g_thread_new("AtspiEventThread", eventThreadLoop, this);
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",
141 result = g_dbus_proxy_call_sync(
143 g_variant_new("(ssv)", "org.a11y.Status", "IsEnabled", g_variant_new_boolean(true)),
144 G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
146 g_variant_unref(result);
147 if (error) g_error_free(error);
150 AtspiAccessibleWatcher::~AtspiAccessibleWatcher()
152 GVariant *result = nullptr;
153 GError *error = nullptr;
155 result = g_dbus_proxy_call_sync(
157 g_variant_new("(ssv)", "org.a11y.Status", "IsEnabled", g_variant_new_boolean(false)),
158 G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
160 g_object_unref(mDbusProxy);
161 g_variant_unref(result);
162 if (error) g_error_free(error);
164 g_main_loop_quit(mLoop);
165 g_thread_join(mEventThread);
169 void AtspiAccessibleWatcher::onAtspiEvents(AtspiEvent *event, void *watcher)
175 char *name = NULL, *pkg = NULL;
176 AtspiAccessibleWatcher *instance = (AtspiAccessibleWatcher *)watcher;
177 name = AtspiWrapper::Atspi_accessible_get_name(event->source, NULL);
179 AtspiAccessible *app = AtspiWrapper::Atspi_accessible_get_application(event->source, NULL);
182 pkg = AtspiWrapper::Atspi_accessible_get_name(app, NULL);
183 if (!strncmp(event->type, "window:activate", 15)) {
184 AtspiWrapper::Atspi_accessible_set_cache_mask(app, ATSPI_CACHE_ALL);
185 LOGI("window activated in app(%s)", pkg);
186 if (!instance->mActiveAppMap.count(app)) {
187 LOGI("add activated window's app in map");
188 instance->mActiveAppMap.insert(std::pair<AtspiAccessible *, std::shared_ptr<AccessibleApplication>>(app,
189 std::make_shared<AtspiAccessibleApplication>(std::make_shared<AtspiAccessibleNode>(app))));
192 LOGI("app(%s) is already in map", pkg);
195 else if (!strncmp(event->type, "window:deactivate", 16)) {
196 LOGI("window deactivate in app(%s)", pkg);
197 if (instance->mActiveAppMap.count(app)) {
198 LOGI("window deactivated delete app(%s) in map", pkg);
199 instance->mActiveAppMap.erase(app);
202 LOGE("deactivated window's app(%s) is not in map", pkg);
212 mEventQueue.push_back(std::make_shared<A11yEventInfo>(std::string(event->type), std::string(name), std::string(pkg)));
215 if (!strcmp(event->type, "object:state-changed:defunct")) {
216 instance->onObjectDefunct(
217 static_cast<AtspiAccessible *>(event->source));
219 if (name) free(name);
223 void AtspiAccessibleWatcher::onObjectDefunct(AtspiAccessible *node)
225 LOGI("onObjectDefunct obj:%p", node);
226 notifyAll((int)EventType::Object, (int)ObjectEventType::ObjectStateDefunct, node);
229 int AtspiAccessibleWatcher::getApplicationCount(void) const
231 AtspiAccessible *root = AtspiWrapper::Atspi_get_desktop(0);
232 int nchild = AtspiWrapper::Atspi_accessible_get_child_count(root, NULL);
233 g_object_unref(root);
235 if (nchild <= 0) return 0;
239 std::shared_ptr<AccessibleApplication> AtspiAccessibleWatcher::getApplicationAt(int index) const
241 AtspiAccessible *root = AtspiWrapper::Atspi_get_desktop(0);
242 AtspiAccessible *child = AtspiWrapper::Atspi_accessible_get_child_at_index(root, index, NULL);
243 g_object_unref(root);
244 return std::make_shared<AtspiAccessibleApplication>(std::make_shared<AtspiAccessibleNode>(child));
247 std::vector<std::shared_ptr<AccessibleApplication>> AtspiAccessibleWatcher::getApplications(void) const
249 std::vector<std::shared_ptr<AccessibleApplication>> ret{};
250 AtspiAccessible *root = AtspiWrapper::Atspi_get_desktop(0);
251 int nchild = AtspiWrapper::Atspi_accessible_get_child_count(root, NULL);
253 g_object_unref(root);
257 for (int i = 0; i < nchild; i++){
258 AtspiAccessible *child = AtspiWrapper::Atspi_accessible_get_child_at_index(root, i, NULL);
260 ret.push_back(std::make_shared<AtspiAccessibleApplication>(std::make_shared<AtspiAccessibleNode>(child)));
263 g_object_unref(root);
267 bool AtspiAccessibleWatcher::executeAndWaitForEvents(const Runnable *cmd, const A11yEvent type, const int timeout)
275 std::chrono::system_clock::time_point start =
276 std::chrono::system_clock::now();
279 std::vector<std::shared_ptr<A11yEventInfo>> localEvents;
281 localEvents.assign(mEventQueue.begin(), mEventQueue.end());
285 if (!localEvents.empty())
287 for (const auto &event : localEvents) {
288 if (COMPARE(type, event->getEvent()))
290 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());
295 if ((std::chrono::system_clock::now() - start) >
296 std::chrono::milliseconds{timeout})
298 std::this_thread::sleep_for(
299 std::chrono::milliseconds{100});
305 std::map<AtspiAccessible *, std::shared_ptr<AccessibleApplication>> AtspiAccessibleWatcher::getActiveAppMap(void)
307 return mActiveAppMap;
310 bool AtspiAccessibleWatcher::removeFromActivatedList(AtspiAccessible *node)
312 LOGI("remove from activelist node %p", node);
313 mActivatedWindowList.remove_if([&](auto &n) { return n == node; });
315 AtspiAccessible *app = AtspiWrapper::Atspi_accessible_get_application(node, NULL);
316 LOGI("node:%p, app:%p", node, app);
318 mActivatedApplicationList.remove_if([&](auto &n) { return n == app; });
324 bool AtspiAccessibleWatcher::addToActivatedList(AtspiAccessible *node)
326 LOGI("add to activelist node %p", node);
327 mActivatedWindowList.remove_if([&](auto &n) { return n == node; });
328 mActivatedWindowList.push_front(node);
330 auto iter = mWindowSet.find(node);
331 if ( iter == mWindowSet.end()) mWindowSet.insert(node);
333 AtspiAccessible *app = AtspiWrapper::Atspi_accessible_get_application(node, NULL);
334 LOGI("node:%p, app:%p", node, app);
336 mActivatedApplicationList.remove_if([&](auto &n) { if(n == app) { g_object_unref(app); return true;} else return false; });
337 mActivatedApplicationList.push_front(app);
343 bool AtspiAccessibleWatcher::removeFromWindowSet(AtspiAccessible *node)
345 removeFromActivatedList(node);
346 auto iter = mWindowSet.find(node);
347 if ( iter != mWindowSet.end()){
348 mWindowSet.erase(node);
354 bool AtspiAccessibleWatcher::addToWindowSet(AtspiAccessible *node)
356 auto iter = mWindowSet.find(node);
357 if ( iter == mWindowSet.end()){
358 mWindowSet.insert(node);