Accessibility Linux: Make dbus registration async
Change-Id: I74043be04f4ee17089353304fdc007a7f22cdea0 Reviewed-by: Friedemann Kleint <Friedemann.Kleint@digia.com> Reviewed-by: Jan Arve Sæther <jan-arve.saether@digia.com>
This commit is contained in:
parent
ffeaff9a26
commit
15a3243556
@ -69,7 +69,7 @@ static bool isDebugging = false;
|
||||
#define qAtspiDebug if (!::isDebugging); else qDebug
|
||||
|
||||
AtSpiAdaptor::AtSpiAdaptor(DBusConnection *connection, QObject *parent)
|
||||
: QDBusVirtualObject(parent), m_dbus(connection), initialized(false)
|
||||
: QDBusVirtualObject(parent), m_dbus(connection)
|
||||
, sendFocus(0)
|
||||
, sendObject(0)
|
||||
, sendObject_active_descendant_changed(0)
|
||||
@ -132,6 +132,17 @@ AtSpiAdaptor::AtSpiAdaptor(DBusConnection *connection, QObject *parent)
|
||||
|
||||
m_applicationAdaptor = new QSpiApplicationAdaptor(m_dbus->connection(), this);
|
||||
connect(m_applicationAdaptor, SIGNAL(windowActivated(QObject*,bool)), this, SLOT(windowActivated(QObject*,bool)));
|
||||
|
||||
updateEventListeners();
|
||||
bool success = m_dbus->connection().connect(QLatin1String("org.a11y.atspi.Registry"), QLatin1String("/org/a11y/atspi/registry"),
|
||||
QLatin1String("org.a11y.atspi.Registry"), QLatin1String("EventListenerRegistered"), this,
|
||||
SLOT(eventListenerRegistered(QString,QString)));
|
||||
success = success && m_dbus->connection().connect(QLatin1String("org.a11y.atspi.Registry"), QLatin1String("/org/a11y/atspi/registry"),
|
||||
QLatin1String("org.a11y.atspi.Registry"), QLatin1String("EventListenerDeregistered"), this,
|
||||
SLOT(eventListenerDeregistered(QString,QString)));
|
||||
#ifdef QT_ATSPI_DEBUG
|
||||
qAtspiDebug() << "Registered event listener change listener: " << success;
|
||||
#endif
|
||||
}
|
||||
|
||||
AtSpiAdaptor::~AtSpiAdaptor()
|
||||
@ -605,30 +616,6 @@ QString AtSpiAdaptor::introspect(const QString &path) const
|
||||
return xml;
|
||||
}
|
||||
|
||||
/*!
|
||||
When initialized we will send updates, not before this.
|
||||
|
||||
This function also checks which event listeners are registered in the at-spi registry.
|
||||
*/
|
||||
void AtSpiAdaptor::setInitialized(bool init)
|
||||
{
|
||||
initialized = init;
|
||||
|
||||
if (!initialized)
|
||||
return;
|
||||
|
||||
updateEventListeners();
|
||||
bool success = m_dbus->connection().connect(QLatin1String("org.a11y.atspi.Registry"), QLatin1String("/org/a11y/atspi/registry"),
|
||||
QLatin1String("org.a11y.atspi.Registry"), QLatin1String("EventListenerRegistered"), this,
|
||||
SLOT(eventListenerRegistered(QString,QString)));
|
||||
success = success && m_dbus->connection().connect(QLatin1String("org.a11y.atspi.Registry"), QLatin1String("/org/a11y/atspi/registry"),
|
||||
QLatin1String("org.a11y.atspi.Registry"), QLatin1String("EventListenerDeregistered"), this,
|
||||
SLOT(eventListenerDeregistered(QString,QString)));
|
||||
#ifdef QT_ATSPI_DEBUG
|
||||
qAtspiDebug() << "Registered event listener change listener: " << success;
|
||||
#endif
|
||||
}
|
||||
|
||||
void AtSpiAdaptor::setBitFlag(const QString &flag)
|
||||
{
|
||||
Q_ASSERT(flag.size());
|
||||
@ -918,9 +905,6 @@ void AtSpiAdaptor::notifyStateChange(const QAIPointer &interface, const QString
|
||||
*/
|
||||
void AtSpiAdaptor::notify(QAccessibleEvent *event)
|
||||
{
|
||||
if (!initialized)
|
||||
return;
|
||||
|
||||
switch (event->type()) {
|
||||
case QAccessible::ObjectCreated:
|
||||
if (sendObject || sendObject_children_changed)
|
||||
|
@ -75,8 +75,8 @@ public:
|
||||
bool handleMessage(const QDBusMessage &message, const QDBusConnection &connection);
|
||||
void notify(QAccessibleEvent *event);
|
||||
|
||||
void setInitialized(bool init);
|
||||
|
||||
void init();
|
||||
void checkInitializedAndEnabled();
|
||||
public Q_SLOTS:
|
||||
void eventListenerRegistered(const QString &bus, const QString &path);
|
||||
void eventListenerDeregistered(const QString &bus, const QString &path);
|
||||
@ -140,7 +140,6 @@ private:
|
||||
|
||||
/// Assigned from the accessibility registry.
|
||||
int m_applicationId;
|
||||
bool initialized;
|
||||
|
||||
mutable QHash<quintptr, QPointer<QObject> > m_handledObjects;
|
||||
|
||||
|
@ -62,24 +62,16 @@ QT_BEGIN_NAMESPACE
|
||||
*/
|
||||
|
||||
QSpiAccessibleBridge::QSpiAccessibleBridge()
|
||||
: cache(0)
|
||||
: cache(0), dec(0), dbusAdaptor(0), m_enabled(false)
|
||||
{
|
||||
dbusConnection = new DBusConnection();
|
||||
if (!dBusConnection().isConnected())
|
||||
qWarning() << "Could not connect to dbus.";
|
||||
connect(dbusConnection, SIGNAL(enabledChanged(bool)), this, SLOT(enabledChanged(bool)));
|
||||
}
|
||||
|
||||
qSpiInitializeStructTypes();
|
||||
initializeConstantMappings();
|
||||
|
||||
/* Create the cache of accessible objects */
|
||||
cache = new QSpiDBusCache(dBusConnection(), this);
|
||||
dec = new DeviceEventControllerAdaptor(this);
|
||||
|
||||
dBusConnection().registerObject(QLatin1String(ATSPI_DBUS_PATH_DEC), this, QDBusConnection::ExportAdaptors);
|
||||
|
||||
dbusAdaptor = new AtSpiAdaptor(dbusConnection, this);
|
||||
dBusConnection().registerVirtualObject(QLatin1String(QSPI_OBJECT_PATH_ACCESSIBLE), dbusAdaptor, QDBusConnection::SubPath);
|
||||
dbusAdaptor->registerApplication();
|
||||
void QSpiAccessibleBridge::enabledChanged(bool enabled)
|
||||
{
|
||||
m_enabled = enabled;
|
||||
updateStatus();
|
||||
}
|
||||
|
||||
QSpiAccessibleBridge::~QSpiAccessibleBridge()
|
||||
@ -92,15 +84,30 @@ QDBusConnection QSpiAccessibleBridge::dBusConnection() const
|
||||
return dbusConnection->connection();
|
||||
}
|
||||
|
||||
void QSpiAccessibleBridge::setRootObject(QObject *obj)
|
||||
void QSpiAccessibleBridge::updateStatus()
|
||||
{
|
||||
Q_UNUSED(obj);
|
||||
dbusAdaptor->setInitialized(true);
|
||||
// create the adaptor to handle everything if we are in enabled state
|
||||
if (!dbusAdaptor && m_enabled) {
|
||||
qSpiInitializeStructTypes();
|
||||
initializeConstantMappings();
|
||||
|
||||
cache = new QSpiDBusCache(dbusConnection->connection(), this);
|
||||
dec = new DeviceEventControllerAdaptor(this);
|
||||
|
||||
dbusConnection->connection().registerObject(QLatin1String(ATSPI_DBUS_PATH_DEC), this, QDBusConnection::ExportAdaptors);
|
||||
|
||||
dbusAdaptor = new AtSpiAdaptor(dbusConnection, this);
|
||||
dbusConnection->connection().registerVirtualObject(QLatin1String(QSPI_OBJECT_PATH_ACCESSIBLE), dbusAdaptor, QDBusConnection::SubPath);
|
||||
dbusAdaptor->registerApplication();
|
||||
}
|
||||
}
|
||||
|
||||
void QSpiAccessibleBridge::notifyAccessibilityUpdate(QAccessibleEvent *event)
|
||||
{
|
||||
dbusAdaptor->notify(event);
|
||||
if (!dbusAdaptor)
|
||||
return;
|
||||
if (m_enabled)
|
||||
dbusAdaptor->notify(event);
|
||||
}
|
||||
|
||||
struct RoleMapping {
|
||||
|
@ -62,21 +62,22 @@ public:
|
||||
QSpiAccessibleBridge();
|
||||
|
||||
virtual ~QSpiAccessibleBridge();
|
||||
virtual void setRootObject(QObject *obj);
|
||||
|
||||
virtual void notifyAccessibilityUpdate(QAccessibleEvent *event);
|
||||
QDBusConnection dBusConnection() const;
|
||||
|
||||
public Q_SLOTS:
|
||||
void enabledChanged(bool enabled);
|
||||
|
||||
private:
|
||||
void initializeConstantMappings();
|
||||
void updateStatus();
|
||||
|
||||
QSpiDBusCache *cache;
|
||||
DeviceEventControllerAdaptor *dec;
|
||||
|
||||
AtSpiAdaptor *dbusAdaptor;
|
||||
|
||||
DBusConnection* dbusConnection;
|
||||
bool initialized;
|
||||
bool m_enabled;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
@ -44,10 +44,16 @@
|
||||
#include "dbusconnection_p.h"
|
||||
|
||||
#include <QtDBus/QDBusMessage>
|
||||
#include <QtDBus/QDBusServiceWatcher>
|
||||
#include <qdebug.h>
|
||||
|
||||
#include <QDBusConnectionInterface>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
QString A11Y_SERVICE = QStringLiteral("org.a11y.Bus");
|
||||
QString A11Y_PATH = QStringLiteral("/org/a11y/bus");
|
||||
|
||||
/*!
|
||||
\class DBusConnection
|
||||
\internal
|
||||
@ -55,53 +61,81 @@ QT_BEGIN_NAMESPACE
|
||||
|
||||
This is usually a different bus from the session bus.
|
||||
*/
|
||||
DBusConnection::DBusConnection()
|
||||
: dbusConnection(connectDBus())
|
||||
{}
|
||||
|
||||
QDBusConnection DBusConnection::connectDBus()
|
||||
DBusConnection::DBusConnection(QObject *parent)
|
||||
: QObject(parent), m_a11yConnection(QString()), m_enabled(false)
|
||||
{
|
||||
QString address = getAccessibilityBusAddress();
|
||||
|
||||
if (!address.isEmpty()) {
|
||||
QDBusConnection c = QDBusConnection::connectToBus(address, QStringLiteral("a11y"));
|
||||
if (c.isConnected())
|
||||
return c;
|
||||
qWarning("Found Accessibility DBus address but cannot connect. Falling back to session bus.");
|
||||
} else {
|
||||
qWarning("Accessibility DBus not found. Falling back to session bus.");
|
||||
}
|
||||
|
||||
// Start monitoring if "org.a11y.Bus" is registered as DBus service.
|
||||
QDBusConnection c = QDBusConnection::sessionBus();
|
||||
if (!c.isConnected()) {
|
||||
qWarning("Could not connect to DBus.");
|
||||
}
|
||||
return QDBusConnection::sessionBus();
|
||||
dbusWatcher = new QDBusServiceWatcher(A11Y_SERVICE, c, QDBusServiceWatcher::WatchForRegistration, this);
|
||||
connect(dbusWatcher, SIGNAL(serviceRegistered(QString)), this, SLOT(serviceRegistered()));
|
||||
|
||||
// If it is registered already, setup a11y right away
|
||||
if (c.interface()->isServiceRegistered(A11Y_SERVICE))
|
||||
serviceRegistered();
|
||||
}
|
||||
|
||||
QString DBusConnection::getAccessibilityBusAddress() const
|
||||
// We have the a11y registry on the session bus.
|
||||
// Subscribe to updates about a11y enabled state.
|
||||
// Find out the bus address
|
||||
void DBusConnection::serviceRegistered()
|
||||
{
|
||||
// listen to enabled changes
|
||||
QDBusConnection c = QDBusConnection::sessionBus();
|
||||
// FXIME check for changes of enabled state
|
||||
// if (!c.connect(A11Y_SERVICE, A11Y_PATH, QStringLiteral("org.freedesktop.DBus.Properties"), QStringLiteral("PropertiesChanged"), this, SLOT(enabledStateChanged(QDBusVariant))))
|
||||
// qWarning() << "Could not listen to accessibility enabled state changes.";
|
||||
|
||||
QDBusMessage m = QDBusMessage::createMethodCall(QLatin1String("org.a11y.Bus"),
|
||||
QLatin1String("/org/a11y/bus"),
|
||||
QLatin1String("org.a11y.Bus"), QLatin1String("GetAddress"));
|
||||
QDBusMessage reply = c.call(m);
|
||||
if (reply.type() == QDBusMessage::ErrorMessage) {
|
||||
qWarning() << "Qt at-spi: error getting the accessibility dbus address: " << reply.errorMessage();
|
||||
return QString();
|
||||
// check if it's enabled right away
|
||||
QDBusMessage enabledMessage = QDBusMessage::createMethodCall(A11Y_SERVICE, A11Y_PATH, QStringLiteral("org.freedesktop.DBus.Properties"), QStringLiteral("Get"));
|
||||
QList<QVariant> args;
|
||||
args << QStringLiteral("org.a11y.Status") << QStringLiteral("IsEnabled");
|
||||
enabledMessage.setArguments(args);
|
||||
c.callWithCallback(enabledMessage, this, SLOT(enabledStateCallback(QDBusVariant)), SLOT(dbusError(QDBusError)));
|
||||
}
|
||||
|
||||
void DBusConnection::dbusError(const QDBusError &error)
|
||||
{
|
||||
qWarning() << "Accessibility encountered a DBus error:" << error;
|
||||
}
|
||||
|
||||
void DBusConnection::serviceUnregistered()
|
||||
{
|
||||
emit enabledChanged(false);
|
||||
}
|
||||
|
||||
void DBusConnection::enabledStateCallback(const QDBusVariant &enabled)
|
||||
{
|
||||
m_enabled = enabled.variant().toBool();
|
||||
if (m_a11yConnection.isConnected()) {
|
||||
emit enabledChanged(m_enabled);
|
||||
} else {
|
||||
QDBusConnection c = QDBusConnection::sessionBus();
|
||||
QDBusMessage m = QDBusMessage::createMethodCall(QLatin1String("org.a11y.Bus"),
|
||||
QLatin1String("/org/a11y/bus"),
|
||||
QLatin1String("org.a11y.Bus"), QLatin1String("GetAddress"));
|
||||
c.callWithCallback(m, this, SLOT(connectA11yBus(QString)), SLOT(dbusError(QDBusError)));
|
||||
}
|
||||
}
|
||||
|
||||
QString busAddress = reply.arguments().at(0).toString();
|
||||
return busAddress;
|
||||
void DBusConnection::connectA11yBus(const QString &address)
|
||||
{
|
||||
if (address.isEmpty()) {
|
||||
qWarning("Could not find Accessibility DBus address.");
|
||||
return;
|
||||
}
|
||||
m_a11yConnection = QDBusConnection(QDBusConnection::connectToBus(address, QStringLiteral("a11y")));
|
||||
|
||||
if (m_enabled)
|
||||
emit enabledChanged(true);
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the DBus connection that got established.
|
||||
Or an invalid connection if not yet connected.
|
||||
*/
|
||||
QDBusConnection DBusConnection::connection() const
|
||||
{
|
||||
return dbusConnection;
|
||||
return m_a11yConnection;
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
@ -45,21 +45,41 @@
|
||||
|
||||
#include <QtCore/QString>
|
||||
#include <QtDBus/QDBusConnection>
|
||||
#include <QtDBus/QDBusVariant>
|
||||
|
||||
QT_BEGIN_HEADER
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class DBusConnection
|
||||
class QDBusServiceWatcher;
|
||||
|
||||
class DBusConnection : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
DBusConnection();
|
||||
DBusConnection(QObject *parent = 0);
|
||||
QDBusConnection connection() const;
|
||||
bool isEnabled() const { return m_enabled; }
|
||||
|
||||
Q_SIGNALS:
|
||||
// Emitted when the global accessibility status changes to enabled
|
||||
void enabledChanged(bool enabled);
|
||||
|
||||
private Q_SLOTS:
|
||||
void serviceRegistered();
|
||||
void serviceUnregistered();
|
||||
void enabledStateCallback(const QDBusVariant &enabled);
|
||||
// void enabledStateChanged(const QDBusVariant &);
|
||||
void connectA11yBus(const QString &address);
|
||||
|
||||
void dbusError(const QDBusError &error);
|
||||
|
||||
private:
|
||||
QString getAccessibilityBusAddress() const;
|
||||
QDBusConnection connectDBus();
|
||||
|
||||
QDBusConnection dbusConnection;
|
||||
QDBusServiceWatcher *dbusWatcher;
|
||||
QDBusConnection m_a11yConnection;
|
||||
bool m_enabled;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
@ -67,11 +67,8 @@ class AccessibleTestWindow : public QWidget
|
||||
public:
|
||||
AccessibleTestWindow()
|
||||
{
|
||||
DBusConnection c;
|
||||
m_address = c.connection().baseService().toLatin1().data();
|
||||
new QHBoxLayout(this);
|
||||
}
|
||||
QString dbusAddress() const { return m_address; }
|
||||
|
||||
void addWidget(QWidget* widget)
|
||||
{
|
||||
@ -85,10 +82,6 @@ public:
|
||||
qDeleteAll(children());
|
||||
new QHBoxLayout(this);
|
||||
}
|
||||
|
||||
private:
|
||||
QString m_address;
|
||||
QString m_bus;
|
||||
};
|
||||
|
||||
|
||||
@ -116,7 +109,6 @@ private:
|
||||
|
||||
AccessibleTestWindow *m_window;
|
||||
|
||||
QString bus;
|
||||
QString address;
|
||||
QDBusInterface *root; // the root object on dbus (for the app)
|
||||
QDBusInterface *rootApplication;
|
||||
@ -129,10 +121,12 @@ private:
|
||||
QStringList tst_QAccessibilityLinux::getChildren(QDBusInterface *interface)
|
||||
{
|
||||
QSpiObjectReferenceArray list;
|
||||
interface->call(QDBus::Block, "GetChildren").arguments().first().value<QDBusArgument>() >> list;
|
||||
const QList<QVariant> args = interface->call(QDBus::Block, "GetChildren").arguments();
|
||||
Q_ASSERT(args.size() == 1);
|
||||
Q_ASSERT(args.first().isValid());
|
||||
args.first().value<QDBusArgument>() >> list;
|
||||
|
||||
Q_ASSERT(interface->property("ChildCount").toInt() == list.count());
|
||||
|
||||
QStringList children;
|
||||
Q_FOREACH (const QSpiObjectReference &ref, list)
|
||||
children << ref.path.path();
|
||||
@ -164,26 +158,18 @@ QDBusInterface *tst_QAccessibilityLinux::getInterface(const QString &path, const
|
||||
void tst_QAccessibilityLinux::initTestCase()
|
||||
{
|
||||
// Oxygen style creates many extra items, it's simply unusable here
|
||||
qDebug() << "Using fusion style...";
|
||||
qApp->setStyle("fusion");
|
||||
qApp->setApplicationName("tst_QAccessibilityLinux app");
|
||||
dbus = DBusConnection();
|
||||
|
||||
QTRY_VERIFY(dbus.isEnabled());
|
||||
QTRY_VERIFY(dbus.connection().isConnected());
|
||||
address = dbus.connection().baseService().toLatin1().data();
|
||||
|
||||
m_window = new AccessibleTestWindow();
|
||||
m_window->show();
|
||||
|
||||
// this has the side-effect of immediately activating accessibility
|
||||
qDebug() << "Explicitly activating accessibility...";
|
||||
delete QAccessible::queryAccessibleInterface(m_window);
|
||||
|
||||
QTest::qWaitForWindowExposed(m_window);
|
||||
|
||||
address = m_window->dbusAddress();
|
||||
registerDbus();
|
||||
|
||||
QStringList appChildren = getChildren(root);
|
||||
QString window = appChildren.at(0);
|
||||
mainWindow = getInterface(window, "org.a11y.atspi.Accessible");
|
||||
}
|
||||
|
||||
void tst_QAccessibilityLinux::cleanupTestCase()
|
||||
|
Loading…
Reference in New Issue
Block a user