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 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))));
191 LOGI("app(%s) is already in map", pkg);
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);
201 LOGE("deactivated window's app(%s) is not in map", pkg);
211 mEventQueue.push_back(std::make_shared<A11yEventInfo>(std::string(event->type), std::string(name), std::string(pkg)));
214 if (!strcmp(event->type, "object:state-changed:defunct")) {
215 instance->onObjectDefunct(
216 static_cast<AtspiAccessible *>(event->source));
218 if (name) free(name);
222 void AtspiAccessibleWatcher::onObjectDefunct(AtspiAccessible *node)
224 LOGI("onObjectDefunct obj:%p", node);
225 notifyAll((int)EventType::Object, (int)ObjectEventType::ObjectStateDefunct, node);
228 int AtspiAccessibleWatcher::getApplicationCount(void) const
230 AtspiAccessible *root = AtspiWrapper::Atspi_get_desktop(0);
231 int nchild = AtspiWrapper::Atspi_accessible_get_child_count(root, NULL);
232 g_object_unref(root);
234 if (nchild <= 0) return 0;
238 std::shared_ptr<AccessibleApplication> AtspiAccessibleWatcher::getApplicationAt(int index) const
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));
246 std::vector<std::shared_ptr<AccessibleApplication>> AtspiAccessibleWatcher::getApplications(void) const
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);
252 g_object_unref(root);
256 for (int i = 0; i < nchild; i++){
257 AtspiAccessible *child = AtspiWrapper::Atspi_accessible_get_child_at_index(root, i, NULL);
259 ret.push_back(std::make_shared<AtspiAccessibleApplication>(std::make_shared<AtspiAccessibleNode>(child)));
262 g_object_unref(root);
266 bool AtspiAccessibleWatcher::executeAndWaitForEvents(const Runnable *cmd, const A11yEvent type, const int timeout)
274 std::chrono::system_clock::time_point start =
275 std::chrono::system_clock::now();
278 std::vector<std::shared_ptr<A11yEventInfo>> localEvents;
280 localEvents.assign(mEventQueue.begin(), mEventQueue.end());
284 if (!localEvents.empty())
286 for (const auto &event : localEvents) {
287 if (COMPARE(type, event->getEvent()))
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());
294 if ((std::chrono::system_clock::now() - start) >
295 std::chrono::milliseconds{timeout})
297 std::this_thread::sleep_for(
298 std::chrono::milliseconds{100});
304 std::map<AtspiAccessible *, std::shared_ptr<AccessibleApplication>> AtspiAccessibleWatcher::getActiveAppMap(void)
306 return mActiveAppMap;
309 bool AtspiAccessibleWatcher::removeFromActivatedList(AtspiAccessible *node)
311 LOGI("remove from activelist node %p", node);
312 mActivatedWindowList.remove_if([&](auto &n) { return n == node; });
314 AtspiAccessible *app = AtspiWrapper::Atspi_accessible_get_application(node, NULL);
315 LOGI("node:%p, app:%p", node, app);
317 mActivatedApplicationList.remove_if([&](auto &n) { return n == app; });
323 bool AtspiAccessibleWatcher::addToActivatedList(AtspiAccessible *node)
325 LOGI("add to activelist node %p", node);
326 mActivatedWindowList.remove_if([&](auto &n) { return n == node; });
327 mActivatedWindowList.push_front(node);
329 auto iter = mWindowSet.find(node);
330 if ( iter == mWindowSet.end()) mWindowSet.insert(node);
332 AtspiAccessible *app = AtspiWrapper::Atspi_accessible_get_application(node, NULL);
333 LOGI("node:%p, app:%p", node, app);
335 mActivatedApplicationList.remove_if([&](auto &n) { if(n == app) { g_object_unref(app); return true;} else return false; });
336 mActivatedApplicationList.push_front(app);
342 bool AtspiAccessibleWatcher::removeFromWindowSet(AtspiAccessible *node)
344 removeFromActivatedList(node);
345 auto iter = mWindowSet.find(node);
346 if ( iter != mWindowSet.end()){
347 mWindowSet.erase(node);
353 bool AtspiAccessibleWatcher::addToWindowSet(AtspiAccessible *node)
355 auto iter = mWindowSet.find(node);
356 if ( iter == mWindowSet.end()){
357 mWindowSet.insert(node);