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