1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4
5
6#include "dbusconnection_p.h"
7
8#include <QtDBus/QDBusMessage>
9#include <QtDBus/QDBusServiceWatcher>
10#include <qdebug.h>
11
12#include <QDBusConnectionInterface>
13
14#include <QtGui/qguiapplication.h>
15#include <qpa/qplatformnativeinterface.h>
16
17QT_BEGIN_NAMESPACE
18
19using namespace Qt::StringLiterals;
20
21/* note: do not change these to QStringLiteral;
22 we are unloaded before QtDBus is done using the strings.
23 */
24#define A11Y_SERVICE "org.a11y.Bus"_L1
25#define A11Y_PATH "/org/a11y/bus"_L1
26
27/*!
28 \class QAtSpiDBusConnection
29 \internal
30 \brief Connects to the accessibility dbus.
31
32 This is usually a different bus from the session bus.
33*/
34QAtSpiDBusConnection::QAtSpiDBusConnection(QObject *parent)
35 : QObject(parent), m_a11yConnection(QString()), m_enabled(false)
36{
37 // If the bus is explicitly set via env var it overrides everything else.
38 QByteArray addressEnv = qgetenv(varName: "AT_SPI_BUS_ADDRESS");
39 if (!addressEnv.isEmpty()) {
40 m_enabled = true;
41 connectA11yBus(address: QString::fromLocal8Bit(ba: addressEnv));
42 return;
43 }
44
45 // Start monitoring if "org.a11y.Bus" is registered as DBus service.
46 QDBusConnection c = QDBusConnection::sessionBus();
47 if (!c.isConnected()) {
48 return;
49 }
50
51 m_a11yStatus = new OrgA11yStatusInterface(A11Y_SERVICE, A11Y_PATH, c, this);
52 m_dbusProperties = new OrgFreedesktopDBusPropertiesInterface(A11Y_SERVICE, A11Y_PATH, c, this);
53
54 dbusWatcher = new QDBusServiceWatcher(A11Y_SERVICE, c, QDBusServiceWatcher::WatchForRegistration, this);
55 connect(dbusWatcher, &QDBusServiceWatcher::serviceRegistered,
56 this, &QAtSpiDBusConnection::checkEnabledState);
57
58 // If it is registered already, setup a11y right away
59 if (c.interface()->isServiceRegistered(A11Y_SERVICE))
60 checkEnabledState();
61
62 // Subscribe to updates about a11y enabled state.
63 connect(m_dbusProperties, &OrgFreedesktopDBusPropertiesInterface::PropertiesChanged,
64 this, [this](const QString &interface_name) {
65 if (interface_name == QLatin1StringView(OrgA11yStatusInterface::staticInterfaceName()))
66 checkEnabledState();
67 });
68
69 if (QGuiApplication::platformName().startsWith(s: "xcb"_L1)) {
70 // In addition try if there is an xatom exposing the bus address, this allows applications run as root to work
71 QString address = getAddressFromXCB();
72 if (!address.isEmpty()) {
73 m_enabled = true;
74 connectA11yBus(address);
75 }
76 }
77}
78
79QString QAtSpiDBusConnection::getAddressFromXCB()
80{
81 QGuiApplication *app = qobject_cast<QGuiApplication *>(object: QCoreApplication::instance());
82 if (!app)
83 return QString();
84 QPlatformNativeInterface *platformNativeInterface = app->platformNativeInterface();
85 QByteArray *addressByteArray = reinterpret_cast<QByteArray*>(
86 platformNativeInterface->nativeResourceForIntegration(QByteArrayLiteral("AtspiBus")));
87 if (addressByteArray) {
88 QString address = QString::fromLatin1(ba: *addressByteArray);
89 delete addressByteArray;
90 return address;
91 }
92 return QString();
93}
94
95void QAtSpiDBusConnection::checkEnabledState()
96{
97 //The variable was introduced because on some embedded platforms there are custom accessibility
98 //clients which don't set Status.ScreenReaderEnabled to true. The variable is also useful for
99 //debugging.
100 static const bool a11yAlwaysOn = qEnvironmentVariableIsSet(varName: "QT_LINUX_ACCESSIBILITY_ALWAYS_ON");
101
102 bool enabled = a11yAlwaysOn || m_a11yStatus->screenReaderEnabled() || m_a11yStatus->isEnabled();
103
104 if (enabled != m_enabled) {
105 m_enabled = enabled;
106 if (m_a11yConnection.isConnected()) {
107 emit enabledChanged(enabled: m_enabled);
108 } else {
109 QDBusConnection c = QDBusConnection::sessionBus();
110 QDBusMessage m = QDBusMessage::createMethodCall(A11Y_SERVICE, A11Y_PATH, A11Y_SERVICE,
111 method: "GetAddress"_L1);
112 c.callWithCallback(m, this, SLOT(connectA11yBus(QString)), SLOT(dbusError(QDBusError)));
113 }
114 }
115}
116
117void QAtSpiDBusConnection::serviceUnregistered()
118{
119 emit enabledChanged(enabled: false);
120}
121
122void QAtSpiDBusConnection::connectA11yBus(const QString &address)
123{
124 if (address.isEmpty()) {
125 qWarning(msg: "Could not find Accessibility DBus address.");
126 return;
127 }
128 m_a11yConnection = QDBusConnection(QDBusConnection::connectToBus(address, name: "a11y"_L1));
129
130 if (m_enabled)
131 emit enabledChanged(enabled: true);
132}
133
134void QAtSpiDBusConnection::dbusError(const QDBusError &error)
135{
136 qWarning() << "Accessibility encountered a DBus error:" << error;
137}
138
139/*!
140 Returns the DBus connection that got established.
141 Or an invalid connection if not yet connected.
142*/
143QDBusConnection QAtSpiDBusConnection::connection() const
144{
145 return m_a11yConnection;
146}
147
148QT_END_NAMESPACE
149
150#include "moc_dbusconnection_p.cpp"
151

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of qtbase/src/gui/accessible/linux/dbusconnection.cpp