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{};
41 static bool iShowingNode(AtspiAccessible *node)
44 if (node) name = AtspiWrapper::Atspi_accessible_get_name(node, NULL);
47 LOGI("isShowing %s", name);
48 auto stateSet = AtspiWrapper::Atspi_accessible_get_state_set(node);
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);
54 g_object_unref(stateSet);
58 g_object_unref(stateSet);
62 static std::vector<AtspiAccessible *>
63 findActiveNode(AtspiAccessible *node, int depth,
66 LOGI("findActiveNode %p %d/%d", node, depth, max_depth);
68 std::vector<AtspiAccessible *> ret{};
70 if (iShowingNode(node)) {
71 char *name = AtspiWrapper::Atspi_accessible_get_name(node, NULL);
80 if (depth >= max_depth) return ret;
82 int nchild = AtspiWrapper::Atspi_accessible_get_child_count(node, NULL);
83 if (nchild <= 0) return ret;
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);
97 static gpointer _event_thread_loop (gpointer data)
99 LOGI("event thread start");
100 AtspiEventListener *listener =
101 atspi_event_listener_new(AtspiAccessibleWatcher::onAtspiEvents, data, NULL);
103 atspi_event_listener_register(listener, "window:", NULL);
104 atspi_event_listener_register(listener, "object:", NULL);
108 LOGI("event thread end");
109 atspi_event_listener_deregister(listener, "object:", NULL);
110 atspi_event_listener_deregister(listener, "window:", NULL);
112 g_object_unref(listener);
116 AtspiAccessibleWatcher::AtspiAccessibleWatcher()
117 : mDbusProxy{nullptr}
119 GVariant *result = nullptr;
120 GError *error = nullptr;
122 atspi_set_main_context (g_main_context_default ());
125 mEventThread = g_thread_new("AtspiEventThread", _event_thread_loop, this);
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",
133 result = g_dbus_proxy_call_sync(
135 g_variant_new("(ssv)", "org.a11y.Status", "IsEnabled", g_variant_new_boolean(true)),
136 G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
138 g_variant_unref(result);
139 if (error) g_error_free(error);
142 AtspiAccessibleWatcher::~AtspiAccessibleWatcher()
144 GVariant *result = nullptr;
145 GError *error = nullptr;
147 result = g_dbus_proxy_call_sync(
149 g_variant_new("(ssv)", "org.a11y.Status", "IsEnabled", g_variant_new_boolean(false)),
150 G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
152 g_object_unref(mDbusProxy);
153 g_variant_unref(result);
154 if (error) g_error_free(error);
157 g_thread_join(mEventThread);
161 void AtspiAccessibleWatcher::onAtspiEvents(AtspiEvent *event, void *watcher)
167 char *name = NULL, *pkg = NULL;
168 AtspiAccessibleWatcher *instance = (AtspiAccessibleWatcher *)watcher;
169 name = AtspiWrapper::Atspi_accessible_get_name(event->source, NULL);
171 AtspiAccessible *app = AtspiWrapper::Atspi_accessible_get_application(event->source, NULL);
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))));
183 LOGI("app(%s) is already in map", pkg);
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);
193 LOGE("deactivated window's app(%s) is not in map", pkg);
203 mEventQueue.push_back(std::make_shared<A11yEventInfo>(std::string(event->type), std::string(name), std::string(pkg)));
206 if (!strcmp(event->type, "object:state-changed:defunct")) {
207 instance->onObjectDefunct(
208 static_cast<AtspiAccessible *>(event->source));
210 if (name) free(name);
214 void AtspiAccessibleWatcher::onObjectDefunct(AtspiAccessible *node)
216 LOGI("onObjectDefunct obj:%p", node);
217 notifyAll((int)EventType::Object, (int)ObjectEventType::ObjectStateDefunct, node);
220 int AtspiAccessibleWatcher::getApplicationCount(void) const
222 AtspiAccessible *root = AtspiWrapper::Atspi_get_desktop(0);
223 int nchild = AtspiWrapper::Atspi_accessible_get_child_count(root, NULL);
224 g_object_unref(root);
226 if (nchild <= 0) return 0;
230 std::shared_ptr<AccessibleApplication> AtspiAccessibleWatcher::getApplicationAt(int index) const
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));
238 std::vector<std::shared_ptr<AccessibleApplication>> AtspiAccessibleWatcher::getApplications(void) const
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);
244 g_object_unref(root);
248 for (int i = 0; i < nchild; i++){
249 AtspiAccessible *child = AtspiWrapper::Atspi_accessible_get_child_at_index(root, i, NULL);
251 ret.push_back(std::make_shared<AtspiAccessibleApplication>(std::make_shared<AtspiAccessibleNode>(child)));
254 g_object_unref(root);
258 bool AtspiAccessibleWatcher::executeAndWaitForEvents(const Runnable *cmd, const A11yEvent type, const int timeout)
266 std::chrono::system_clock::time_point start =
267 std::chrono::system_clock::now();
270 std::vector<std::shared_ptr<A11yEventInfo>> localEvents;
272 localEvents.assign(mEventQueue.begin(), mEventQueue.end());
276 if (!localEvents.empty())
278 for (const auto &event : localEvents) {
279 if (COMPARE(type, event->getEvent()))
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());
286 if ((std::chrono::system_clock::now() - start) >
287 std::chrono::milliseconds{timeout})
289 std::this_thread::sleep_for(
290 std::chrono::milliseconds{100});
296 std::map<AtspiAccessible *, std::shared_ptr<AccessibleApplication>> AtspiAccessibleWatcher::getActiveAppMap(void)
298 return mActiveAppMap;
301 bool AtspiAccessibleWatcher::removeFromActivatedList(AtspiAccessible *node)
303 LOGI("remove from activelist node %p", node);
304 mActivatedWindowList.remove_if([&](auto &n) { return n == node; });
306 AtspiAccessible *app = AtspiWrapper::Atspi_accessible_get_application(node, NULL);
307 LOGI("node:%p, app:%p", node, app);
309 mActivatedApplicationList.remove_if([&](auto &n) { return n == app; });
315 bool AtspiAccessibleWatcher::addToActivatedList(AtspiAccessible *node)
317 LOGI("add to activelist node %p", node);
318 mActivatedWindowList.remove_if([&](auto &n) { return n == node; });
319 mActivatedWindowList.push_front(node);
321 auto iter = mWindowSet.find(node);
322 if ( iter == mWindowSet.end()) mWindowSet.insert(node);
324 AtspiAccessible *app = AtspiWrapper::Atspi_accessible_get_application(node, NULL);
325 LOGI("node:%p, app:%p", node, app);
327 mActivatedApplicationList.remove_if([&](auto &n) { if(n == app) { g_object_unref(app); return true;} else return false; });
328 mActivatedApplicationList.push_front(app);
334 bool AtspiAccessibleWatcher::removeFromWindowSet(AtspiAccessible *node)
336 removeFromActivatedList(node);
337 auto iter = mWindowSet.find(node);
338 if ( iter != mWindowSet.end()){
339 mWindowSet.erase(node);
345 bool AtspiAccessibleWatcher::addToWindowSet(AtspiAccessible *node)
347 auto iter = mWindowSet.find(node);
348 if ( iter == mWindowSet.end()){
349 mWindowSet.insert(node);