Let platform plugin decide if accessibility is active

Change-Id: I881a8ff3fedf3db73ee37046a4363c70960a92a6
Reviewed-by: Jan Arve Sæther <jan-arve.saether@digia.com>
This commit is contained in:
Frederik Gladhorn 2013-03-25 18:07:17 +01:00 committed by The Qt Project
parent c2059ac80d
commit 0d57da067b
16 changed files with 93 additions and 57 deletions

View File

@ -2,7 +2,7 @@
DBUS_ADAPTORS = $$PWD/xml/Cache.xml $$PWD/xml/DeviceEventController.xml DBUS_ADAPTORS = $$PWD/xml/Cache.xml $$PWD/xml/DeviceEventController.xml
QDBUSXML2CPP_ADAPTOR_HEADER_FLAGS = -i struct_marshallers_p.h QDBUSXML2CPP_ADAPTOR_HEADER_FLAGS = -i struct_marshallers_p.h
DBUS_INTERFACES = $$PWD/xml/Socket.xml DBUS_INTERFACES = $$PWD/xml/Socket.xml $$PWD/xml/Bus.xml
QDBUSXML2CPP_INTERFACE_HEADER_FLAGS = -i struct_marshallers_p.h QDBUSXML2CPP_INTERFACE_HEADER_FLAGS = -i struct_marshallers_p.h
INCLUDEPATH += $$PWD INCLUDEPATH += $$PWD

17
src/3rdparty/atspi2/xml/Bus.xml vendored Normal file
View File

@ -0,0 +1,17 @@
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="org.a11y.Status">
<property type="b" name="IsEnabled" access="readwrite">
</property>
<property type="b" name="ScreenReaderEnabled" access="readwrite">
</property>
</interface>
<interface name="org.a11y.Bus">
<method name="GetAddress">
<arg type="s" name="address" direction="out">
</arg>
</method>
</interface>
</node>

View File

