diff --git a/src/platformsupport/linuxaccessibility/atspiadaptor.cpp b/src/platformsupport/linuxaccessibility/atspiadaptor.cpp index 895a5183a0..3d91f883f9 100644 --- a/src/platformsupport/linuxaccessibility/atspiadaptor.cpp +++ b/src/platformsupport/linuxaccessibility/atspiadaptor.cpp @@ -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) diff --git a/src/platformsupport/linuxaccessibility/atspiadaptor_p.h b/src/platformsupport/linuxaccessibility/atspiadaptor_p.h index d282a1ed2e..b7a29fdc15 100644 --- a/src/platformsupport/linuxaccessibility/atspiadaptor_p.h +++ b/src/platformsupport/linuxaccessibility/atspiadaptor_p.h @@ -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 > m_handledObjects; diff --git a/src/platformsupport/linuxaccessibility/bridge.cpp b/src/platformsupport/linuxaccessibility/bridge.cpp index 39560d9509..d42ce8b064 100644 --- a/src/platformsupport/linuxaccessibility/bridge.cpp +++ b/src/platformsupport/linuxaccessibility/bridge.cpp @@ -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 { diff --git a/src/platformsupport/linuxaccessibility/bridge_p.h b/src/platformsupport/linuxaccessibility/bridge_p.h index afc32ac49c..aed437b1e2 100644 --- a/src/platformsupport/linuxaccessibility/bridge_p.h +++ b/src/platformsupport/linuxaccessibility/bridge_p.h @@ -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 diff --git a/src/platformsupport/linuxaccessibility/dbusconnection.cpp b/src/platformsupport/linuxaccessibility/dbusconnection.cpp index c91ad1da39..a37b99c105 100644 --- a/src/platformsupport/linuxaccessibility/dbusconnection.cpp +++ b/src/platformsupport/linuxaccessibility/dbusconnection.cpp @@ -44,10 +44,16 @@ #include "dbusconnection_p.h" #include +#include #include +#include + 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 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 diff --git a/src/platformsupport/linuxaccessibility/dbusconnection_p.h b/src/platformsupport/linuxaccessibility/dbusconnection_p.h index 4fb75bf41f..2875dd89d8 100644 --- a/src/platformsupport/linuxaccessibility/dbusconnection_p.h +++ b/src/platformsupport/linuxaccessibility/dbusconnection_p.h @@ -45,21 +45,41 @@ #include #include +#include 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 diff --git a/tests/auto/other/qaccessibilitylinux/tst_qaccessibilitylinux.cpp b/tests/auto/other/qaccessibilitylinux/tst_qaccessibilitylinux.cpp index fd157dd834..15b8089525 100644 --- a/tests/auto/other/qaccessibilitylinux/tst_qaccessibilitylinux.cpp +++ b/tests/auto/other/qaccessibilitylinux/tst_qaccessibilitylinux.cpp @@ -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() >> list; + const QList args = interface->call(QDBus::Block, "GetChildren").arguments(); + Q_ASSERT(args.size() == 1); + Q_ASSERT(args.first().isValid()); + args.first().value() >> 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()