@ -441,7 +441,6 @@ Q_GLOBAL_STATIC(QAccessiblePluginsHash, qAccessiblePlugins);
QAccessible::UpdateHandler QAccessible::updateHandler = 0; QAccessible::UpdateHandler QAccessible::updateHandler = 0;
QAccessible::RootObjectHandler QAccessible::rootObjectHandler = 0; QAccessible::RootObjectHandler QAccessible::rootObjectHandler = 0;
static bool accessibility_active = false;
static bool cleanupAdded = false; static bool cleanupAdded = false;
#ifndef QT_NO_ACCESSIBILITY #ifndef QT_NO_ACCESSIBILITY
@ -584,7 +583,6 @@ Q_GLOBAL_STATIC(QAccessibleCache, qAccessibleCache)
*/ */
QAccessibleInterface *QAccessible::queryAccessibleInterface(QObject *object) QAccessibleInterface *QAccessible::queryAccessibleInterface(QObject *object)
{ {
accessibility_active = true;
if (!object) if (!object)
return 0; return 0;
@ -699,19 +697,26 @@ QAccessibleInterface *QAccessible::accessibleInterface(Id id)
/*! /*!
Returns true if an accessibility implementation has been requested Returns true if the platform requested accessibility information.
during the runtime of the application; otherwise returns false.
Use this function to prevent potentially expensive notifications via This function will return false until a tool such as a screen reader
updateAccessibility(). accessed the accessibility framework. It is still possible to use
\l QAccessible::queryAccessibleInterface even if accessibility is not
active. But there will be no notifications sent to the platform.
It is recommended to use this function to prevent expensive notifications
via updateAccessibility() when they are not needed.
*/ */
bool QAccessible::isActive() bool QAccessible::isActive()
{ {
return accessibility_active; #ifndef QT_NO_ACCESSIBILITY
if (QPlatformAccessibility *pfAccessibility = platformAccessibility())
return pfAccessibility->isActive();
#endif
return false;
} }
/*! /*!
Sets the root object of the accessible objects of this application Sets the root object of the accessible objects of this application
to \a object. All other accessible objects are reachable using object to \a object. All other accessible objects are reachable using object

View File

@ -73,6 +73,7 @@ Q_GLOBAL_STATIC(QVector<QAccessibleBridge *>, bridges)
\sa QAccessible \sa QAccessible
*/ */
QPlatformAccessibility::QPlatformAccessibility() QPlatformAccessibility::QPlatformAccessibility()
: m_active(false)
{ {
} }

View File

@ -69,6 +69,11 @@ public:
virtual void initialize(); virtual void initialize();
virtual void cleanup(); virtual void cleanup();
inline bool isActive() const { return m_active; }
inline void setActive(bool active) { m_active = active; }
private:
bool m_active;
}; };
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -62,7 +62,7 @@ QT_BEGIN_NAMESPACE
*/ */
QSpiAccessibleBridge::QSpiAccessibleBridge() QSpiAccessibleBridge::QSpiAccessibleBridge()
: cache(0), dec(0), dbusAdaptor(0), m_enabled(false) : cache(0), dec(0), dbusAdaptor(0)
{ {
dbusConnection = new DBusConnection(); dbusConnection = new DBusConnection();
connect(dbusConnection, SIGNAL(enabledChanged(bool)), this, SLOT(enabledChanged(bool))); connect(dbusConnection, SIGNAL(enabledChanged(bool)), this, SLOT(enabledChanged(bool)));
@ -70,7 +70,7 @@ QSpiAccessibleBridge::QSpiAccessibleBridge()
void QSpiAccessibleBridge::enabledChanged(bool enabled) void QSpiAccessibleBridge::enabledChanged(bool enabled)
{ {
m_enabled = enabled; setActive(enabled);
updateStatus(); updateStatus();
} }
@ -87,7 +87,7 @@ QDBusConnection QSpiAccessibleBridge::dBusConnection() const
void QSpiAccessibleBridge::updateStatus() void QSpiAccessibleBridge::updateStatus()
{ {
// create the adaptor to handle everything if we are in enabled state // create the adaptor to handle everything if we are in enabled state
if (!dbusAdaptor && m_enabled) { if (!dbusAdaptor && isActive()) {
qSpiInitializeStructTypes(); qSpiInitializeStructTypes();
initializeConstantMappings(); initializeConstantMappings();
@ -106,7 +106,7 @@ void QSpiAccessibleBridge::notifyAccessibilityUpdate(QAccessibleEvent *event)
{ {
if (!dbusAdaptor) if (!dbusAdaptor)
return; return;
if (m_enabled) if (isActive())
dbusAdaptor->notify(event); dbusAdaptor->notify(event);
} }

View File

@ -76,7 +76,6 @@ private:
DeviceEventControllerAdaptor *dec; DeviceEventControllerAdaptor *dec;
AtSpiAdaptor *dbusAdaptor; AtSpiAdaptor *dbusAdaptor;
DBusConnection* dbusConnection; DBusConnection* dbusConnection;
bool m_enabled;
}; };
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -48,6 +48,7 @@
#include <qdebug.h> #include <qdebug.h>
#include <QDBusConnectionInterface> #include <QDBusConnectionInterface>
#include "bus_interface.h"
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
@ -81,31 +82,12 @@ void DBusConnection::serviceRegistered()
{ {
// listen to enabled changes // listen to enabled changes
QDBusConnection c = QDBusConnection::sessionBus(); QDBusConnection c = QDBusConnection::sessionBus();
// FXIME check for changes of enabled state OrgA11yStatusInterface *a11yStatus = new OrgA11yStatusInterface(A11Y_SERVICE, A11Y_PATH, c, this);
// 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.";
// check if it's enabled right away // a11yStatus->isEnabled() returns always true (since Gnome 3.6)
QDBusMessage enabledMessage = QDBusMessage::createMethodCall(A11Y_SERVICE, A11Y_PATH, QStringLiteral("org.freedesktop.DBus.Properties"), QStringLiteral("Get")); bool enabled = a11yStatus->screenReaderEnabled();
QList<QVariant> args; if (enabled != m_enabled) {
args << QStringLiteral("org.a11y.Status") << QStringLiteral("IsEnabled"); m_enabled = enabled;
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()) { if (m_a11yConnection.isConnected()) {
emit enabledChanged(m_enabled); emit enabledChanged(m_enabled);
} else { } else {
@ -117,6 +99,14 @@ void DBusConnection::enabledStateCallback(const QDBusVariant &enabled)
} }
} }
// connect(a11yStatus, ); QtDbus doesn't support notifications for property changes yet
}
void DBusConnection::serviceUnregistered()
{
emit enabledChanged(false);
}
void DBusConnection::connectA11yBus(const QString &address) void DBusConnection::connectA11yBus(const QString &address)
{ {
if (address.isEmpty()) { if (address.isEmpty()) {
@ -129,6 +119,11 @@ void DBusConnection::connectA11yBus(const QString &address)
emit enabledChanged(true); emit enabledChanged(true);
} }
void DBusConnection::dbusError(const QDBusError &error)
{
qWarning() << "Accessibility encountered a DBus error:" << error;
}
/*! /*!
Returns the DBus connection that got established. Returns the DBus connection that got established.
Or an invalid connection if not yet connected. Or an invalid connection if not yet connected.

View File

@ -67,8 +67,6 @@ Q_SIGNALS:
private Q_SLOTS: private Q_SLOTS:
void serviceRegistered(); void serviceRegistered();
void serviceUnregistered(); void serviceUnregistered();
void enabledStateCallback(const QDBusVariant &enabled);
// void enabledStateChanged(const QDBusVariant &);
void connectA11yBus(const QString &address); void connectA11yBus(const QString &address);
void dbusError(const QDBusError &error); void dbusError(const QDBusError &error);

View File

@ -63,7 +63,6 @@ QT_END_NAMESPACE
QWindow *m_window; QWindow *m_window;
QCocoaWindow *m_platformWindow; QCocoaWindow *m_platformWindow;
Qt::MouseButtons m_buttons; Qt::MouseButtons m_buttons;
QAccessibleInterface *m_accessibleRoot;
QString m_composingText; QString m_composingText;
bool m_sendKeyEvent; bool m_sendKeyEvent;
QStringList *currentCustomDragTypes; QStringList *currentCustomDragTypes;

View File

@ -121,7 +121,6 @@ static QTouchDevice *touchDevice = 0;
m_window = window; m_window = window;
m_platformWindow = platformWindow; m_platformWindow = platformWindow;
m_accessibleRoot = 0;
m_sendKeyEvent = false; m_sendKeyEvent = false;
#ifdef QT_COCOA_ENABLE_ACCESSIBILITY_INSPECTOR #ifdef QT_COCOA_ENABLE_ACCESSIBILITY_INSPECTOR
@ -130,15 +129,13 @@ static QTouchDevice *touchDevice = 0;
static bool skipAccessibilityForInspectorWindows = false; static bool skipAccessibilityForInspectorWindows = false;
if (!skipAccessibilityForInspectorWindows) { if (!skipAccessibilityForInspectorWindows) {
m_accessibleRoot = window->accessibleRoot(); // m_accessibleRoot = window->accessibleRoot();
AccessibilityInspector *inspector = new AccessibilityInspector(window); AccessibilityInspector *inspector = new AccessibilityInspector(window);
skipAccessibilityForInspectorWindows = true; skipAccessibilityForInspectorWindows = true;
inspector->inspectWindow(window); inspector->inspectWindow(window);
skipAccessibilityForInspectorWindows = false; skipAccessibilityForInspectorWindows = false;
} }
#else
m_accessibleRoot = window->accessibleRoot();
#endif #endif
[self registerDragTypes]; [self registerDragTypes];

View File

@ -45,6 +45,7 @@
#include "qcocoahelpers.h" #include "qcocoahelpers.h"
#include "qcocoaaccessibility.h" #include "qcocoaaccessibility.h"
#include "qcocoaaccessibilityelement.h" #include "qcocoaaccessibilityelement.h"
#include <qpa/qplatformintegration.h>
#include <QtGui/private/qaccessible2_p.h> #include <QtGui/private/qaccessible2_p.h>
#include <QtCore/QDebug> #include <QtCore/QDebug>
@ -60,22 +61,26 @@
} }
- (id)accessibilityAttributeValue:(NSString *)attribute { - (id)accessibilityAttributeValue:(NSString *)attribute {
// activate accessibility updates
QGuiApplicationPrivate::platformIntegration()->accessibility()->setActive(true);
if ([attribute isEqualToString:NSAccessibilityRoleAttribute]) { if ([attribute isEqualToString:NSAccessibilityRoleAttribute]) {
if (m_accessibleRoot) if (m_window->accessibleRoot())
return QCocoaAccessible::macRole(m_accessibleRoot); return QCocoaAccessible::macRole(m_window->accessibleRoot());
return NSAccessibilityUnknownRole; return NSAccessibilityUnknownRole;
} else if ([attribute isEqualToString:NSAccessibilityRoleDescriptionAttribute]) { } else if ([attribute isEqualToString:NSAccessibilityRoleDescriptionAttribute]) {
return NSAccessibilityRoleDescriptionForUIElement(self); return NSAccessibilityRoleDescriptionForUIElement(self);
} else if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) { } else if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) {
if (!m_accessibleRoot) if (!m_window->accessibleRoot())
return [super accessibilityAttributeValue:attribute]; return [super accessibilityAttributeValue:attribute];
// Create QCocoaAccessibleElements for each child if the // Create QCocoaAccessibleElements for each child if the
// root accessible interface. // root accessible interface.
int numKids = m_accessibleRoot->childCount(); int numKids = m_window->accessibleRoot()->childCount();
NSMutableArray *kids = [NSMutableArray arrayWithCapacity:numKids]; NSMutableArray *kids = [NSMutableArray arrayWithCapacity:numKids];
for (int i = 0; i < numKids; ++i) { for (int i = 0; i < numKids; ++i) {
QAccessibleInterface *child = m_accessibleRoot->child(i); QAccessibleInterface *child = m_window->accessibleRoot()->child(i);
Q_ASSERT(child); Q_ASSERT(child);
QAccessible::Id childAxid = QAccessible::uniqueId(child); QAccessible::Id childAxid = QAccessible::uniqueId(child);
QCocoaAccessibleElement *element = [QCocoaAccessibleElement createElementWithId:childAxid parent:self]; QCocoaAccessibleElement *element = [QCocoaAccessibleElement createElementWithId:childAxid parent:self];
@ -90,10 +95,10 @@
} }
- (id)accessibilityHitTest:(NSPoint)point { - (id)accessibilityHitTest:(NSPoint)point {
if (!m_accessibleRoot) if (!m_window->accessibleRoot())
return [super accessibilityHitTest:point]; return [super accessibilityHitTest:point];
QAccessibleInterface *childInterface = m_accessibleRoot->childAt(point.x, qt_mac_flipYCoordinate(point.y)); QAccessibleInterface *childInterface = m_window->accessibleRoot()->childAt(point.x, qt_mac_flipYCoordinate(point.y));
// No child found, meaning we hit the NSView // No child found, meaning we hit the NSView
if (!childInterface) { if (!childInterface) {
return [super accessibilityHitTest:point]; return [super accessibilityHitTest:point];

View File

@ -52,7 +52,9 @@
#include <QtCore/qsettings.h> #include <QtCore/qsettings.h>
#include <QtGui/qaccessible.h> #include <QtGui/qaccessible.h>
#include <QtGui/private/qaccessible2_p.h> #include <QtGui/private/qaccessible2_p.h>
#include <QtGui/private/qguiapplication_p.h>
#include <qpa/qplatformnativeinterface.h> #include <qpa/qplatformnativeinterface.h>
#include <qpa/qplatformintegration.h>
#include <QtGui/qwindow.h> #include <QtGui/qwindow.h>
#include <QtGui/qguiapplication.h> #include <QtGui/qguiapplication.h>
@ -245,6 +247,8 @@ bool QWindowsAccessibility::handleAccessibleObjectFromWindowRequest(HWND hwnd, W
if (static_cast<long>(lParam) == static_cast<long>(UiaRootObjectId)) { if (static_cast<long>(lParam) == static_cast<long>(UiaRootObjectId)) {
/* For UI Automation */ /* For UI Automation */
} else if ((DWORD)lParam == DWORD(OBJID_CLIENT)) { } else if ((DWORD)lParam == DWORD(OBJID_CLIENT)) {
// Start handling accessibility internally
QGuiApplicationPrivate::platformIntegration()->accessibility()->setActive(true);
#if 1 #if 1
// Ignoring all requests while starting up // Ignoring all requests while starting up
// ### Maybe QPA takes care of this??? // ### Maybe QPA takes care of this???

View File

@ -1,7 +1,7 @@
CONFIG += testcase CONFIG += testcase
TARGET = tst_qaccessibility TARGET = tst_qaccessibility
requires(contains(QT_CONFIG,accessibility)) requires(contains(QT_CONFIG,accessibility))
QT += testlib gui-private widgets-private QT += testlib core-private gui-private widgets-private
SOURCES += tst_qaccessibility.cpp SOURCES += tst_qaccessibility.cpp
unix:!mac:LIBS+=-lm unix:!mac:LIBS+=-lm

View File

@ -63,6 +63,9 @@
#include <QtWidgets/private/qaccessiblewidget_p.h> #include <QtWidgets/private/qaccessiblewidget_p.h>
#include <math.h> #include <math.h>
#include <qpa/qplatformnativeinterface.h> #include <qpa/qplatformnativeinterface.h>
#include <qpa/qplatformintegration.h>
#include <qpa/qplatformaccessibility.h>
#include <QtGui/private/qguiapplication_p.h>
#if defined(Q_OS_WIN) && defined(interface) #if defined(Q_OS_WIN) && defined(interface)
# undef interface # undef interface
@ -311,6 +314,8 @@ void tst_QAccessibility::onClicked()
void tst_QAccessibility::initTestCase() void tst_QAccessibility::initTestCase()
{ {
QTestAccessibility::initialize(); QTestAccessibility::initialize();
QPlatformIntegration *pfIntegration = QGuiApplicationPrivate::platformIntegration();
pfIntegration->accessibility()->setActive(true);
} }
void tst_QAccessibility::cleanupTestCase() void tst_QAccessibility::cleanupTestCase()

View File

@ -55,6 +55,7 @@
#include <QDBusReply> #include <QDBusReply>
#include "atspi/atspi-constants.h" #include "atspi/atspi-constants.h"
#include "bus_interface.h"
#include "dbusconnection_p.h" #include "dbusconnection_p.h"
#include "struct_marshallers_p.h" #include "struct_marshallers_p.h"
@ -154,16 +155,21 @@ QDBusInterface *tst_QAccessibilityLinux::getInterface(const QString &path, const
return new QDBusInterface(address, path, interfaceName, dbus.connection(), this); return new QDBusInterface(address, path, interfaceName, dbus.connection(), this);
} }
void tst_QAccessibilityLinux::initTestCase() void tst_QAccessibilityLinux::initTestCase()
{ {
// Oxygen style creates many extra items, it's simply unusable here // Oxygen style creates many extra items, it's simply unusable here
qApp->setStyle("fusion"); qApp->setStyle("fusion");
qApp->setApplicationName("tst_QAccessibilityLinux app"); qApp->setApplicationName("tst_QAccessibilityLinux app");
// Pretend we are a screen reader
QDBusConnection c = QDBusConnection::sessionBus();
OrgA11yStatusInterface *a11yStatus = new OrgA11yStatusInterface(QStringLiteral("org.a11y.Bus"), QStringLiteral("/org/a11y/bus"), c, this);
a11yStatus->setScreenReaderEnabled(true);
QTRY_VERIFY(dbus.isEnabled()); QTRY_VERIFY(dbus.isEnabled());
QTRY_VERIFY(dbus.connection().isConnected()); QTRY_VERIFY(dbus.connection().isConnected());
address = dbus.connection().baseService().toLatin1().data(); address = dbus.connection().baseService().toLatin1().data();
QVERIFY(!address.isEmpty());
m_window = new AccessibleTestWindow(); m_window = new AccessibleTestWindow();
m_window->show(); m_window->show();