From 9e733622a605c841adf7da7304895758d307c843 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Thu, 2 Jul 2020 10:04:34 +0200 Subject: Move linuxaccessibility to QtGui MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change some too-generic file names. Task-number: QTBUG-83255 Change-Id: I4497ee2508bc323566f4061d4547707b7bda7a77 Reviewed-by: Tor Arne Vestbø --- src/gui/.prev_CMakeLists.txt | 25 +- src/gui/CMakeLists.txt | 31 +- src/gui/accessible/accessible.pri | 2 + src/gui/accessible/linux/.prev_CMakeLists.txt | 40 + src/gui/accessible/linux/CMakeLists.txt | 42 + src/gui/accessible/linux/atspiadaptor.cpp | 2482 ++++++++++++++++++++ src/gui/accessible/linux/atspiadaptor_p.h | 228 ++ src/gui/accessible/linux/dbusconnection.cpp | 172 ++ src/gui/accessible/linux/dbusconnection_p.h | 95 + src/gui/accessible/linux/dbusxml/Bus.xml | 17 + src/gui/accessible/linux/dbusxml/Cache.xml | 21 + .../linux/dbusxml/DeviceEventController.xml | 66 + src/gui/accessible/linux/dbusxml/Socket.xml | 23 + src/gui/accessible/linux/linux.pri | 27 + .../accessible/linux/qspi_constant_mappings.cpp | 154 ++ .../accessible/linux/qspi_constant_mappings_p.h | 145 ++ .../accessible/linux/qspi_struct_marshallers.cpp | 237 ++ .../accessible/linux/qspi_struct_marshallers_p.h | 200 ++ src/gui/accessible/linux/qspiaccessiblebridge.cpp | 286 +++ src/gui/accessible/linux/qspiaccessiblebridge_p.h | 95 + .../accessible/linux/qspiapplicationadaptor.cpp | 240 ++ .../accessible/linux/qspiapplicationadaptor_p.h | 99 + src/gui/accessible/linux/qspidbuscache.cpp | 91 + src/gui/accessible/linux/qspidbuscache_p.h | 82 + .../platform/unix/dbusmenu/qdbusmenuconnection_p.h | 6 +- src/platformsupport/.prev_CMakeLists.txt | 3 - src/platformsupport/CMakeLists.txt | 3 - .../linuxaccessibility/.prev_CMakeLists.txt | 40 - .../linuxaccessibility/CMakeLists.txt | 42 - .../linuxaccessibility/application.cpp | 240 -- .../linuxaccessibility/application_p.h | 99 - .../linuxaccessibility/atspiadaptor.cpp | 2482 -------------------- .../linuxaccessibility/atspiadaptor_p.h | 228 -- src/platformsupport/linuxaccessibility/bridge.cpp | 286 --- src/platformsupport/linuxaccessibility/bridge_p.h | 95 - src/platformsupport/linuxaccessibility/cache.cpp | 91 - src/platformsupport/linuxaccessibility/cache_p.h | 82 - .../linuxaccessibility/constant_mappings.cpp | 154 -- .../linuxaccessibility/constant_mappings_p.h | 146 -- .../linuxaccessibility/dbusconnection.cpp | 172 -- .../linuxaccessibility/dbusconnection_p.h | 95 - .../linuxaccessibility/dbusxml/Bus.xml | 17 - .../linuxaccessibility/dbusxml/Cache.xml | 21 - .../dbusxml/DeviceEventController.xml | 66 - .../linuxaccessibility/dbusxml/Socket.xml | 23 - .../linuxaccessibility/linuxaccessibility.pro | 36 - .../linuxaccessibility/struct_marshallers.cpp | 237 -- .../linuxaccessibility/struct_marshallers_p.h | 197 -- src/platformsupport/platformsupport.pro | 6 - src/plugins/platforms/xcb/.prev_CMakeLists.txt | 9 +- src/plugins/platforms/xcb/CMakeLists.txt | 9 +- src/plugins/platforms/xcb/qxcbintegration.cpp | 2 +- src/plugins/platforms/xcb/xcb_qpa_lib.pro | 3 - src/src.pro | 2 +- 54 files changed, 4902 insertions(+), 4890 deletions(-) create mode 100644 src/gui/accessible/linux/.prev_CMakeLists.txt create mode 100644 src/gui/accessible/linux/CMakeLists.txt create mode 100644 src/gui/accessible/linux/atspiadaptor.cpp create mode 100644 src/gui/accessible/linux/atspiadaptor_p.h create mode 100644 src/gui/accessible/linux/dbusconnection.cpp create mode 100644 src/gui/accessible/linux/dbusconnection_p.h create mode 100644 src/gui/accessible/linux/dbusxml/Bus.xml create mode 100644 src/gui/accessible/linux/dbusxml/Cache.xml create mode 100644 src/gui/accessible/linux/dbusxml/DeviceEventController.xml create mode 100644 src/gui/accessible/linux/dbusxml/Socket.xml create mode 100644 src/gui/accessible/linux/linux.pri create mode 100644 src/gui/accessible/linux/qspi_constant_mappings.cpp create mode 100644 src/gui/accessible/linux/qspi_constant_mappings_p.h create mode 100644 src/gui/accessible/linux/qspi_struct_marshallers.cpp create mode 100644 src/gui/accessible/linux/qspi_struct_marshallers_p.h create mode 100644 src/gui/accessible/linux/qspiaccessiblebridge.cpp create mode 100644 src/gui/accessible/linux/qspiaccessiblebridge_p.h create mode 100644 src/gui/accessible/linux/qspiapplicationadaptor.cpp create mode 100644 src/gui/accessible/linux/qspiapplicationadaptor_p.h create mode 100644 src/gui/accessible/linux/qspidbuscache.cpp create mode 100644 src/gui/accessible/linux/qspidbuscache_p.h delete mode 100644 src/platformsupport/linuxaccessibility/.prev_CMakeLists.txt delete mode 100644 src/platformsupport/linuxaccessibility/CMakeLists.txt delete mode 100644 src/platformsupport/linuxaccessibility/application.cpp delete mode 100644 src/platformsupport/linuxaccessibility/application_p.h delete mode 100644 src/platformsupport/linuxaccessibility/atspiadaptor.cpp delete mode 100644 src/platformsupport/linuxaccessibility/atspiadaptor_p.h delete mode 100644 src/platformsupport/linuxaccessibility/bridge.cpp delete mode 100644 src/platformsupport/linuxaccessibility/bridge_p.h delete mode 100644 src/platformsupport/linuxaccessibility/cache.cpp delete mode 100644 src/platformsupport/linuxaccessibility/cache_p.h delete mode 100644 src/platformsupport/linuxaccessibility/constant_mappings.cpp delete mode 100644 src/platformsupport/linuxaccessibility/constant_mappings_p.h delete mode 100644 src/platformsupport/linuxaccessibility/dbusconnection.cpp delete mode 100644 src/platformsupport/linuxaccessibility/dbusconnection_p.h delete mode 100644 src/platformsupport/linuxaccessibility/dbusxml/Bus.xml delete mode 100644 src/platformsupport/linuxaccessibility/dbusxml/Cache.xml delete mode 100644 src/platformsupport/linuxaccessibility/dbusxml/DeviceEventController.xml delete mode 100644 src/platformsupport/linuxaccessibility/dbusxml/Socket.xml delete mode 100644 src/platformsupport/linuxaccessibility/linuxaccessibility.pro delete mode 100644 src/platformsupport/linuxaccessibility/struct_marshallers.cpp delete mode 100644 src/platformsupport/linuxaccessibility/struct_marshallers_p.h (limited to 'src') diff --git a/src/gui/.prev_CMakeLists.txt b/src/gui/.prev_CMakeLists.txt index 31eb39fb95..a4820965e7 100644 --- a/src/gui/.prev_CMakeLists.txt +++ b/src/gui/.prev_CMakeLists.txt @@ -399,6 +399,23 @@ qt_extend_target(Gui CONDITION QT_FEATURE_accessibility AND WIN32 accessible/windows/apisupport/uiatypes_p.h ) +qt_extend_target(Gui CONDITION QT_FEATURE_accessibility AND QT_FEATURE_accessibility_atspi_bridge + SOURCES + accessible/linux/atspiadaptor.cpp accessible/linux/atspiadaptor_p.h + accessible/linux/dbusconnection.cpp accessible/linux/dbusconnection_p.h + accessible/linux/qspi_constant_mappings.cpp accessible/linux/qspi_constant_mappings_p.h + accessible/linux/qspi_struct_marshallers.cpp accessible/linux/qspi_struct_marshallers_p.h + accessible/linux/qspiaccessiblebridge.cpp accessible/linux/qspiaccessiblebridge_p.h + accessible/linux/qspiapplicationadaptor.cpp accessible/linux/qspiapplicationadaptor_p.h + accessible/linux/qspidbuscache.cpp accessible/linux/qspidbuscache_p.h + DBUS_ADAPTOR_SOURCES + accessibility_adaptors + DBUS_INTERFACE_SOURCES + accessibility_interfaces + PUBLIC_LIBRARIES + PkgConfig::ATSPI2_nolink +) + qt_extend_target(Gui CONDITION QT_FEATURE_action SOURCES kernel/qaction.cpp kernel/qaction.h kernel/qaction_p.h @@ -433,7 +450,7 @@ qt_extend_target(Gui CONDITION QT_FEATURE_png WrapPNG::WrapPNG ) -#### Keys ignored in scope 38:.:image:image/image.pri:WIN32 AND MINGW: +#### Keys ignored in scope 40:.:image:image/image.pri:WIN32 AND MINGW: # GCC_VERSION = "$${QMAKE_GCC_MAJOR_VERSION}.$${QMAKE_GCC_MINOR_VERSION}.$${QMAKE_GCC_PATCH_VERSION}" qt_extend_target(Gui CONDITION ((QT_FEATURE_png) AND (WIN32 AND MINGW)) AND (GCC_VERSION___equals___8.1.0) @@ -676,7 +693,7 @@ qt_extend_target(Gui CONDITION QT_FEATURE_vulkan Vulkan::Vulkan_nolink ) -#### Keys ignored in scope 99:.:vulkan:vulkan/vulkan.pri:QT_FEATURE_vkgen: +#### Keys ignored in scope 101:.:vulkan:vulkan/vulkan.pri:QT_FEATURE_vkgen: # QMAKE_EXTRA_COMPILERS = "qvkgen_h" "qvkgen_ph" "qvkgen_pimpl" # QMAKE_QVKGEN_INPUT = "vulkan/vk.xml" # QMAKE_QVKGEN_LICENSE_HEADER = "$$QT_SOURCE_TREE/header.LGPL" @@ -692,10 +709,10 @@ qt_extend_target(Gui CONDITION QT_FEATURE_vulkan # qvkgen_pimpl.input = "QMAKE_QVKGEN_INPUT" # qvkgen_pimpl.output = "$$OUT_PWD/vulkan/qvulkanfunctions_p.cpp" -#### Keys ignored in scope 100:.:vulkan:vulkan/vulkan.pri:QT_FEATURE_vulkan: +#### Keys ignored in scope 102:.:vulkan:vulkan/vulkan.pri:QT_FEATURE_vulkan: # qvkgen_h.variable_out = "HEADERS" -#### Keys ignored in scope 101:.:vulkan:vulkan/vulkan.pri:else: +#### Keys ignored in scope 103:.:vulkan:vulkan/vulkan.pri:else: # qvkgen_h.CONFIG = "target_predeps" "no_link" qt_extend_target(Gui CONDITION WASM diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index db6f463c0b..21dc18721e 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -487,6 +487,31 @@ qt_extend_target(Gui CONDITION QT_FEATURE_accessibility AND WIN32 accessible/windows/apisupport/uiatypes_p.h ) +qt_extend_target(Gui CONDITION QT_FEATURE_accessibility AND QT_FEATURE_accessibility_atspi_bridge + SOURCES + accessible/linux/atspiadaptor.cpp accessible/linux/atspiadaptor_p.h + accessible/linux/dbusconnection.cpp accessible/linux/dbusconnection_p.h + accessible/linux/qspi_constant_mappings.cpp accessible/linux/qspi_constant_mappings_p.h + accessible/linux/qspi_struct_marshallers.cpp accessible/linux/qspi_struct_marshallers_p.h + accessible/linux/qspiaccessiblebridge.cpp accessible/linux/qspiaccessiblebridge_p.h + accessible/linux/qspiapplicationadaptor.cpp accessible/linux/qspiapplicationadaptor_p.h + accessible/linux/qspidbuscache.cpp accessible/linux/qspidbuscache_p.h +# special case begin + DBUS_ADAPTOR_SOURCES + accessible/linux/dbusxml/Cache.xml + accessible/linux/dbusxml/DeviceEventController.xml + DBUS_ADAPTOR_FLAGS + "-i" "QtGui/private/qspi_struct_marshallers_p.h" + DBUS_INTERFACE_SOURCES + accessible/linux/dbusxml/Bus.xml + accessible/linux/dbusxml/Socket.xml + DBUS_INTERFACE_FLAGS + "-i" "QtGui/private/qspi_struct_marshallers_p.h" +# special case end + PUBLIC_LIBRARIES + PkgConfig::ATSPI2_nolink +) + qt_extend_target(Gui CONDITION QT_FEATURE_action SOURCES kernel/qaction.cpp kernel/qaction.h kernel/qaction_p.h @@ -521,7 +546,7 @@ qt_extend_target(Gui CONDITION QT_FEATURE_png WrapPNG::WrapPNG ) -#### Keys ignored in scope 38:.:image:image/image.pri:WIN32 AND MINGW: +#### Keys ignored in scope 40:.:image:image/image.pri:WIN32 AND MINGW: # GCC_VERSION = "$${QMAKE_GCC_MAJOR_VERSION}.$${QMAKE_GCC_MINOR_VERSION}.$${QMAKE_GCC_PATCH_VERSION}" qt_extend_target(Gui CONDITION ((QT_FEATURE_png) AND (WIN32 AND MINGW)) AND (GCC_VERSION___equals___8.1.0) @@ -839,10 +864,10 @@ add_custom_command( # qvkgen_pimpl.input = "QMAKE_QVKGEN_INPUT" # qvkgen_pimpl.output = "$$OUT_PWD/vulkan/qvulkanfunctions_p.cpp" -#### Keys ignored in scope 100:.:vulkan:vulkan/vulkan.pri:QT_FEATURE_vulkan: +#### Keys ignored in scope 102:.:vulkan:vulkan/vulkan.pri:QT_FEATURE_vulkan: # qvkgen_h.variable_out = "HEADERS" -#### Keys ignored in scope 101:.:vulkan:vulkan/vulkan.pri:else: +#### Keys ignored in scope 103:.:vulkan:vulkan/vulkan.pri:else: # qvkgen_h.CONFIG = "target_predeps" "no_link" qt_extend_target(Gui CONDITION WASM diff --git a/src/gui/accessible/accessible.pri b/src/gui/accessible/accessible.pri index 471e2fbaea..cc403ba9e9 100644 --- a/src/gui/accessible/accessible.pri +++ b/src/gui/accessible/accessible.pri @@ -25,4 +25,6 @@ qtConfig(accessibility) { } win32: include(windows/windows.pri) + + qtConfig(accessibility-atspi-bridge): include(linux/linux.pri) } diff --git a/src/gui/accessible/linux/.prev_CMakeLists.txt b/src/gui/accessible/linux/.prev_CMakeLists.txt new file mode 100644 index 0000000000..ffb0ca9610 --- /dev/null +++ b/src/gui/accessible/linux/.prev_CMakeLists.txt @@ -0,0 +1,40 @@ +# Generated from linuxaccessibility.pro. + +##################################################################### +## LinuxAccessibilitySupport Module: +##################################################################### + +qt_add_module(LinuxAccessibilitySupport + STATIC + INTERNAL_MODULE + SOURCES + application.cpp application_p.h + atspiadaptor.cpp atspiadaptor_p.h + bridge.cpp bridge_p.h + cache.cpp cache_p.h + constant_mappings.cpp constant_mappings_p.h + dbusconnection.cpp dbusconnection_p.h + struct_marshallers.cpp struct_marshallers_p.h + DBUS_ADAPTOR_SOURCES + dbusxml/Cache.xml + dbusxml/DeviceEventController.xml + DBUS_ADAPTOR_FLAGS + "-i" "struct_marshallers_p.h" + DBUS_INTERFACE_SOURCES + dbusxml/Bus.xml + dbusxml/Socket.xml + DBUS_INTERFACE_FLAGS + "-i" "struct_marshallers_p.h" + DEFINES + QT_NO_CAST_FROM_ASCII + PUBLIC_LIBRARIES + PkgConfig::ATSPI2_nolink + Qt::CorePrivate + Qt::DBus + Qt::GuiPrivate + PRECOMPILED_HEADER + "../../corelib/global/qt_pch.h" +) + +#### Keys ignored in scope 1:.:.:linuxaccessibility.pro:: +# MODULE = "linuxaccessibility_support" diff --git a/src/gui/accessible/linux/CMakeLists.txt b/src/gui/accessible/linux/CMakeLists.txt new file mode 100644 index 0000000000..0e1a7a9d16 --- /dev/null +++ b/src/gui/accessible/linux/CMakeLists.txt @@ -0,0 +1,42 @@ +# Generated from linuxaccessibility.pro. + +qt_find_package(ATSPI2) # special case + +##################################################################### +## LinuxAccessibilitySupport Module: +##################################################################### + +qt_add_module(LinuxAccessibilitySupport + STATIC + INTERNAL_MODULE + SOURCES + application.cpp application_p.h + atspiadaptor.cpp atspiadaptor_p.h + bridge.cpp bridge_p.h + cache.cpp cache_p.h + constant_mappings.cpp constant_mappings_p.h + dbusconnection.cpp dbusconnection_p.h + struct_marshallers.cpp struct_marshallers_p.h + DBUS_ADAPTOR_SOURCES + dbusxml/Cache.xml + dbusxml/DeviceEventController.xml + DBUS_ADAPTOR_FLAGS + "-i" "struct_marshallers_p.h" + DBUS_INTERFACE_SOURCES + dbusxml/Bus.xml + dbusxml/Socket.xml + DBUS_INTERFACE_FLAGS + "-i" "struct_marshallers_p.h" + DEFINES + QT_NO_CAST_FROM_ASCII + PUBLIC_LIBRARIES + PkgConfig::ATSPI2_nolink + Qt::CorePrivate + Qt::DBus + Qt::GuiPrivate + PRECOMPILED_HEADER + "../../corelib/global/qt_pch.h" +) + +#### Keys ignored in scope 1:.:.:linuxaccessibility.pro:: +# MODULE = "linuxaccessibility_support" diff --git a/src/gui/accessible/linux/atspiadaptor.cpp b/src/gui/accessible/linux/atspiadaptor.cpp new file mode 100644 index 0000000000..65b014c9e1 --- /dev/null +++ b/src/gui/accessible/linux/atspiadaptor.cpp @@ -0,0 +1,2482 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "atspiadaptor_p.h" + +#include +#include +#include +#include +#include + +#include + +#ifndef QT_NO_ACCESSIBILITY +#include "socket_interface.h" +#include "qspi_constant_mappings_p.h" +#include + +#include "qspiapplicationadaptor_p.h" +/*! + \class AtSpiAdaptor + \internal + + \brief AtSpiAdaptor is the main class to forward between QAccessibleInterface and AT-SPI DBus + + AtSpiAdaptor implements the functions specified in all at-spi interfaces. + It sends notifications coming from Qt via dbus and listens to incoming dbus requests. +*/ + +QT_BEGIN_NAMESPACE + +Q_LOGGING_CATEGORY(lcAccessibilityAtspi, "qt.accessibility.atspi") +Q_LOGGING_CATEGORY(lcAccessibilityAtspiCreation, "qt.accessibility.atspi.creation") + +AtSpiAdaptor::AtSpiAdaptor(DBusConnection *connection, QObject *parent) + : QDBusVirtualObject(parent), m_dbus(connection) + , sendFocus(0) + , sendObject(0) + , sendObject_active_descendant_changed(0) + , sendObject_attributes_changed(0) + , sendObject_bounds_changed(0) + , sendObject_children_changed(0) +// , sendObject_children_changed_add(0) +// , sendObject_children_changed_remove(0) + , sendObject_column_deleted(0) + , sendObject_column_inserted(0) + , sendObject_column_reordered(0) + , sendObject_link_selected(0) + , sendObject_model_changed(0) + , sendObject_property_change(0) + , sendObject_property_change_accessible_description(0) + , sendObject_property_change_accessible_name(0) + , sendObject_property_change_accessible_parent(0) + , sendObject_property_change_accessible_role(0) + , sendObject_property_change_accessible_table_caption(0) + , sendObject_property_change_accessible_table_column_description(0) + , sendObject_property_change_accessible_table_column_header(0) + , sendObject_property_change_accessible_table_row_description(0) + , sendObject_property_change_accessible_table_row_header(0) + , sendObject_property_change_accessible_table_summary(0) + , sendObject_property_change_accessible_value(0) + , sendObject_row_deleted(0) + , sendObject_row_inserted(0) + , sendObject_row_reordered(0) + , sendObject_selection_changed(0) + , sendObject_state_changed(0) + , sendObject_text_attributes_changed(0) + , sendObject_text_bounds_changed(0) + , sendObject_text_caret_moved(0) + , sendObject_text_changed(0) +// , sendObject_text_changed_delete(0) +// , sendObject_text_changed_insert(0) + , sendObject_text_selection_changed(0) + , sendObject_value_changed(0) + , sendObject_visible_data_changed(0) + , sendWindow(0) + , sendWindow_activate(0) + , sendWindow_close(0) + , sendWindow_create(0) + , sendWindow_deactivate(0) +// , sendWindow_desktop_create(0) +// , sendWindow_desktop_destroy(0) + , sendWindow_lower(0) + , sendWindow_maximize(0) + , sendWindow_minimize(0) + , sendWindow_move(0) + , sendWindow_raise(0) + , sendWindow_reparent(0) + , sendWindow_resize(0) + , sendWindow_restore(0) + , sendWindow_restyle(0) + , sendWindow_shade(0) + , sendWindow_unshade(0) +{ + 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))); +} + +AtSpiAdaptor::~AtSpiAdaptor() +{ +} + +/*! + Provide DBus introspection. + */ +QString AtSpiAdaptor::introspect(const QString &path) const +{ + static const QLatin1String accessibleIntrospection( + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + ); + + static const QLatin1String actionIntrospection( + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + ); + + static const QLatin1String applicationIntrospection( + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + ); + + static const QLatin1String componentIntrospection( + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + ); + + static const QLatin1String editableTextIntrospection( + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + ); + + static const QLatin1String tableIntrospection( + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + ); + + static const QLatin1String textIntrospection( + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + ); + + static const QLatin1String valueIntrospection( + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + ); + + QAccessibleInterface * interface = interfaceFromPath(path); + if (!interface) { + qCDebug(lcAccessibilityAtspi) << "WARNING Qt AtSpiAdaptor: Could not find accessible on path: " << path; + return QString(); + } + + QStringList interfaces = accessibleInterfaces(interface); + + QString xml; + xml.append(accessibleIntrospection); + + if (interfaces.contains(QLatin1String(ATSPI_DBUS_INTERFACE_COMPONENT))) + xml.append(componentIntrospection); + if (interfaces.contains(QLatin1String(ATSPI_DBUS_INTERFACE_TEXT))) + xml.append(textIntrospection); + if (interfaces.contains(QLatin1String(ATSPI_DBUS_INTERFACE_EDITABLE_TEXT))) + xml.append(editableTextIntrospection); + if (interfaces.contains(QLatin1String(ATSPI_DBUS_INTERFACE_ACTION))) + xml.append(actionIntrospection); + if (interfaces.contains(QLatin1String(ATSPI_DBUS_INTERFACE_TABLE))) + xml.append(tableIntrospection); + if (interfaces.contains(QLatin1String(ATSPI_DBUS_INTERFACE_VALUE))) + xml.append(valueIntrospection); + if (path == QLatin1String(QSPI_OBJECT_PATH_ROOT)) + xml.append(applicationIntrospection); + + return xml; +} + +void AtSpiAdaptor::setBitFlag(const QString &flag) +{ + Q_ASSERT(flag.size()); + + // assume we don't get nonsense - look at first letter only + switch (flag.at(0).toLower().toLatin1()) { + case 'o': { + if (flag.size() <= 8) { // Object:: + sendObject = 1; + } else { // Object:Foo:Bar + QString right = flag.mid(7); + if (false) { + } else if (right.startsWith(QLatin1String("ActiveDescendantChanged"))) { + sendObject_active_descendant_changed = 1; + } else if (right.startsWith(QLatin1String("AttributesChanged"))) { + sendObject_attributes_changed = 1; + } else if (right.startsWith(QLatin1String("BoundsChanged"))) { + sendObject_bounds_changed = 1; + } else if (right.startsWith(QLatin1String("ChildrenChanged"))) { + sendObject_children_changed = 1; + } else if (right.startsWith(QLatin1String("ColumnDeleted"))) { + sendObject_column_deleted = 1; + } else if (right.startsWith(QLatin1String("ColumnInserted"))) { + sendObject_column_inserted = 1; + } else if (right.startsWith(QLatin1String("ColumnReordered"))) { + sendObject_column_reordered = 1; + } else if (right.startsWith(QLatin1String("LinkSelected"))) { + sendObject_link_selected = 1; + } else if (right.startsWith(QLatin1String("ModelChanged"))) { + sendObject_model_changed = 1; + } else if (right.startsWith(QLatin1String("PropertyChange"))) { + if (right == QLatin1String("PropertyChange:AccessibleDescription")) { + sendObject_property_change_accessible_description = 1; + } else if (right == QLatin1String("PropertyChange:AccessibleName")) { + sendObject_property_change_accessible_name = 1; + } else if (right == QLatin1String("PropertyChange:AccessibleParent")) { + sendObject_property_change_accessible_parent = 1; + } else if (right == QLatin1String("PropertyChange:AccessibleRole")) { + sendObject_property_change_accessible_role = 1; + } else if (right == QLatin1String("PropertyChange:TableCaption")) { + sendObject_property_change_accessible_table_caption = 1; + } else if (right == QLatin1String("PropertyChange:TableColumnDescription")) { + sendObject_property_change_accessible_table_column_description = 1; + } else if (right == QLatin1String("PropertyChange:TableColumnHeader")) { + sendObject_property_change_accessible_table_column_header = 1; + } else if (right == QLatin1String("PropertyChange:TableRowDescription")) { + sendObject_property_change_accessible_table_row_description = 1; + } else if (right == QLatin1String("PropertyChange:TableRowHeader")) { + sendObject_property_change_accessible_table_row_header = 1; + } else if (right == QLatin1String("PropertyChange:TableSummary")) { + sendObject_property_change_accessible_table_summary = 1; + } else if (right == QLatin1String("PropertyChange:AccessibleValue")) { + sendObject_property_change_accessible_value = 1; + } else { + sendObject_property_change = 1; + } + } else if (right.startsWith(QLatin1String("RowDeleted"))) { + sendObject_row_deleted = 1; + } else if (right.startsWith(QLatin1String("RowInserted"))) { + sendObject_row_inserted = 1; + } else if (right.startsWith(QLatin1String("RowReordered"))) { + sendObject_row_reordered = 1; + } else if (right.startsWith(QLatin1String("SelectionChanged"))) { + sendObject_selection_changed = 1; + } else if (right.startsWith(QLatin1String("StateChanged"))) { + sendObject_state_changed = 1; + } else if (right.startsWith(QLatin1String("TextAttributesChanged"))) { + sendObject_text_attributes_changed = 1; + } else if (right.startsWith(QLatin1String("TextBoundsChanged"))) { + sendObject_text_bounds_changed = 1; + } else if (right.startsWith(QLatin1String("TextCaretMoved"))) { + sendObject_text_caret_moved = 1; + } else if (right.startsWith(QLatin1String("TextChanged"))) { + sendObject_text_changed = 1; + } else if (right.startsWith(QLatin1String("TextSelectionChanged"))) { + sendObject_text_selection_changed = 1; + } else if (right.startsWith(QLatin1String("ValueChanged"))) { + sendObject_value_changed = 1; + } else if (right.startsWith(QLatin1String("VisibleDataChanged")) + || right.startsWith(QLatin1String("VisibledataChanged"))) { // typo in libatspi + sendObject_visible_data_changed = 1; + } else { + qCDebug(lcAccessibilityAtspi) << "WARNING: subscription string not handled:" << flag; + } + } + break; + } + case 'w': { // window + if (flag.size() <= 8) { + sendWindow = 1; + } else { // object:Foo:Bar + QString right = flag.mid(7); + if (false) { + } else if (right.startsWith(QLatin1String("Activate"))) { + sendWindow_activate = 1; + } else if (right.startsWith(QLatin1String("Close"))) { + sendWindow_close= 1; + } else if (right.startsWith(QLatin1String("Create"))) { + sendWindow_create = 1; + } else if (right.startsWith(QLatin1String("Deactivate"))) { + sendWindow_deactivate = 1; + } else if (right.startsWith(QLatin1String("Lower"))) { + sendWindow_lower = 1; + } else if (right.startsWith(QLatin1String("Maximize"))) { + sendWindow_maximize = 1; + } else if (right.startsWith(QLatin1String("Minimize"))) { + sendWindow_minimize = 1; + } else if (right.startsWith(QLatin1String("Move"))) { + sendWindow_move = 1; + } else if (right.startsWith(QLatin1String("Raise"))) { + sendWindow_raise = 1; + } else if (right.startsWith(QLatin1String("Reparent"))) { + sendWindow_reparent = 1; + } else if (right.startsWith(QLatin1String("Resize"))) { + sendWindow_resize = 1; + } else if (right.startsWith(QLatin1String("Restore"))) { + sendWindow_restore = 1; + } else if (right.startsWith(QLatin1String("Restyle"))) { + sendWindow_restyle = 1; + } else if (right.startsWith(QLatin1String("Shade"))) { + sendWindow_shade = 1; + } else if (right.startsWith(QLatin1String("Unshade"))) { + sendWindow_unshade = 1; + } else if (right.startsWith(QLatin1String("DesktopCreate"))) { + // ignore this one + } else if (right.startsWith(QLatin1String("DesktopDestroy"))) { + // ignore this one + } else { + qCDebug(lcAccessibilityAtspi) << "WARNING: subscription string not handled:" << flag; + } + } + break; + } + case 'f': { + sendFocus = 1; + break; + } + case 'd': { // document is not implemented + break; + } + case 't': { // terminal is not implemented + break; + } + case 'm': { // mouse* is handled in a different way by the gnome atspi stack + break; + } + default: + qCDebug(lcAccessibilityAtspi) << "WARNING: subscription string not handled:" << flag; + } +} + +/*! + Checks via dbus which events should be sent. + */ +void AtSpiAdaptor::updateEventListeners() +{ + QDBusMessage m = QDBusMessage::createMethodCall(QLatin1String("org.a11y.atspi.Registry"), + QLatin1String("/org/a11y/atspi/registry"), + QLatin1String("org.a11y.atspi.Registry"), QLatin1String("GetRegisteredEvents")); + QDBusReply listenersReply = m_dbus->connection().call(m); + if (listenersReply.isValid()) { + const QSpiEventListenerArray evList = listenersReply.value(); + for (const QSpiEventListener &ev : evList) + setBitFlag(ev.eventName); + m_applicationAdaptor->sendEvents(!evList.isEmpty()); + } else { + qCDebug(lcAccessibilityAtspi) << "Could not query active accessibility event listeners."; + } +} + +void AtSpiAdaptor::eventListenerDeregistered(const QString &/*bus*/, const QString &/*path*/) +{ +// qCDebug(lcAccessibilityAtspi) << "AtSpiAdaptor::eventListenerDeregistered: " << bus << path; + updateEventListeners(); +} + +void AtSpiAdaptor::eventListenerRegistered(const QString &/*bus*/, const QString &/*path*/) +{ +// qCDebug(lcAccessibilityAtspi) << "AtSpiAdaptor::eventListenerRegistered: " << bus << path; + updateEventListeners(); +} + +/*! + This slot needs to get called when a \a window has be activated or deactivated (become focused). + When \a active is true, the window just received focus, otherwise it lost the focus. + */ +void AtSpiAdaptor::windowActivated(QObject* window, bool active) +{ + if (!(sendWindow || sendWindow_activate)) + return; + + QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(window); + Q_ASSERT(iface); + Q_ASSERT(!active || iface->isValid()); + + QString windowTitle; + // in dtor it may be invalid + if (iface->isValid()) + windowTitle = iface->text(QAccessible::Name); + + QDBusVariant data; + data.setVariant(windowTitle); + + QVariantList args = packDBusSignalArguments(QString(), 0, 0, QVariant::fromValue(data)); + + QString status = active ? QLatin1String("Activate") : QLatin1String("Deactivate"); + QString path = pathForObject(window); + sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_WINDOW), status, args); + + QVariantList stateArgs = packDBusSignalArguments(QLatin1String("active"), active ? 1 : 0, 0, variantForPath(path)); + sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT), + QLatin1String("StateChanged"), stateArgs); +} + +QVariantList AtSpiAdaptor::packDBusSignalArguments(const QString &type, int data1, int data2, const QVariant &variantData) const +{ + QVariantList arguments; + arguments << type << data1 << data2 << variantData + << QVariant::fromValue(QSpiObjectReference(m_dbus->connection(), QDBusObjectPath(QSPI_OBJECT_PATH_ROOT))); + return arguments; +} + +QVariant AtSpiAdaptor::variantForPath(const QString &path) const +{ + QDBusVariant data; + data.setVariant(QVariant::fromValue(QSpiObjectReference(m_dbus->connection(), QDBusObjectPath(path)))); + return QVariant::fromValue(data); +} + +bool AtSpiAdaptor::sendDBusSignal(const QString &path, const QString &interface, const QString &signalName, const QVariantList &arguments) const +{ + QDBusMessage message = QDBusMessage::createSignal(path, interface, signalName); + message.setArguments(arguments); + return m_dbus->connection().send(message); +} + +QAccessibleInterface *AtSpiAdaptor::interfaceFromPath(const QString& dbusPath) const +{ + if (dbusPath == QLatin1String(QSPI_OBJECT_PATH_ROOT)) + return QAccessible::queryAccessibleInterface(qApp); + + QStringList parts = dbusPath.split(QLatin1Char('/')); + if (parts.size() != 6) { + qCDebug(lcAccessibilityAtspi) << "invalid path: " << dbusPath; + return 0; + } + + QString objectString = parts.at(5); + QAccessible::Id id = objectString.toUInt(); + + // The id is always in the range [INT_MAX+1, UINT_MAX] + if ((int)id >= 0) + qCWarning(lcAccessibilityAtspi) << "No accessible object found for id: " << id; + + return QAccessible::accessibleInterface(id); +} + +void AtSpiAdaptor::notifyStateChange(QAccessibleInterface *interface, const QString &state, int value) +{ + QString path = pathForInterface(interface); + QVariantList stateArgs = packDBusSignalArguments(state, value, 0, variantForPath(path)); + sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT), + QLatin1String("StateChanged"), stateArgs); +} + + +/*! + This function gets called when Qt notifies about accessibility updates. +*/ +void AtSpiAdaptor::notify(QAccessibleEvent *event) +{ + switch (event->type()) { + case QAccessible::ObjectCreated: + if (sendObject || sendObject_children_changed) + notifyAboutCreation(event->accessibleInterface()); + break; + case QAccessible::ObjectShow: { + if (sendObject || sendObject_state_changed) { + notifyStateChange(event->accessibleInterface(), QLatin1String("showing"), 1); + } + break; + } + case QAccessible::ObjectHide: { + if (sendObject || sendObject_state_changed) { + notifyStateChange(event->accessibleInterface(), QLatin1String("showing"), 0); + } + break; + } + case QAccessible::ObjectDestroyed: { + if (sendObject || sendObject_state_changed) + notifyAboutDestruction(event->accessibleInterface()); + break; + } + case QAccessible::ObjectReorder: { + if (sendObject || sendObject_children_changed) + childrenChanged(event->accessibleInterface()); + break; + } + case QAccessible::NameChanged: { + if (sendObject || sendObject_property_change || sendObject_property_change_accessible_name) { + QString path = pathForInterface(event->accessibleInterface()); + QVariantList args = packDBusSignalArguments(QLatin1String("accessible-name"), 0, 0, variantForPath(path)); + sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT), + QLatin1String("PropertyChange"), args); + } + break; + } + case QAccessible::DescriptionChanged: { + if (sendObject || sendObject_property_change || sendObject_property_change_accessible_description) { + QString path = pathForInterface(event->accessibleInterface()); + QVariantList args = packDBusSignalArguments(QLatin1String("accessible-description"), 0, 0, variantForPath(path)); + sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT), + QLatin1String("PropertyChange"), args); + } + break; + } + case QAccessible::Focus: { + if (sendFocus || sendObject || sendObject_state_changed) + sendFocusChanged(event->accessibleInterface()); + break; + } + case QAccessible::TextInserted: + case QAccessible::TextRemoved: + case QAccessible::TextUpdated: { + if (sendObject || sendObject_text_changed) { + QAccessibleInterface * iface = event->accessibleInterface(); + if (!iface || !iface->textInterface()) { + qCDebug(lcAccessibilityAtspi) << "Received text event for invalid interface."; + return; + } + QString path = pathForInterface(iface); + + int changePosition = 0; + int cursorPosition = 0; + QString textRemoved; + QString textInserted; + + if (event->type() == QAccessible::TextInserted) { + QAccessibleTextInsertEvent *textEvent = static_cast(event); + textInserted = textEvent->textInserted(); + changePosition = textEvent->changePosition(); + cursorPosition = textEvent->cursorPosition(); + } else if (event->type() == QAccessible::TextRemoved) { + QAccessibleTextRemoveEvent *textEvent = static_cast(event); + textRemoved = textEvent->textRemoved(); + changePosition = textEvent->changePosition(); + cursorPosition = textEvent->cursorPosition(); + } else if (event->type() == QAccessible::TextUpdated) { + QAccessibleTextUpdateEvent *textEvent = static_cast(event); + textInserted = textEvent->textInserted(); + textRemoved = textEvent->textRemoved(); + changePosition = textEvent->changePosition(); + cursorPosition = textEvent->cursorPosition(); + } + + QDBusVariant data; + + if (!textRemoved.isEmpty()) { + data.setVariant(QVariant::fromValue(textRemoved)); + QVariantList args = packDBusSignalArguments(QLatin1String("delete"), changePosition, textRemoved.length(), QVariant::fromValue(data)); + sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT), + QLatin1String("TextChanged"), args); + } + + if (!textInserted.isEmpty()) { + data.setVariant(QVariant::fromValue(textInserted)); + QVariantList args = packDBusSignalArguments(QLatin1String("insert"), changePosition, textInserted.length(), QVariant::fromValue(data)); + sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT), + QLatin1String("TextChanged"), args); + } + + // send a cursor update + Q_UNUSED(cursorPosition) +// QDBusVariant cursorData; +// cursorData.setVariant(QVariant::fromValue(cursorPosition)); +// QVariantList args = packDBusSignalArguments(QString(), cursorPosition, 0, QVariant::fromValue(cursorData)); +// sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT), +// QLatin1String("TextCaretMoved"), args); + } + break; + } + case QAccessible::TextCaretMoved: { + if (sendObject || sendObject_text_caret_moved) { + QAccessibleInterface * iface = event->accessibleInterface(); + if (!iface || !iface->textInterface()) { + qCWarning(lcAccessibilityAtspi) << "Sending TextCaretMoved from object that does not implement text interface: " << iface; + return; + } + + QString path = pathForInterface(iface); + QDBusVariant cursorData; + int pos = iface->textInterface()->cursorPosition(); + cursorData.setVariant(QVariant::fromValue(pos)); + QVariantList args = packDBusSignalArguments(QString(), pos, 0, QVariant::fromValue(cursorData)); + sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT), + QLatin1String("TextCaretMoved"), args); + } + break; + } + case QAccessible::TextSelectionChanged: { + if (sendObject || sendObject_text_selection_changed) { + QAccessibleInterface * iface = event->accessibleInterface(); + QString path = pathForInterface(iface); + QVariantList args = packDBusSignalArguments(QString(), 0, 0, QVariant::fromValue(QDBusVariant(QVariant(QString())))); + sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT), + QLatin1String("TextSelectionChanged"), args); + } + break; + } + case QAccessible::ValueChanged: { + if (sendObject || sendObject_value_changed || sendObject_property_change_accessible_value) { + QAccessibleInterface * iface = event->accessibleInterface(); + if (!iface) { + qCWarning(lcAccessibilityAtspi) << "ValueChanged event from invalid accessible."; + return; + } + if (iface->valueInterface()) { + QString path = pathForInterface(iface); + QVariantList args = packDBusSignalArguments(QLatin1String("accessible-value"), 0, 0, variantForPath(path)); + sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT), + QLatin1String("PropertyChange"), args); + } else if (iface->role() == QAccessible::ComboBox) { + // Combo Box with AT-SPI likes to be special + // It requires a name-change to update caches and then selection-changed + QString path = pathForInterface(iface); + QVariantList args1 = packDBusSignalArguments(QLatin1String("accessible-name"), 0, 0, variantForPath(path)); + sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT), + QLatin1String("PropertyChange"), args1); + QVariantList args2 = packDBusSignalArguments(QString(), 0, 0, QVariant::fromValue(QDBusVariant(QVariant(0)))); + sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT), + QLatin1String("SelectionChanged"), args2); + } else { + qCWarning(lcAccessibilityAtspi) << "ValueChanged event and no ValueInterface or ComboBox: " << iface; + } + } + break; + } + case QAccessible::SelectionAdd: + case QAccessible::SelectionRemove: + case QAccessible::Selection: { + QAccessibleInterface * iface = event->accessibleInterface(); + if (!iface) { + qCWarning(lcAccessibilityAtspi) << "Selection event from invalid accessible."; + return; + } + QString path = pathForInterface(iface); + int selected = iface->state().selected ? 1 : 0; + QVariantList stateArgs = packDBusSignalArguments(QLatin1String("selected"), selected, 0, variantForPath(path)); + sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT), + QLatin1String("StateChanged"), stateArgs); + break; + } + + case QAccessible::StateChanged: { + if (sendObject || sendObject_state_changed || sendWindow || sendWindow_activate) { + QAccessible::State stateChange = static_cast(event)->changedStates(); + if (stateChange.checked) { + QAccessibleInterface * iface = event->accessibleInterface(); + if (!iface) { + qCWarning(lcAccessibilityAtspi) << "StateChanged event from invalid accessible."; + return; + } + int checked = iface->state().checked; + notifyStateChange(iface, QLatin1String("checked"), checked); + } else if (stateChange.active) { + QAccessibleInterface * iface = event->accessibleInterface(); + if (!iface || !(iface->role() == QAccessible::Window && (sendWindow || sendWindow_activate))) + return; + int isActive = iface->state().active; + QString windowTitle = iface->text(QAccessible::Name); + QDBusVariant data; + data.setVariant(windowTitle); + QVariantList args = packDBusSignalArguments(QString(), 0, 0, QVariant::fromValue(data)); + QString status = isActive ? QLatin1String("Activate") : QLatin1String("Deactivate"); + QString path = pathForInterface(iface); + sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_WINDOW), status, args); + notifyStateChange(iface, QLatin1String("active"), isActive); + } else if (stateChange.disabled) { + QAccessibleInterface *iface = event->accessibleInterface(); + QAccessible::State state = iface->state(); + bool enabled = !state.disabled; + + notifyStateChange(iface, QLatin1String("enabled"), enabled); + notifyStateChange(iface, QLatin1String("sensitive"), enabled); + } + } + break; + } + // For now we ignore these events + case QAccessible::TableModelChanged: + // For tables, setting manages_descendants should + // indicate to the client that it cannot cache these + // interfaces. + case QAccessible::ParentChanged: + case QAccessible::DialogStart: + case QAccessible::DialogEnd: + case QAccessible::PopupMenuStart: + case QAccessible::PopupMenuEnd: + case QAccessible::SoundPlayed: + case QAccessible::Alert: + case QAccessible::ForegroundChanged: + case QAccessible::MenuStart: + case QAccessible::MenuEnd: + case QAccessible::ContextHelpStart: + case QAccessible::ContextHelpEnd: + case QAccessible::DragDropStart: + case QAccessible::DragDropEnd: + case QAccessible::ScrollingStart: + case QAccessible::ScrollingEnd: + case QAccessible::MenuCommand: + case QAccessible::ActionChanged: + case QAccessible::ActiveDescendantChanged: + case QAccessible::AttributeChanged: + case QAccessible::DocumentContentChanged: + case QAccessible::DocumentLoadComplete: + case QAccessible::DocumentLoadStopped: + case QAccessible::DocumentReload: + case QAccessible::HyperlinkEndIndexChanged: + case QAccessible::HyperlinkNumberOfAnchorsChanged: + case QAccessible::HyperlinkSelectedLinkChanged: + case QAccessible::HypertextLinkActivated: + case QAccessible::HypertextLinkSelected: + case QAccessible::HyperlinkStartIndexChanged: + case QAccessible::HypertextChanged: + case QAccessible::HypertextNLinksChanged: + case QAccessible::ObjectAttributeChanged: + case QAccessible::PageChanged: + case QAccessible::SectionChanged: + case QAccessible::TableCaptionChanged: + case QAccessible::TableColumnDescriptionChanged: + case QAccessible::TableColumnHeaderChanged: + case QAccessible::TableRowDescriptionChanged: + case QAccessible::TableRowHeaderChanged: + case QAccessible::TableSummaryChanged: + case QAccessible::TextAttributeChanged: + case QAccessible::TextColumnChanged: + case QAccessible::VisibleDataChanged: + case QAccessible::SelectionWithin: + case QAccessible::LocationChanged: + case QAccessible::HelpChanged: + case QAccessible::DefaultActionChanged: + case QAccessible::AcceleratorChanged: + case QAccessible::InvalidEvent: + break; + } +} + +void AtSpiAdaptor::sendFocusChanged(QAccessibleInterface *interface) const +{ + static QString lastFocusPath; + // "remove" old focus + if (!lastFocusPath.isEmpty()) { + QVariantList stateArgs = packDBusSignalArguments(QLatin1String("focused"), 0, 0, variantForPath(lastFocusPath)); + sendDBusSignal(lastFocusPath, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT), + QLatin1String("StateChanged"), stateArgs); + } + // send new focus + { + QString path = pathForInterface(interface); + + QVariantList stateArgs = packDBusSignalArguments(QLatin1String("focused"), 1, 0, variantForPath(path)); + sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT), + QLatin1String("StateChanged"), stateArgs); + + QVariantList focusArgs = packDBusSignalArguments(QString(), 0, 0, variantForPath(path)); + sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_FOCUS), + QLatin1String("Focus"), focusArgs); + lastFocusPath = path; + } +} + +void AtSpiAdaptor::childrenChanged(QAccessibleInterface *interface) const +{ + QString parentPath = pathForInterface(interface); + int childCount = interface->childCount(); + for (int i = 0; i < interface->childCount(); ++i) { + QString childPath = pathForInterface(interface->child(i)); + QVariantList args = packDBusSignalArguments(QLatin1String("add"), childCount, 0, childPath); + sendDBusSignal(parentPath, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT), QLatin1String("ChildrenChanged"), args); + } +} + +void AtSpiAdaptor::notifyAboutCreation(QAccessibleInterface *interface) const +{ +// // say hello to d-bus +// cache->emitAddAccessible(accessible->getCacheItem()); + + // notify about the new child of our parent + QAccessibleInterface * parent = interface->parent(); + if (!parent) { + qCDebug(lcAccessibilityAtspi) << "AtSpiAdaptor::notifyAboutCreation: Could not find parent for " << interface->object(); + return; + } + QString path = pathForInterface(interface); + int childCount = parent->childCount(); + QString parentPath = pathForInterface(parent); + QVariantList args = packDBusSignalArguments(QLatin1String("add"), childCount, 0, variantForPath(path)); + sendDBusSignal(parentPath, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT), QLatin1String("ChildrenChanged"), args); +} + +void AtSpiAdaptor::notifyAboutDestruction(QAccessibleInterface *interface) const +{ + if (!interface || !interface->isValid()) + return; + + QAccessibleInterface * parent = interface->parent(); + if (!parent) { + qCDebug(lcAccessibilityAtspi) << "AtSpiAdaptor::notifyAboutDestruction: Could not find parent for " << interface->object(); + return; + } + QString path = pathForInterface(interface); + + // this is in the destructor. we have no clue which child we used to be. + // FIXME + int childIndex = -1; + // if (child) { + // childIndex = child; + // } else { + // childIndex = parent->indexOfChild(interface); + // } + + QString parentPath = pathForInterface(parent); + QVariantList args = packDBusSignalArguments(QLatin1String("remove"), childIndex, 0, variantForPath(path)); + sendDBusSignal(parentPath, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT), QLatin1String("ChildrenChanged"), args); +} + +/*! + Handle incoming DBus message. + This function dispatches the dbus message to the right interface handler. + */ +bool AtSpiAdaptor::handleMessage(const QDBusMessage &message, const QDBusConnection &connection) +{ + // get accessible interface + QAccessibleInterface * accessible = interfaceFromPath(message.path()); + if (!accessible) { + qCDebug(lcAccessibilityAtspi) << "WARNING Qt AtSpiAdaptor: Could not find accessible on path: " << message.path(); + return false; + } + if (!accessible->isValid()) { + qCWarning(lcAccessibilityAtspi) << "WARNING Qt AtSpiAdaptor: Accessible invalid: " << accessible << message.path(); + return false; + } + + QString interface = message.interface(); + QString function = message.member(); + + // qCDebug(lcAccessibilityAtspi) << "AtSpiAdaptor::handleMessage: " << interface << function; + + if (function == QLatin1String("Introspect")) { + //introspect(message.path()); + return false; + } + + // handle properties like regular functions + if (interface == QLatin1String("org.freedesktop.DBus.Properties")) { + interface = message.arguments().at(0).toString(); + // Get/Set + Name + function = message.member() + message.arguments().at(1).toString(); + } + + // switch interface to call + if (interface == QLatin1String(ATSPI_DBUS_INTERFACE_ACCESSIBLE)) + return accessibleInterface(accessible, function, message, connection); + if (interface == QLatin1String(ATSPI_DBUS_INTERFACE_APPLICATION)) + return applicationInterface(accessible, function, message, connection); + if (interface == QLatin1String(ATSPI_DBUS_INTERFACE_COMPONENT)) + return componentInterface(accessible, function, message, connection); + if (interface == QLatin1String(ATSPI_DBUS_INTERFACE_ACTION)) + return actionInterface(accessible, function, message, connection); + if (interface == QLatin1String(ATSPI_DBUS_INTERFACE_TEXT)) + return textInterface(accessible, function, message, connection); + if (interface == QLatin1String(ATSPI_DBUS_INTERFACE_EDITABLE_TEXT)) + return editableTextInterface(accessible, function, message, connection); + if (interface == QLatin1String(ATSPI_DBUS_INTERFACE_VALUE)) + return valueInterface(accessible, function, message, connection); + if (interface == QLatin1String(ATSPI_DBUS_INTERFACE_TABLE)) + return tableInterface(accessible, function, message, connection); + + qCDebug(lcAccessibilityAtspi) << "AtSpiAdaptor::handleMessage with unknown interface: " << message.path() << interface << function; + return false; +} + +// Application +bool AtSpiAdaptor::applicationInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection) +{ + if (message.path() != QLatin1String(ATSPI_DBUS_PATH_ROOT)) { + qCDebug(lcAccessibilityAtspi) << "WARNING Qt AtSpiAdaptor: Could not find application interface for: " << message.path() << interface; + return false; + } + + if (function == QLatin1String("SetId")) { + Q_ASSERT(message.signature() == QLatin1String("ssv")); + QVariant value = qvariant_cast(message.arguments().at(2)).variant(); + + m_applicationId = value.toInt(); + return true; + } + if (function == QLatin1String("GetId")) { + Q_ASSERT(message.signature() == QLatin1String("ss")); + QDBusMessage reply = message.createReply(QVariant::fromValue(QDBusVariant(m_applicationId))); + return connection.send(reply); + } + if (function == QLatin1String("GetToolkitName")) { + Q_ASSERT(message.signature() == QLatin1String("ss")); + QDBusMessage reply = message.createReply(QVariant::fromValue(QDBusVariant(QLatin1String("Qt")))); + return connection.send(reply); + } + if (function == QLatin1String("GetVersion")) { + Q_ASSERT(message.signature() == QLatin1String("ss")); + QDBusMessage reply = message.createReply(QVariant::fromValue(QDBusVariant(QLatin1String(qVersion())))); + return connection.send(reply); + } + if (function == QLatin1String("GetLocale")) { + Q_ASSERT(message.signature() == QLatin1String("u")); + QDBusMessage reply = message.createReply(QVariant::fromValue(QLocale().name())); + return connection.send(reply); + } + qCDebug(lcAccessibilityAtspi) << "AtSpiAdaptor::applicationInterface " << message.path() << interface << function; + return false; +} + +/*! + Register this application as accessible on the accessibility DBus. + */ +void AtSpiAdaptor::registerApplication() +{ + OrgA11yAtspiSocketInterface *registry; + registry = new OrgA11yAtspiSocketInterface(QLatin1String(QSPI_REGISTRY_NAME), + QLatin1String(QSPI_OBJECT_PATH_ROOT), m_dbus->connection()); + + QDBusPendingReply reply; + QSpiObjectReference ref = QSpiObjectReference(m_dbus->connection(), QDBusObjectPath(QSPI_OBJECT_PATH_ROOT)); + reply = registry->Embed(ref); + reply.waitForFinished(); // TODO: make this async + if (reply.isValid ()) { + const QSpiObjectReference &socket = reply.value(); + accessibilityRegistry = QSpiObjectReference(socket); + } else { + qCDebug(lcAccessibilityAtspi) << "Error in contacting registry: " + << reply.error().name() + << reply.error().message(); + } + delete registry; +} + +// Accessible +bool AtSpiAdaptor::accessibleInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection) +{ + if (function == QLatin1String("GetRole")) { + sendReply(connection, message, (uint) getRole(interface)); + } else if (function == QLatin1String("GetName")) { + sendReply(connection, message, QVariant::fromValue(QDBusVariant(interface->text(QAccessible::Name)))); + } else if (function == QLatin1String("GetRoleName")) { + sendReply(connection, message, qSpiRoleMapping[interface->role()].name()); + } else if (function == QLatin1String("GetLocalizedRoleName")) { + sendReply(connection, message, QVariant::fromValue(qSpiRoleMapping[interface->role()].localizedName())); + } else if (function == QLatin1String("GetChildCount")) { + sendReply(connection, message, QVariant::fromValue(QDBusVariant(interface->childCount()))); + } else if (function == QLatin1String("GetIndexInParent")) { + int childIndex = -1; + QAccessibleInterface * parent = interface->parent(); + if (parent) { + childIndex = parent->indexOfChild(interface); + if (childIndex < 0) { + qCDebug(lcAccessibilityAtspi) << "GetIndexInParent get invalid index: " << childIndex << interface; + } + } + sendReply(connection, message, childIndex); + } else if (function == QLatin1String("GetParent")) { + QString path; + QAccessibleInterface * parent = interface->parent(); + if (!parent) { + path = QLatin1String(ATSPI_DBUS_PATH_NULL); + } else if (parent->role() == QAccessible::Application) { + path = QLatin1String(ATSPI_DBUS_PATH_ROOT); + } else { + path = pathForInterface(parent); + } + // Parent is a property, so it needs to be wrapped inside an extra variant. + sendReply(connection, message, QVariant::fromValue( + QDBusVariant(QVariant::fromValue(QSpiObjectReference(connection, QDBusObjectPath(path)))))); + } else if (function == QLatin1String("GetChildAtIndex")) { + const int index = message.arguments().at(0).toInt(); + if (index < 0) { + sendReply(connection, message, QVariant::fromValue( + QSpiObjectReference(connection, QDBusObjectPath(ATSPI_DBUS_PATH_NULL)))); + } else { + QAccessibleInterface * childInterface = interface->child(index); + sendReply(connection, message, QVariant::fromValue( + QSpiObjectReference(connection, QDBusObjectPath(pathForInterface(childInterface))))); + } + } else if (function == QLatin1String("GetInterfaces")) { + sendReply(connection, message, accessibleInterfaces(interface)); + } else if (function == QLatin1String("GetDescription")) { + sendReply(connection, message, QVariant::fromValue(QDBusVariant(interface->text(QAccessible::Description)))); + } else if (function == QLatin1String("GetState")) { + quint64 spiState = spiStatesFromQState(interface->state()); + if (interface->tableInterface()) { + setSpiStateBit(&spiState, ATSPI_STATE_MANAGES_DESCENDANTS); + } + QAccessible::Role role = interface->role(); + if (role == QAccessible::TreeItem || + role == QAccessible::ListItem) { + /* Transient means libatspi2 will not cache items. + This is important because when adding/removing an item + the cache becomes outdated and we don't change the paths of + items in lists/trees/tables. */ + setSpiStateBit(&spiState, ATSPI_STATE_TRANSIENT); + } + sendReply(connection, message, + QVariant::fromValue(spiStateSetFromSpiStates(spiState))); + } else if (function == QLatin1String("GetAttributes")) { + sendReply(connection, message, QVariant::fromValue(QSpiAttributeSet())); + } else if (function == QLatin1String("GetRelationSet")) { + sendReply(connection, message, QVariant::fromValue(relationSet(interface, connection))); + } else if (function == QLatin1String("GetApplication")) { + sendReply(connection, message, QVariant::fromValue( + QSpiObjectReference(connection, QDBusObjectPath(QSPI_OBJECT_PATH_ROOT)))); + } else if (function == QLatin1String("GetChildren")) { + QSpiObjectReferenceArray children; + const int numChildren = interface->childCount(); + children.reserve(numChildren); + for (int i = 0; i < numChildren; ++i) { + QString childPath = pathForInterface(interface->child(i)); + QSpiObjectReference ref(connection, QDBusObjectPath(childPath)); + children << ref; + } + connection.send(message.createReply(QVariant::fromValue(children))); + } else { + qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::accessibleInterface does not implement " << function << message.path(); + return false; + } + return true; +} + +AtspiRole AtSpiAdaptor::getRole(QAccessibleInterface *interface) const +{ + if ((interface->role() == QAccessible::EditableText) && interface->state().passwordEdit) + return ATSPI_ROLE_PASSWORD_TEXT; + return qSpiRoleMapping[interface->role()].spiRole(); +} + +QStringList AtSpiAdaptor::accessibleInterfaces(QAccessibleInterface *interface) const +{ + QStringList ifaces; + qCDebug(lcAccessibilityAtspiCreation) << "AtSpiAdaptor::accessibleInterfaces create: " << interface->object(); + ifaces << QLatin1String(ATSPI_DBUS_INTERFACE_ACCESSIBLE); + + if ( (!interface->rect().isEmpty()) || + (interface->object() && interface->object()->isWidgetType()) || + (interface->role() == QAccessible::ListItem) || + (interface->role() == QAccessible::Cell) || + (interface->role() == QAccessible::TreeItem) || + (interface->role() == QAccessible::Row) || + (interface->object() && interface->object()->inherits("QSGItem")) + ) { + ifaces << QLatin1String(ATSPI_DBUS_INTERFACE_COMPONENT); + } else { + qCDebug(lcAccessibilityAtspiCreation) << " IS NOT a component"; + } + if (interface->role() == QAccessible::Application) + ifaces << QLatin1String(ATSPI_DBUS_INTERFACE_APPLICATION); + + if (interface->actionInterface() || interface->valueInterface()) + ifaces << QLatin1String(ATSPI_DBUS_INTERFACE_ACTION); + + if (interface->textInterface()) + ifaces << QLatin1String(ATSPI_DBUS_INTERFACE_TEXT); + + if (interface->editableTextInterface()) + ifaces << QLatin1String(ATSPI_DBUS_INTERFACE_EDITABLE_TEXT); + + if (interface->valueInterface()) + ifaces << QLatin1String(ATSPI_DBUS_INTERFACE_VALUE); + + if (interface->tableInterface()) + ifaces << QLatin1String(ATSPI_DBUS_INTERFACE_TABLE); + + return ifaces; +} + +QSpiRelationArray AtSpiAdaptor::relationSet(QAccessibleInterface *interface, const QDBusConnection &connection) const +{ + typedef QPair RelationPair; + const QVector relationInterfaces = interface->relations(); + + QSpiRelationArray relations; + for (const RelationPair &pair : relationInterfaces) { +// FIXME: this loop seems a bit strange... "related" always have one item when we check. +//And why is it a list, when it always have one item? And it seems to assume that the QAccessible::Relation enum maps directly to AtSpi + QSpiObjectReferenceArray related; + + QDBusObjectPath path = QDBusObjectPath(pathForInterface(pair.first)); + related.append(QSpiObjectReference(connection, path)); + + if (!related.isEmpty()) + relations.append(QSpiRelationArrayEntry(qAccessibleRelationToAtSpiRelation(pair.second), related)); + } + return relations; +} + +void AtSpiAdaptor::sendReply(const QDBusConnection &connection, const QDBusMessage &message, const QVariant &argument) const +{ + QDBusMessage reply = message.createReply(argument); + connection.send(reply); +} + + +QString AtSpiAdaptor::pathForObject(QObject *object) const +{ + Q_ASSERT(object); + + if (inheritsQAction(object)) { + qCDebug(lcAccessibilityAtspi) << "AtSpiAdaptor::pathForObject: warning: creating path with QAction as object."; + } + + QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(object); + return pathForInterface(iface); +} + +QString AtSpiAdaptor::pathForInterface(QAccessibleInterface *interface) const +{ + if (!interface || !interface->isValid()) + return QLatin1String(ATSPI_DBUS_PATH_NULL); + if (interface->role() == QAccessible::Application) + return QLatin1String(QSPI_OBJECT_PATH_ROOT); + + QAccessible::Id id = QAccessible::uniqueId(interface); + Q_ASSERT((int)id < 0); + return QLatin1String(QSPI_OBJECT_PATH_PREFIX) + QString::number(id); +} + +bool AtSpiAdaptor::inheritsQAction(QObject *object) +{ + const QMetaObject *mo = object->metaObject(); + while (mo) { + const QLatin1String cn(mo->className()); + if (cn == QLatin1String("QAction")) + return true; + mo = mo->superClass(); + } + return false; +} + +// Component +static QAccessibleInterface * getWindow(QAccessibleInterface * interface) +{ + if (interface->role() == QAccessible::Window) + return interface; + + QAccessibleInterface * parent = interface->parent(); + while (parent && parent->role() != QAccessible::Window) + parent = parent->parent(); + + return parent; +} + +static QRect getRelativeRect(QAccessibleInterface *interface) +{ + QAccessibleInterface * window; + QRect wr, cr; + + cr = interface->rect(); + + window = getWindow(interface); + if (window) { + wr = window->rect(); + + cr.setX(cr.x() - wr.x()); + cr.setY(cr.x() - wr.y()); + } + return cr; +} + +bool AtSpiAdaptor::componentInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection) +{ + if (function == QLatin1String("Contains")) { + bool ret = false; + int x = message.arguments().at(0).toInt(); + int y = message.arguments().at(1).toInt(); + uint coordType = message.arguments().at(2).toUInt(); + if (coordType == ATSPI_COORD_TYPE_SCREEN) + ret = interface->rect().contains(x, y); + else + ret = getRelativeRect(interface).contains(x, y); + sendReply(connection, message, ret); + } else if (function == QLatin1String("GetAccessibleAtPoint")) { + int x = message.arguments().at(0).toInt(); + int y = message.arguments().at(1).toInt(); + uint coordType = message.arguments().at(2).toUInt(); + if (coordType == ATSPI_COORD_TYPE_WINDOW) { + QWindow * window = interface->window(); + if (window) { + x += window->position().x(); + y += window->position().y(); + } + } + + QAccessibleInterface * childInterface(interface->childAt(x, y)); + QAccessibleInterface * iface = 0; + while (childInterface) { + iface = childInterface; + childInterface = iface->childAt(x, y); + } + if (iface) { + QString path = pathForInterface(iface); + sendReply(connection, message, QVariant::fromValue( + QSpiObjectReference(connection, QDBusObjectPath(path)))); + } else { + sendReply(connection, message, QVariant::fromValue( + QSpiObjectReference(connection, QDBusObjectPath(ATSPI_DBUS_PATH_NULL)))); + } + } else if (function == QLatin1String("GetAlpha")) { + sendReply(connection, message, (double) 1.0); + } else if (function == QLatin1String("GetExtents")) { + uint coordType = message.arguments().at(0).toUInt(); + sendReply(connection, message, QVariant::fromValue(getExtents(interface, coordType))); + } else if (function == QLatin1String("GetLayer")) { + sendReply(connection, message, QVariant::fromValue((uint)1)); + } else if (function == QLatin1String("GetMDIZOrder")) { + sendReply(connection, message, QVariant::fromValue((short)0)); + } else if (function == QLatin1String("GetPosition")) { + uint coordType = message.arguments().at(0).toUInt(); + QRect rect; + if (coordType == ATSPI_COORD_TYPE_SCREEN) + rect = interface->rect(); + else + rect = getRelativeRect(interface); + QVariantList pos; + pos << rect.x() << rect.y(); + connection.send(message.createReply(pos)); + } else if (function == QLatin1String("GetSize")) { + QRect rect = interface->rect(); + QVariantList size; + size << rect.width() << rect.height(); + connection.send(message.createReply(size)); + } else if (function == QLatin1String("GrabFocus")) { + QAccessibleActionInterface *actionIface = interface->actionInterface(); + if (actionIface && actionIface->actionNames().contains(QAccessibleActionInterface::setFocusAction())) { + actionIface->doAction(QAccessibleActionInterface::setFocusAction()); + sendReply(connection, message, true); + } else { + sendReply(connection, message, false); + } + } else if (function == QLatin1String("SetExtents")) { +// int x = message.arguments().at(0).toInt(); +// int y = message.arguments().at(1).toInt(); +// int width = message.arguments().at(2).toInt(); +// int height = message.arguments().at(3).toInt(); +// uint coordinateType = message.arguments().at(4).toUInt(); + qCDebug(lcAccessibilityAtspi) << "SetExtents is not implemented."; + sendReply(connection, message, false); + } else if (function == QLatin1String("SetPosition")) { +// int x = message.arguments().at(0).toInt(); +// int y = message.arguments().at(1).toInt(); +// uint coordinateType = message.arguments().at(2).toUInt(); + qCDebug(lcAccessibilityAtspi) << "SetPosition is not implemented."; + sendReply(connection, message, false); + } else if (function == QLatin1String("SetSize")) { +// int width = message.arguments().at(0).toInt(); +// int height = message.arguments().at(1).toInt(); + qCDebug(lcAccessibilityAtspi) << "SetSize is not implemented."; + sendReply(connection, message, false); + } else { + qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::componentInterface does not implement " << function << message.path(); + return false; + } + return true; +} + +QRect AtSpiAdaptor::getExtents(QAccessibleInterface *interface, uint coordType) +{ + return (coordType == ATSPI_COORD_TYPE_SCREEN) ? interface->rect() : getRelativeRect(interface); +} + +// Action interface +bool AtSpiAdaptor::actionInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection) +{ + if (function == QLatin1String("GetNActions")) { + int count = QAccessibleBridgeUtils::effectiveActionNames(interface).count(); + sendReply(connection, message, QVariant::fromValue(QDBusVariant(QVariant::fromValue(count)))); + } else if (function == QLatin1String("DoAction")) { + int index = message.arguments().at(0).toInt(); + const QStringList actionNames = QAccessibleBridgeUtils::effectiveActionNames(interface); + if (index < 0 || index >= actionNames.count()) + return false; + const QString actionName = actionNames.at(index); + bool success = QAccessibleBridgeUtils::performEffectiveAction(interface, actionName); + sendReply(connection, message, success); + } else if (function == QLatin1String("GetActions")) { + sendReply(connection, message, QVariant::fromValue(getActions(interface))); + } else if (function == QLatin1String("GetName")) { + int index = message.arguments().at(0).toInt(); + const QStringList actionNames = QAccessibleBridgeUtils::effectiveActionNames(interface); + if (index < 0 || index >= actionNames.count()) + return false; + sendReply(connection, message, actionNames.at(index)); + } else if (function == QLatin1String("GetDescription")) { + int index = message.arguments().at(0).toInt(); + const QStringList actionNames = QAccessibleBridgeUtils::effectiveActionNames(interface); + if (index < 0 || index >= actionNames.count()) + return false; + QString description; + if (QAccessibleActionInterface *actionIface = interface->actionInterface()) + description = actionIface->localizedActionDescription(actionNames.at(index)); + else + description = qAccessibleLocalizedActionDescription(actionNames.at(index)); + sendReply(connection, message, description); + } else if (function == QLatin1String("GetKeyBinding")) { + int index = message.arguments().at(0).toInt(); + const QStringList actionNames = QAccessibleBridgeUtils::effectiveActionNames(interface); + if (index < 0 || index >= actionNames.count()) + return false; + QStringList keyBindings; + if (QAccessibleActionInterface *actionIface = interface->actionInterface()) + keyBindings = actionIface->keyBindingsForAction(actionNames.at(index)); + if (keyBindings.isEmpty()) { + QString acc = interface->text(QAccessible::Accelerator); + if (!acc.isEmpty()) + keyBindings.append(acc); + } + if (keyBindings.length() > 0) + sendReply(connection, message, keyBindings.join(QLatin1Char(';'))); + else + sendReply(connection, message, QString()); + } else { + qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::actionInterface does not implement " << function << message.path(); + return false; + } + return true; +} + +QSpiActionArray AtSpiAdaptor::getActions(QAccessibleInterface *interface) const +{ + QAccessibleActionInterface *actionInterface = interface->actionInterface(); + QSpiActionArray actions; + const QStringList actionNames = QAccessibleBridgeUtils::effectiveActionNames(interface); + actions.reserve(actionNames.size()); + for (const QString &actionName : actionNames) { + QSpiAction action; + + action.name = actionName; + if (actionInterface) { + action.description = actionInterface->localizedActionDescription(actionName); + const QStringList keyBindings = actionInterface->keyBindingsForAction(actionName); + if (!keyBindings.isEmpty()) + action.keyBinding = keyBindings.front(); + } else { + action.description = qAccessibleLocalizedActionDescription(actionName); + } + + actions.append(std::move(action)); + } + return actions; +} + +// Text interface +bool AtSpiAdaptor::textInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection) +{ + if (!interface->textInterface()) + return false; + + // properties + if (function == QLatin1String("GetCaretOffset")) { + sendReply(connection, message, QVariant::fromValue(QDBusVariant(QVariant::fromValue(interface->textInterface()->cursorPosition())))); + } else if (function == QLatin1String("GetCharacterCount")) { + sendReply(connection, message, QVariant::fromValue(QDBusVariant(QVariant::fromValue(interface->textInterface()->characterCount())))); + + // functions + } else if (function == QLatin1String("AddSelection")) { + int startOffset = message.arguments().at(0).toInt(); + int endOffset = message.arguments().at(1).toInt(); + int lastSelection = interface->textInterface()->selectionCount(); + interface->textInterface()->setSelection(lastSelection, startOffset, endOffset); + sendReply(connection, message, (interface->textInterface()->selectionCount() > lastSelection)); + } else if (function == QLatin1String("GetAttributeRun")) { + int offset = message.arguments().at(0).toInt(); + bool includeDefaults = message.arguments().at(1).toBool(); + Q_UNUSED(includeDefaults) + connection.send(message.createReply(getAttributes(interface, offset, includeDefaults))); + } else if (function == QLatin1String("GetAttributeValue")) { + int offset = message.arguments().at(0).toInt(); + QString attributeName = message.arguments().at(1).toString(); + connection.send(message.createReply(getAttributeValue(interface, offset, attributeName))); + } else if (function == QLatin1String("GetAttributes")) { + int offset = message.arguments().at(0).toInt(); + connection.send(message.createReply(getAttributes(interface, offset, true))); + } else if (function == QLatin1String("GetBoundedRanges")) { + int x = message.arguments().at(0).toInt(); + int y = message.arguments().at(1).toInt(); + int width = message.arguments().at(2).toInt(); + int height = message.arguments().at(3).toInt(); + uint coordType = message.arguments().at(4).toUInt(); + uint xClipType = message.arguments().at(5).toUInt(); + uint yClipType = message.arguments().at(6).toUInt(); + Q_UNUSED(x); + Q_UNUSED(y); + Q_UNUSED(width); + Q_UNUSED(height); + Q_UNUSED(coordType); + Q_UNUSED(xClipType); + Q_UNUSED(yClipType); + qCDebug(lcAccessibilityAtspi) << "Not implemented: QSpiAdaptor::GetBoundedRanges"; + sendReply(connection, message, QVariant::fromValue(QSpiTextRangeList())); + } else if (function == QLatin1String("GetCharacterAtOffset")) { + int offset = message.arguments().at(0).toInt(); + int start; + int end; + QString result = interface->textInterface()->textAtOffset(offset, QAccessible::CharBoundary, &start, &end); + sendReply(connection, message, (int) *(qPrintable (result))); + } else if (function == QLatin1String("GetCharacterExtents")) { + int offset = message.arguments().at(0).toInt(); + int coordType = message.arguments().at(1).toUInt(); + connection.send(message.createReply(getCharacterExtents(interface, offset, coordType))); + } else if (function == QLatin1String("GetDefaultAttributeSet") || function == QLatin1String("GetDefaultAttributes")) { + // GetDefaultAttributes is deprecated in favour of GetDefaultAttributeSet. + // Empty set seems reasonable. There is no default attribute set. + sendReply(connection, message, QVariant::fromValue(QSpiAttributeSet())); + } else if (function == QLatin1String("GetNSelections")) { + sendReply(connection, message, interface->textInterface()->selectionCount()); + } else if (function == QLatin1String("GetOffsetAtPoint")) { + qCDebug(lcAccessibilityAtspi) << message.signature(); + Q_ASSERT(!message.signature().isEmpty()); + QPoint point(message.arguments().at(0).toInt(), message.arguments().at(1).toInt()); + uint coordType = message.arguments().at(2).toUInt(); + if (coordType == ATSPI_COORD_TYPE_WINDOW) { + QWindow *win = interface->window(); + point -= QPoint(win->x(), win->y()); + } + int offset = interface->textInterface()->offsetAtPoint(point); + sendReply(connection, message, offset); + } else if (function == QLatin1String("GetRangeExtents")) { + int startOffset = message.arguments().at(0).toInt(); + int endOffset = message.arguments().at(1).toInt(); + uint coordType = message.arguments().at(2).toUInt(); + connection.send(message.createReply(getRangeExtents(interface, startOffset, endOffset, coordType))); + } else if (function == QLatin1String("GetSelection")) { + int selectionNum = message.arguments().at(0).toInt(); + int start, end; + interface->textInterface()->selection(selectionNum, &start, &end); + if (start < 0) + start = end = interface->textInterface()->cursorPosition(); + QVariantList sel; + sel << start << end; + connection.send(message.createReply(sel)); + } else if (function == QLatin1String("GetText")) { + int startOffset = message.arguments().at(0).toInt(); + int endOffset = message.arguments().at(1).toInt(); + if (endOffset == -1) // AT-SPI uses -1 to signal all characters + endOffset = interface->textInterface()->characterCount(); + sendReply(connection, message, interface->textInterface()->text(startOffset, endOffset)); + } else if (function == QLatin1String("GetTextAfterOffset")) { + int offset = message.arguments().at(0).toInt(); + int type = message.arguments().at(1).toUInt(); + int startOffset, endOffset; + QString text = interface->textInterface()->textAfterOffset(offset, qAccessibleBoundaryType(type), &startOffset, &endOffset); + QVariantList ret; + ret << text << startOffset << endOffset; + connection.send(message.createReply(ret)); + } else if (function == QLatin1String("GetTextAtOffset")) { + int offset = message.arguments().at(0).toInt(); + int type = message.arguments().at(1).toUInt(); + int startOffset, endOffset; + QString text = interface->textInterface()->textAtOffset(offset, qAccessibleBoundaryType(type), &startOffset, &endOffset); + QVariantList ret; + ret << text << startOffset << endOffset; + connection.send(message.createReply(ret)); + } else if (function == QLatin1String("GetTextBeforeOffset")) { + int offset = message.arguments().at(0).toInt(); + int type = message.arguments().at(1).toUInt(); + int startOffset, endOffset; + QString text = interface->textInterface()->textBeforeOffset(offset, qAccessibleBoundaryType(type), &startOffset, &endOffset); + QVariantList ret; + ret << text << startOffset << endOffset; + connection.send(message.createReply(ret)); + } else if (function == QLatin1String("RemoveSelection")) { + int selectionNum = message.arguments().at(0).toInt(); + interface->textInterface()->removeSelection(selectionNum); + sendReply(connection, message, true); + } else if (function == QLatin1String("SetCaretOffset")) { + int offset = message.arguments().at(0).toInt(); + interface->textInterface()->setCursorPosition(offset); + sendReply(connection, message, true); + } else if (function == QLatin1String("SetSelection")) { + int selectionNum = message.arguments().at(0).toInt(); + int startOffset = message.arguments().at(1).toInt(); + int endOffset = message.arguments().at(2).toInt(); + interface->textInterface()->setSelection(selectionNum, startOffset, endOffset); + sendReply(connection, message, true); + } else { + qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::textInterface does not implement " << function << message.path(); + return false; + } + return true; +} + +QAccessible::TextBoundaryType AtSpiAdaptor::qAccessibleBoundaryType(int atspiTextBoundaryType) const +{ + switch (atspiTextBoundaryType) { + case ATSPI_TEXT_BOUNDARY_CHAR: + return QAccessible::CharBoundary; + case ATSPI_TEXT_BOUNDARY_WORD_START: + case ATSPI_TEXT_BOUNDARY_WORD_END: + return QAccessible::WordBoundary; + case ATSPI_TEXT_BOUNDARY_SENTENCE_START: + case ATSPI_TEXT_BOUNDARY_SENTENCE_END: + return QAccessible::SentenceBoundary; + case ATSPI_TEXT_BOUNDARY_LINE_START: + case ATSPI_TEXT_BOUNDARY_LINE_END: + return QAccessible::LineBoundary; + } + Q_ASSERT_X(0, "", "Requested invalid boundary type."); + return QAccessible::CharBoundary; +} + +namespace +{ + struct AtSpiAttribute { + QString name; + QString value; + AtSpiAttribute(const QString &aName, const QString &aValue) : name(aName), value(aValue) {} + bool isNull() const { return name.isNull() || value.isNull(); } + }; + + QString atspiColor(const QString &ia2Color) + { + // "rgb(%u,%u,%u)" -> "%u,%u,%u" + return ia2Color.mid(4, ia2Color.length() - (4+1)); + } + + QString atspiSize(const QString &ia2Size) + { + // "%fpt" -> "%f" + return ia2Size.left(ia2Size.length() - 2); + } + + AtSpiAttribute atspiTextAttribute(const QString &ia2Name, const QString &ia2Value) + { + QString name = ia2Name; + QString value = ia2Value; + + // IAccessible2: http://www.linuxfoundation.org/collaborate/workgroups/accessibility/iaccessible2/textattributes + // ATK attribute names: https://git.gnome.org/browse/orca/tree/src/orca/text_attribute_names.py + // ATK attribute values: https://developer.gnome.org/atk/unstable/AtkText.html#AtkTextAttribute + + // https://bugzilla.gnome.org/show_bug.cgi?id=744553 "ATK docs provide no guidance for allowed values of some text attributes" + // specifically for "weight", "invalid", "language" and value range for colors + + if (ia2Name == QLatin1String("background-color")) { + name = QStringLiteral("bg-color"); + value = atspiColor(value); + } else if (ia2Name == QLatin1String("font-family")) { + name = QStringLiteral("family-name"); + } else if (ia2Name == QLatin1String("color")) { + name = QStringLiteral("fg-color"); + value = atspiColor(value); + } else if (ia2Name == QLatin1String("text-align")) { + name = QStringLiteral("justification"); + if (value == QLatin1String("justify")) { + value = QStringLiteral("fill"); + } else { + if (value != QLatin1String("left") && + value != QLatin1String("right") && + value != QLatin1String("center") + ) { + value = QString(); + qCDebug(lcAccessibilityAtspi) << "Unknown text-align attribute value \"" << value << "\" cannot be translated to AT-SPI."; + } + } + } else if (ia2Name == QLatin1String("font-size")) { + name = QStringLiteral("size"); + value = atspiSize(value); + } else if (ia2Name == QLatin1String("font-style")) { + name = QStringLiteral("style"); + if (value != QLatin1String("normal") && + value != QLatin1String("italic") && + value != QLatin1String("oblique") + ) { + value = QString(); + qCDebug(lcAccessibilityAtspi) << "Unknown font-style attribute value \"" << value << "\" cannot be translated to AT-SPI."; + } + } else if (ia2Name == QLatin1String("text-underline-type")) { + name = QStringLiteral("underline"); + if (value != QLatin1String("none") && + value != QLatin1String("single") && + value != QLatin1String("double") + ) { + value = QString(); + qCDebug(lcAccessibilityAtspi) << "Unknown text-underline-type attribute value \"" << value << "\" cannot be translated to AT-SPI."; + } + } else if (ia2Name == QLatin1String("font-weight")) { + name = QStringLiteral("weight"); + if (value == QLatin1String("normal")) + // Orca seems to accept all IAccessible2 values except for "normal" + // (on which it produces traceback and fails to read any following text attributes), + // but that is the default value, so omit it anyway + value = QString(); + } else if (ia2Name == QLatin1String("text-position")) { + name = QStringLiteral("vertical-align"); + if (value != QLatin1String("baseline") && + value != QLatin1String("super") && + value != QLatin1String("sub") + ) { + value = QString(); + qCDebug(lcAccessibilityAtspi) << "Unknown text-position attribute value \"" << value << "\" cannot be translated to AT-SPI."; + } + } else if (ia2Name == QLatin1String("writing-mode")) { + name = QStringLiteral("direction"); + if (value == QLatin1String("lr")) + value = QStringLiteral("ltr"); + else if (value == QLatin1String("rl")) + value = QStringLiteral("rtl"); + else if (value == QLatin1String("tb")) { + // IAccessible2 docs refer to XSL, which specifies "tb" is shorthand for "tb-rl"; so at least give a hint about the horizontal direction (ATK does not support vertical direction in this attribute (yet)) + value = QStringLiteral("rtl"); + qCDebug(lcAccessibilityAtspi) << "writing-mode attribute value \"tb\" translated only w.r.t. horizontal direction; vertical direction ignored"; + } else { + value = QString(); + qCDebug(lcAccessibilityAtspi) << "Unknown writing-mode attribute value \"" << value << "\" cannot be translated to AT-SPI."; + } + } else if (ia2Name == QLatin1String("language")) { + // OK - ATK has no docs on the format of the value, IAccessible2 has reasonable format - leave it at that now + } else if (ia2Name == QLatin1String("invalid")) { + // OK - ATK docs are vague but suggest they support the same range of values as IAccessible2 + } else { + // attribute we know nothing about + name = QString(); + value = QString(); + } + return AtSpiAttribute(name, value); + } +} + +// FIXME all attribute methods below should share code +QVariantList AtSpiAdaptor::getAttributes(QAccessibleInterface *interface, int offset, bool includeDefaults) const +{ + Q_UNUSED(includeDefaults); + + QSpiAttributeSet set; + int startOffset; + int endOffset; + + QString joined = interface->textInterface()->attributes(offset, &startOffset, &endOffset); + const QStringList attributes = joined.split (QLatin1Char(';'), Qt::SkipEmptyParts, Qt::CaseSensitive); + for (const QString &attr : attributes) { + QStringList items; + items = attr.split(QLatin1Char(':'), Qt::SkipEmptyParts, Qt::CaseSensitive); + AtSpiAttribute attribute = atspiTextAttribute(items[0], items[1]); + if (!attribute.isNull()) + set[attribute.name] = attribute.value; + } + + QVariantList list; + list << QVariant::fromValue(set) << startOffset << endOffset; + + return list; +} + +QVariantList AtSpiAdaptor::getAttributeValue(QAccessibleInterface *interface, int offset, const QString &attributeName) const +{ + QString mapped; + QString joined; + QSpiAttributeSet map; + int startOffset; + int endOffset; + + joined = interface->textInterface()->attributes(offset, &startOffset, &endOffset); + const QStringList attributes = joined.split (QLatin1Char(';'), Qt::SkipEmptyParts, Qt::CaseSensitive); + for (const QString& attr : attributes) { + QStringList items; + items = attr.split(QLatin1Char(':'), Qt::SkipEmptyParts, Qt::CaseSensitive); + AtSpiAttribute attribute = atspiTextAttribute(items[0], items[1]); + if (!attribute.isNull()) + map[attribute.name] = attribute.value; + } + mapped = map[attributeName]; + const bool defined = !mapped.isEmpty(); + QVariantList list; + list << mapped << startOffset << endOffset << defined; + return list; +} + +QList AtSpiAdaptor::getCharacterExtents(QAccessibleInterface *interface, int offset, uint coordType) const +{ + QRect rect = interface->textInterface()->characterRect(offset); + + if (coordType == ATSPI_COORD_TYPE_WINDOW) + rect = translateRectToWindowCoordinates(interface, rect); + + return QList() << rect.x() << rect.y() << rect.width() << rect.height(); +} + +QList AtSpiAdaptor::getRangeExtents(QAccessibleInterface *interface, + int startOffset, int endOffset, uint coordType) const +{ + if (endOffset == -1) + endOffset = interface->textInterface()->characterCount(); + + QAccessibleTextInterface *textInterface = interface->textInterface(); + if (endOffset <= startOffset || !textInterface) + return QList() << -1 << -1 << 0 << 0; + + QRect rect = textInterface->characterRect(startOffset); + for (int i=startOffset + 1; i <= endOffset; i++) + rect = rect | textInterface->characterRect(i); + + // relative to window + if (coordType == ATSPI_COORD_TYPE_WINDOW) + rect = translateRectToWindowCoordinates(interface, rect); + + return QList() << rect.x() << rect.y() << rect.width() << rect.height(); +} + +QRect AtSpiAdaptor::translateRectToWindowCoordinates(QAccessibleInterface *interface, const QRect &rect) +{ + QAccessibleInterface * window = getWindow(interface); + if (window) + return rect.translated(-window->rect().x(), -window->rect().y()); + + return rect; +} + + +// Editable Text interface +static QString textForRange(QAccessibleInterface *accessible, int startOffset, int endOffset) +{ + if (QAccessibleTextInterface *textIface = accessible->textInterface()) { + if (endOffset == -1) + endOffset = textIface->characterCount(); + return textIface->text(startOffset, endOffset); + } + QString txt = accessible->text(QAccessible::Value); + if (endOffset == -1) + endOffset = txt.length(); + return txt.mid(startOffset, endOffset - startOffset); +} + +static void replaceTextFallback(QAccessibleInterface *accessible, long startOffset, long endOffset, const QString &txt) +{ + QString t = textForRange(accessible, 0, -1); + if (endOffset == -1) + endOffset = t.length(); + if (endOffset - startOffset == 0) + t.insert(startOffset, txt); + else + t.replace(startOffset, endOffset - startOffset, txt); + accessible->setText(QAccessible::Value, t); +} + +bool AtSpiAdaptor::editableTextInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection) +{ + if (function == QLatin1String("CopyText")) { +#ifndef QT_NO_CLIPBOARD + int startOffset = message.arguments().at(0).toInt(); + int endOffset = message.arguments().at(1).toInt(); + const QString t = textForRange(interface, startOffset, endOffset); + QGuiApplication::clipboard()->setText(t); +#endif + connection.send(message.createReply(true)); + } else if (function == QLatin1String("CutText")) { +#ifndef QT_NO_CLIPBOARD + int startOffset = message.arguments().at(0).toInt(); + int endOffset = message.arguments().at(1).toInt(); + const QString t = textForRange(interface, startOffset, endOffset); + if (QAccessibleEditableTextInterface *editableTextIface = interface->editableTextInterface()) + editableTextIface->deleteText(startOffset, endOffset); + else + replaceTextFallback(interface, startOffset, endOffset, QString()); + QGuiApplication::clipboard()->setText(t); +#endif + connection.send(message.createReply(true)); + } else if (function == QLatin1String("DeleteText")) { + int startOffset = message.arguments().at(0).toInt(); + int endOffset = message.arguments().at(1).toInt(); + if (QAccessibleEditableTextInterface *editableTextIface = interface->editableTextInterface()) + editableTextIface->deleteText(startOffset, endOffset); + else + replaceTextFallback(interface, startOffset, endOffset, QString()); + connection.send(message.createReply(true)); + } else if (function == QLatin1String("InsertText")) { + int position = message.arguments().at(0).toInt(); + QString text = message.arguments().at(1).toString(); + int length = message.arguments().at(2).toInt(); + text.resize(length); + if (QAccessibleEditableTextInterface *editableTextIface = interface->editableTextInterface()) + editableTextIface->insertText(position, text); + else + replaceTextFallback(interface, position, position, text); + connection.send(message.createReply(true)); + } else if (function == QLatin1String("PasteText")) { +#ifndef QT_NO_CLIPBOARD + int position = message.arguments().at(0).toInt(); + const QString txt = QGuiApplication::clipboard()->text(); + if (QAccessibleEditableTextInterface *editableTextIface = interface->editableTextInterface()) + editableTextIface->insertText(position, txt); + else + replaceTextFallback(interface, position, position, txt); +#endif + connection.send(message.createReply(true)); + } else if (function == QLatin1String("SetTextContents")) { + QString newContents = message.arguments().at(0).toString(); + if (QAccessibleEditableTextInterface *editableTextIface = interface->editableTextInterface()) + editableTextIface->replaceText(0, interface->textInterface()->characterCount(), newContents); + else + replaceTextFallback(interface, 0, -1, newContents); + connection.send(message.createReply(true)); + } else if (function == QLatin1String("")) { + connection.send(message.createReply()); + } else { + qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::editableTextInterface does not implement " << function << message.path(); + return false; + } + return true; +} + +// Value interface +bool AtSpiAdaptor::valueInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection) +{ + QAccessibleValueInterface *valueIface = interface->valueInterface(); + if (!valueIface) + return false; + + if (function == QLatin1String("SetCurrentValue")) { + QDBusVariant v = qvariant_cast(message.arguments().at(2)); + double value = v.variant().toDouble(); + //Temporary fix + //See https://bugzilla.gnome.org/show_bug.cgi?id=652596 + valueIface->setCurrentValue(value); + connection.send(message.createReply()); // FIXME is the reply needed? + } else { + QVariant value; + if (function == QLatin1String("GetCurrentValue")) + value = valueIface->currentValue(); + else if (function == QLatin1String("GetMaximumValue")) + value = valueIface->maximumValue(); + else if (function == QLatin1String("GetMinimumIncrement")) + value = valueIface->minimumStepSize(); + else if (function == QLatin1String("GetMinimumValue")) + value = valueIface->minimumValue(); + else { + qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::valueInterface does not implement " << function << message.path(); + return false; + } + if (!value.canConvert(QMetaType::Double)) { + qCDebug(lcAccessibilityAtspi) << "AtSpiAdaptor::valueInterface: Could not convert to double: " << function; + } + + // explicitly convert to dbus-variant containing one double since atspi expects that + // everything else might fail to convert back on the other end + connection.send(message.createReply( + QVariant::fromValue(QDBusVariant(QVariant::fromValue(value.toDouble()))))); + } + return true; +} + +// Table interface +bool AtSpiAdaptor::tableInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection) +{ + if (!(interface->tableInterface() || interface->tableCellInterface())) { + qCDebug(lcAccessibilityAtspi) << "WARNING Qt AtSpiAdaptor: Could not find table interface for: " << message.path() << interface; + return false; + } + + if (0) { + // properties + } else if (function == QLatin1String("GetCaption")) { + QAccessibleInterface * captionInterface= interface->tableInterface()->caption(); + if (captionInterface) { + QSpiObjectReference ref = QSpiObjectReference(connection, QDBusObjectPath(pathForInterface(captionInterface))); + sendReply(connection, message, QVariant::fromValue(ref)); + } else { + sendReply(connection, message, QVariant::fromValue( + QSpiObjectReference(connection, QDBusObjectPath(ATSPI_DBUS_PATH_NULL)))); + } + } else if (function == QLatin1String("GetNColumns")) { + connection.send(message.createReply(QVariant::fromValue(QDBusVariant( + QVariant::fromValue(interface->tableInterface()->columnCount()))))); + } else if (function == QLatin1String("GetNRows")) { + connection.send(message.createReply(QVariant::fromValue(QDBusVariant( + QVariant::fromValue(interface->tableInterface()->rowCount()))))); + } else if (function == QLatin1String("GetNSelectedColumns")) { + connection.send(message.createReply(QVariant::fromValue(QDBusVariant( + QVariant::fromValue(interface->tableInterface()->selectedColumnCount()))))); + } else if (function == QLatin1String("GetNSelectedRows")) { + connection.send(message.createReply(QVariant::fromValue(QDBusVariant( + QVariant::fromValue(interface->tableInterface()->selectedRowCount()))))); + } else if (function == QLatin1String("GetSummary")) { + QAccessibleInterface * summary = interface->tableInterface() ? interface->tableInterface()->summary() : 0; + QSpiObjectReference ref(connection, QDBusObjectPath(pathForInterface(summary))); + connection.send(message.createReply(QVariant::fromValue(QDBusVariant(QVariant::fromValue(ref))))); + } else if (function == QLatin1String("GetAccessibleAt")) { + int row = message.arguments().at(0).toInt(); + int column = message.arguments().at(1).toInt(); + if ((row < 0) || + (column < 0) || + (row >= interface->tableInterface()->rowCount()) || + (column >= interface->tableInterface()->columnCount())) { + qCDebug(lcAccessibilityAtspi) << "WARNING: invalid index for tableInterface GetAccessibleAt (" << row << ", " << column << ')'; + return false; + } + + QSpiObjectReference ref; + QAccessibleInterface * cell(interface->tableInterface()->cellAt(row, column)); + if (cell) { + ref = QSpiObjectReference(connection, QDBusObjectPath(pathForInterface(cell))); + } else { + qCDebug(lcAccessibilityAtspi) << "WARNING: no cell interface returned for " << interface->object() << row << column; + ref = QSpiObjectReference(); + } + connection.send(message.createReply(QVariant::fromValue(ref))); + + } else if (function == QLatin1String("GetIndexAt")) { + int row = message.arguments().at(0).toInt(); + int column = message.arguments().at(1).toInt(); + QAccessibleInterface *cell = interface->tableInterface()->cellAt(row, column); + if (!cell) { + qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::GetIndexAt(" << row << ',' << column << ") did not find a cell. " << interface; + return false; + } + int index = interface->indexOfChild(cell); + qCDebug(lcAccessibilityAtspi) << "QSpiAdaptor::GetIndexAt row:" << row << " col:" << column << " logical index:" << index; + Q_ASSERT(index > 0); + connection.send(message.createReply(index)); + } else if ((function == QLatin1String("GetColumnAtIndex")) || (function == QLatin1String("GetRowAtIndex"))) { + int index = message.arguments().at(0).toInt(); + int ret = -1; + if (index >= 0) { + QAccessibleInterface * cell = interface->child(index); + if (cell) { + if (function == QLatin1String("GetColumnAtIndex")) { + if (cell->role() == QAccessible::ColumnHeader) { + ret = index; + } else if (cell->role() == QAccessible::RowHeader) { + ret = -1; + } else { + if (!cell->tableCellInterface()) { + qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::" << function << " No table cell interface: " << cell; + return false; + } + ret = cell->tableCellInterface()->columnIndex(); + } + } else { + if (cell->role() == QAccessible::ColumnHeader) { + ret = -1; + } else if (cell->role() == QAccessible::RowHeader) { + ret = index % interface->tableInterface()->columnCount(); + } else { + if (!cell->tableCellInterface()) { + qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::" << function << " No table cell interface: " << cell; + return false; + } + ret = cell->tableCellInterface()->rowIndex(); + } + } + } else { + qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::" << function << " No cell at index: " << index << interface; + return false; + } + } + connection.send(message.createReply(ret)); + + } else if (function == QLatin1String("GetColumnDescription")) { + int column = message.arguments().at(0).toInt(); + connection.send(message.createReply(interface->tableInterface()->columnDescription(column))); + } else if (function == QLatin1String("GetRowDescription")) { + int row = message.arguments().at(0).toInt(); + connection.send(message.createReply(interface->tableInterface()->rowDescription(row))); + + + + } else if (function == QLatin1String("GetRowColumnExtentsAtIndex")) { + int index = message.arguments().at(0).toInt(); + bool success = false; + + int row = -1; + int col = -1; + int rowExtents = -1; + int colExtents = -1; + bool isSelected = false; + + int cols = interface->tableInterface()->columnCount(); + if (cols > 0) { + row = index / cols; + col = index % cols; + QAccessibleTableCellInterface *cell = interface->tableInterface()->cellAt(row, col)->tableCellInterface(); + if (cell) { + row = cell->rowIndex(); + col = cell->columnIndex(); + rowExtents = cell->rowExtent(); + colExtents = cell->columnExtent(); + isSelected = cell->isSelected(); + success = true; + } + } + QVariantList list; + list << success << row << col << rowExtents << colExtents << isSelected; + connection.send(message.createReply(list)); + + } else if (function == QLatin1String("GetColumnExtentAt")) { + int row = message.arguments().at(0).toInt(); + int column = message.arguments().at(1).toInt(); + connection.send(message.createReply(interface->tableInterface()->cellAt(row, column)->tableCellInterface()->columnExtent())); + + } else if (function == QLatin1String("GetRowExtentAt")) { + int row = message.arguments().at(0).toInt(); + int column = message.arguments().at(1).toInt(); + connection.send(message.createReply(interface->tableInterface()->cellAt(row, column)->tableCellInterface()->rowExtent())); + + } else if (function == QLatin1String("GetColumnHeader")) { + int column = message.arguments().at(0).toInt(); + QSpiObjectReference ref; + + QAccessibleInterface * cell(interface->tableInterface()->cellAt(0, column)); + if (cell && cell->tableCellInterface()) { + QList header = cell->tableCellInterface()->columnHeaderCells(); + if (header.size() > 0) { + ref = QSpiObjectReference(connection, QDBusObjectPath(pathForInterface(header.takeAt(0)))); + } + } + connection.send(message.createReply(QVariant::fromValue(ref))); + + } else if (function == QLatin1String("GetRowHeader")) { + int row = message.arguments().at(0).toInt(); + QSpiObjectReference ref; + QAccessibleTableCellInterface *cell = interface->tableInterface()->cellAt(row, 0)->tableCellInterface(); + if (cell) { + QList header = cell->rowHeaderCells(); + if (header.size() > 0) { + ref = QSpiObjectReference(connection, QDBusObjectPath(pathForInterface(header.takeAt(0)))); + } + } + connection.send(message.createReply(QVariant::fromValue(ref))); + + } else if (function == QLatin1String("GetSelectedColumns")) { + connection.send(message.createReply(QVariant::fromValue(interface->tableInterface()->selectedColumns()))); + } else if (function == QLatin1String("GetSelectedRows")) { + connection.send(message.createReply(QVariant::fromValue(interface->tableInterface()->selectedRows()))); + } else if (function == QLatin1String("IsColumnSelected")) { + int column = message.arguments().at(0).toInt(); + connection.send(message.createReply(interface->tableInterface()->isColumnSelected(column))); + } else if (function == QLatin1String("IsRowSelected")) { + int row = message.arguments().at(0).toInt(); + connection.send(message.createReply(interface->tableInterface()->isRowSelected(row))); + } else if (function == QLatin1String("IsSelected")) { + int row = message.arguments().at(0).toInt(); + int column = message.arguments().at(1).toInt(); + QAccessibleTableCellInterface* cell = interface->tableInterface()->cellAt(row, column)->tableCellInterface(); + connection.send(message.createReply(cell->isSelected())); + } else if (function == QLatin1String("AddColumnSelection")) { + int column = message.arguments().at(0).toInt(); + connection.send(message.createReply(interface->tableInterface()->selectColumn(column))); + } else if (function == QLatin1String("AddRowSelection")) { + int row = message.arguments().at(0).toInt(); + connection.send(message.createReply(interface->tableInterface()->selectRow(row))); + } else if (function == QLatin1String("RemoveColumnSelection")) { + int column = message.arguments().at(0).toInt(); + connection.send(message.createReply(interface->tableInterface()->unselectColumn(column))); + } else if (function == QLatin1String("RemoveRowSelection")) { + int row = message.arguments().at(0).toInt(); + connection.send(message.createReply(interface->tableInterface()->unselectRow(row))); + } else { + qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::tableInterface does not implement " << function << message.path(); + return false; + } + return true; +} + +QT_END_NAMESPACE +#endif //QT_NO_ACCESSIBILITY diff --git a/src/gui/accessible/linux/atspiadaptor_p.h b/src/gui/accessible/linux/atspiadaptor_p.h new file mode 100644 index 0000000000..b9f4011695 --- /dev/null +++ b/src/gui/accessible/linux/atspiadaptor_p.h @@ -0,0 +1,228 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef ATSPIADAPTOR_H +#define ATSPIADAPTOR_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +#include +#include +#include +#include + +#include "dbusconnection_p.h" +#include "qspi_struct_marshallers_p.h" + +QT_REQUIRE_CONFIG(accessibility); + +QT_BEGIN_NAMESPACE + +class QAccessibleInterface; +class QSpiAccessibleInterface; +class QSpiApplicationAdaptor; + + +class AtSpiAdaptor :public QDBusVirtualObject +{ + Q_OBJECT + +public: + explicit AtSpiAdaptor(DBusConnection *connection, QObject *parent = nullptr); + ~AtSpiAdaptor(); + + void registerApplication(); + QString introspect(const QString &path) const override; + bool handleMessage(const QDBusMessage &message, const QDBusConnection &connection) override; + void notify(QAccessibleEvent *event); + + void init(); + void checkInitializedAndEnabled(); +public Q_SLOTS: + void eventListenerRegistered(const QString &bus, const QString &path); + void eventListenerDeregistered(const QString &bus, const QString &path); + void windowActivated(QObject* window, bool active); + +private: + void updateEventListeners(); + void setBitFlag(const QString &flag); + + // sending messages + QVariantList packDBusSignalArguments(const QString &type, int data1, int data2, const QVariant &variantData) const; + bool sendDBusSignal(const QString &path, const QString &interface, const QString &name, const QVariantList &arguments) const; + QVariant variantForPath(const QString &path) const; + + void sendFocusChanged(QAccessibleInterface *interface) const; + void notifyAboutCreation(QAccessibleInterface *interface) const; + void notifyAboutDestruction(QAccessibleInterface *interface) const; + void childrenChanged(QAccessibleInterface *interface) const; + + // handlers for the different accessible interfaces + bool applicationInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection); + bool accessibleInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection); + bool componentInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection); + bool actionInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection); + bool textInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection); + bool editableTextInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection); + bool valueInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection); + bool tableInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection); + + void sendReply(const QDBusConnection &connection, const QDBusMessage &message, const QVariant &argument) const; + + QAccessibleInterface *interfaceFromPath(const QString& dbusPath) const; + QString pathForInterface(QAccessibleInterface *interface) const; + QString pathForObject(QObject *object) const; + + void notifyStateChange(QAccessibleInterface *interface, const QString& state, int value); + + // accessible helper functions + AtspiRole getRole(QAccessibleInterface *interface) const; + QSpiRelationArray relationSet(QAccessibleInterface *interface, const QDBusConnection &connection) const; + QStringList accessibleInterfaces(QAccessibleInterface *interface) const; + + // component helper functions + static QRect getExtents(QAccessibleInterface *interface, uint coordType); + static QRect translateRectToWindowCoordinates(QAccessibleInterface *interface, const QRect &rect); + + // action helper functions + QSpiActionArray getActions(QAccessibleInterface *interface) const; + + // text helper functions + QVariantList getAttributes(QAccessibleInterface *, int offset, bool includeDefaults) const; + QVariantList getAttributeValue(QAccessibleInterface *, int offset, const QString &attributeName) const; + QList getCharacterExtents(QAccessibleInterface *, int offset, uint coordType) const; + QList getRangeExtents(QAccessibleInterface *, int startOffset, int endOffset, uint coordType) const; + QAccessible::TextBoundaryType qAccessibleBoundaryType(int atspiTextBoundaryType) const; + static bool inheritsQAction(QObject *object); + + // private vars + QSpiObjectReference accessibilityRegistry; + DBusConnection *m_dbus; + QSpiApplicationAdaptor *m_applicationAdaptor; + + /// Assigned from the accessibility registry. + int m_applicationId; + + // Bit fields - which updates to send + + // AT-SPI has some events that we do not care about: + // document + // document-load-complete + // document-load-stopped + // document-reload + uint sendFocus : 1; + // mouse abs/rel/button + + // all of object + uint sendObject : 1; + uint sendObject_active_descendant_changed : 1; + uint sendObject_attributes_changed : 1; + uint sendObject_bounds_changed : 1; + uint sendObject_children_changed : 1; +// uint sendObject_children_changed_add : 1; +// uint sendObject_children_changed_remove : 1; + uint sendObject_column_deleted : 1; + uint sendObject_column_inserted : 1; + uint sendObject_column_reordered : 1; + uint sendObject_link_selected : 1; + uint sendObject_model_changed : 1; + uint sendObject_property_change : 1; + uint sendObject_property_change_accessible_description : 1; + uint sendObject_property_change_accessible_name : 1; + uint sendObject_property_change_accessible_parent : 1; + uint sendObject_property_change_accessible_role : 1; + uint sendObject_property_change_accessible_table_caption : 1; + uint sendObject_property_change_accessible_table_column_description : 1; + uint sendObject_property_change_accessible_table_column_header : 1; + uint sendObject_property_change_accessible_table_row_description : 1; + uint sendObject_property_change_accessible_table_row_header : 1; + uint sendObject_property_change_accessible_table_summary : 1; + uint sendObject_property_change_accessible_value : 1; + uint sendObject_row_deleted : 1; + uint sendObject_row_inserted : 1; + uint sendObject_row_reordered : 1; + uint sendObject_selection_changed : 1; + uint sendObject_state_changed : 1; + uint sendObject_text_attributes_changed : 1; + uint sendObject_text_bounds_changed : 1; + uint sendObject_text_caret_moved : 1; + uint sendObject_text_changed : 1; +// uint sendObject_text_changed_delete : 1; +// uint sendObject_text_changed_insert : 1; + uint sendObject_text_selection_changed : 1; + uint sendObject_value_changed : 1; + uint sendObject_visible_data_changed : 1; + + // we don't implement terminal + // terminal-application_changed/charwidth_changed/columncount_changed/line_changed/linecount_changed + uint sendWindow : 1; + uint sendWindow_activate : 1; + uint sendWindow_close: 1; + uint sendWindow_create : 1; + uint sendWindow_deactivate : 1; +// uint sendWindow_desktop_create : 1; +// uint sendWindow_desktop_destroy : 1; + uint sendWindow_lower : 1; + uint sendWindow_maximize : 1; + uint sendWindow_minimize : 1; + uint sendWindow_move : 1; + uint sendWindow_raise : 1; + uint sendWindow_reparent : 1; + uint sendWindow_resize : 1; + uint sendWindow_restore : 1; + uint sendWindow_restyle : 1; + uint sendWindow_shade : 1; + uint sendWindow_unshade : 1; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/gui/accessible/linux/dbusconnection.cpp b/src/gui/accessible/linux/dbusconnection.cpp new file mode 100644 index 0000000000..45ddc8e496 --- /dev/null +++ b/src/gui/accessible/linux/dbusconnection.cpp @@ -0,0 +1,172 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + + +#include "dbusconnection_p.h" + +#include +#include +#include + +#include +#include "bus_interface.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +/* note: do not change these to QStringLiteral; + we are unloaded before QtDBus is done using the strings. + */ +#define A11Y_SERVICE QLatin1String("org.a11y.Bus") +#define A11Y_PATH QLatin1String("/org/a11y/bus") + +/*! + \class DBusConnection + \internal + \brief Connects to the accessibility dbus. + + This is usually a different bus from the session bus. +*/ +DBusConnection::DBusConnection(QObject *parent) + : QObject(parent), m_a11yConnection(QString()), m_enabled(false) +{ + // Start monitoring if "org.a11y.Bus" is registered as DBus service. + QDBusConnection c = QDBusConnection::sessionBus(); + if (!c.isConnected()) { + return; + } + + 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(); + + // In addition try if there is an xatom exposing the bus address, this allows applications run as root to work + QString address = getAddressFromXCB(); + if (!address.isEmpty()) { + m_enabled = true; + connectA11yBus(address); + } +} + +QString DBusConnection::getAddressFromXCB() +{ + QGuiApplication *app = qobject_cast(QCoreApplication::instance()); + if (!app) + return QString(); + QPlatformNativeInterface *platformNativeInterface = app->platformNativeInterface(); + QByteArray *addressByteArray = reinterpret_cast( + platformNativeInterface->nativeResourceForIntegration(QByteArrayLiteral("AtspiBus"))); + if (addressByteArray) { + QString address = QString::fromLatin1(*addressByteArray); + delete addressByteArray; + return address; + } + return QString(); +} + +// 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(); + OrgA11yStatusInterface *a11yStatus = new OrgA11yStatusInterface(A11Y_SERVICE, A11Y_PATH, c, this); + + //The variable was introduced because on some embedded platforms there are custom accessibility + //clients which don't set Status.ScreenReaderEnabled to true. The variable is also useful for + //debugging. + static const bool a11yAlwaysOn = qEnvironmentVariableIsSet("QT_LINUX_ACCESSIBILITY_ALWAYS_ON"); + + bool enabled = a11yAlwaysOn || a11yStatus->screenReaderEnabled() || a11yStatus->isEnabled(); + + if (enabled != m_enabled) { + m_enabled = enabled; + 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))); + } + } + + // connect(a11yStatus, ); QtDbus doesn't support notifications for property changes yet +} + +void DBusConnection::serviceUnregistered() +{ + emit enabledChanged(false); +} + +void DBusConnection::connectA11yBus(const QString &address) +{ + if (address.isEmpty()) { + qWarning("Could not find Accessibility DBus address."); + return; + } + m_a11yConnection = QDBusConnection(QDBusConnection::connectToBus(address, QLatin1String("a11y"))); + + if (m_enabled) + emit enabledChanged(true); +} + +void DBusConnection::dbusError(const QDBusError &error) +{ + qWarning() << "Accessibility encountered a DBus error:" << error; +} + +/*! + Returns the DBus connection that got established. + Or an invalid connection if not yet connected. +*/ +QDBusConnection DBusConnection::connection() const +{ + return m_a11yConnection; +} + +QT_END_NAMESPACE diff --git a/src/gui/accessible/linux/dbusconnection_p.h b/src/gui/accessible/linux/dbusconnection_p.h new file mode 100644 index 0000000000..826630fddc --- /dev/null +++ b/src/gui/accessible/linux/dbusconnection_p.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef DBUSCONNECTION_H +#define DBUSCONNECTION_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +Q_MOC_INCLUDE() + +QT_BEGIN_NAMESPACE + +class QDBusServiceWatcher; + +class DBusConnection : public QObject +{ + Q_OBJECT + +public: + DBusConnection(QObject *parent = nullptr); + 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: + QString getAddressFromXCB(); + void serviceRegistered(); + void serviceUnregistered(); + void connectA11yBus(const QString &address); + + void dbusError(const QDBusError &error); + +private: + QString getAccessibilityBusAddress() const; + + QDBusServiceWatcher *dbusWatcher; + QDBusConnection m_a11yConnection; + bool m_enabled; +}; + +QT_END_NAMESPACE + +#endif // DBUSCONNECTION_H diff --git a/src/gui/accessible/linux/dbusxml/Bus.xml b/src/gui/accessible/linux/dbusxml/Bus.xml new file mode 100644 index 0000000000..5a33e335a1 --- /dev/null +++ b/src/gui/accessible/linux/dbusxml/Bus.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + diff --git a/src/gui/accessible/linux/dbusxml/Cache.xml b/src/gui/accessible/linux/dbusxml/Cache.xml new file mode 100644 index 0000000000..01c52810ac --- /dev/null +++ b/src/gui/accessible/linux/dbusxml/Cache.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/src/gui/accessible/linux/dbusxml/DeviceEventController.xml b/src/gui/accessible/linux/dbusxml/DeviceEventController.xml new file mode 100644 index 0000000000..d4c26ef7e7 --- /dev/null +++ b/src/gui/accessible/linux/dbusxml/DeviceEventController.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/gui/accessible/linux/dbusxml/Socket.xml b/src/gui/accessible/linux/dbusxml/Socket.xml new file mode 100644 index 0000000000..75ec99f994 --- /dev/null +++ b/src/gui/accessible/linux/dbusxml/Socket.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/gui/accessible/linux/linux.pri b/src/gui/accessible/linux/linux.pri new file mode 100644 index 0000000000..43e59cb7bc --- /dev/null +++ b/src/gui/accessible/linux/linux.pri @@ -0,0 +1,27 @@ +accessibility_adaptors.files = accessible/linux/dbusxml/Cache.xml accessible/linux/dbusxml/DeviceEventController.xml +accessibility_adaptors.header_flags = -i QtGui/private/qspi_struct_marshallers_p.h +DBUS_ADAPTORS += accessibility_adaptors + +accessibility_interfaces.files = accessible/linux/dbusxml/Socket.xml accessible/linux/dbusxml/Bus.xml +accessibility_interfaces.header_flags = -i QtGui/private/qspi_struct_marshallers_p.h +DBUS_INTERFACES += accessibility_interfaces + +HEADERS += \ + accessible/linux/atspiadaptor_p.h \ + accessible/linux/dbusconnection_p.h \ + accessible/linux/qspi_constant_mappings_p.h \ + accessible/linux/qspi_struct_marshallers_p.h \ + accessible/linux/qspiaccessiblebridge_p.h \ + accessible/linux/qspiapplicationadaptor_p.h \ + accessible/linux/qspidbuscache_p.h + +SOURCES += \ + accessible/linux/atspiadaptor.cpp \ + accessible/linux/dbusconnection.cpp \ + accessible/linux/qspi_constant_mappings.cpp \ + accessible/linux/qspi_struct_marshallers.cpp \ + accessible/linux/qspiaccessiblebridge.cpp \ + accessible/linux/qspiapplicationadaptor.cpp \ + accessible/linux/qspidbuscache.cpp + +QMAKE_USE += atspi/nolink diff --git a/src/gui/accessible/linux/qspi_constant_mappings.cpp b/src/gui/accessible/linux/qspi_constant_mappings.cpp new file mode 100644 index 0000000000..d4fe3ecd69 --- /dev/null +++ b/src/gui/accessible/linux/qspi_constant_mappings.cpp @@ -0,0 +1,154 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qspi_constant_mappings_p.h" + +#include +#include + +// FIXME the assignment of roles is quite arbitrary, at some point go through this list and sort and check that it makes sense +// "calendar" "check menu item" "color chooser" "column header" "dateeditor" "desktop icon" "desktop frame" +// "directory pane" "drawing area" "file chooser" "fontchooser" "frame" "glass pane" "html container" "icon" +// "internal frame" "option pane" "password text" "radio menu item" "root pane" "row header" "scroll pane" +// "tear off menu item" "terminal" "toggle button" "tree table" "unknown" "viewport" "header" "footer" "paragraph" +// "ruler" "autocomplete" "edit bar" "embedded component" "entry" "caption" +// "heading" "page" "section" "redundant object" "form" "input method window" "menu" + +#ifndef QT_NO_ACCESSIBILITY +QT_BEGIN_NAMESPACE + +QHash qSpiRoleMapping; + +quint64 spiStatesFromQState(QAccessible::State state) +{ + quint64 spiState = 0; + + if (state.active) + setSpiStateBit(&spiState, ATSPI_STATE_ACTIVE); + if (state.editable) + setSpiStateBit(&spiState, ATSPI_STATE_EDITABLE); + if (!state.disabled) { + setSpiStateBit(&spiState, ATSPI_STATE_ENABLED); + setSpiStateBit(&spiState, ATSPI_STATE_SENSITIVE); + } + if (state.selected) + setSpiStateBit(&spiState, ATSPI_STATE_SELECTED); + if (state.focused) + setSpiStateBit(&spiState, ATSPI_STATE_FOCUSED); + if (state.pressed) + setSpiStateBit(&spiState, ATSPI_STATE_PRESSED); + if (state.checked) + setSpiStateBit(&spiState, ATSPI_STATE_CHECKED); + if (state.checkStateMixed) + setSpiStateBit(&spiState, ATSPI_STATE_INDETERMINATE); + if (state.readOnly) + setSpiStateBit(&spiState, ATSPI_STATE_READ_ONLY); + // if (state.HotTracked) + if (state.defaultButton) + setSpiStateBit(&spiState, ATSPI_STATE_IS_DEFAULT); + if (state.expandable) + setSpiStateBit(&spiState, ATSPI_STATE_EXPANDABLE); + if (state.expanded) + setSpiStateBit(&spiState, ATSPI_STATE_EXPANDED); + if (state.collapsed) + setSpiStateBit(&spiState, ATSPI_STATE_COLLAPSED); + if (state.busy) + setSpiStateBit(&spiState, ATSPI_STATE_BUSY); + if (state.marqueed || state.animated) + setSpiStateBit(&spiState, ATSPI_STATE_ANIMATED); + if (!state.invisible && !state.offscreen) { + setSpiStateBit(&spiState, ATSPI_STATE_SHOWING); + setSpiStateBit(&spiState, ATSPI_STATE_VISIBLE); + } + if (state.sizeable) + setSpiStateBit(&spiState, ATSPI_STATE_RESIZABLE); + // if (state.Movable) + // if (state.SelfVoicing) + if (state.focusable) + setSpiStateBit(&spiState, ATSPI_STATE_FOCUSABLE); + if (state.selectable) + setSpiStateBit(&spiState, ATSPI_STATE_SELECTABLE); + // if (state.Linked) + if (state.traversed) + setSpiStateBit(&spiState, ATSPI_STATE_VISITED); + if (state.multiSelectable) + setSpiStateBit(&spiState, ATSPI_STATE_MULTISELECTABLE); + if (state.extSelectable) + setSpiStateBit(&spiState, ATSPI_STATE_SELECTABLE); + // if (state.Protected) + // if (state.HasPopup) + if (state.modal) + setSpiStateBit(&spiState, ATSPI_STATE_MODAL); + if (state.multiLine) + setSpiStateBit(&spiState, ATSPI_STATE_MULTI_LINE); + + return spiState; +} + +QSpiUIntList spiStateSetFromSpiStates(quint64 states) +{ + uint low = states & 0xFFFFFFFF; + uint high = (states >> 32) & 0xFFFFFFFF; + + QSpiUIntList stateList; + stateList.append(low); + stateList.append(high); + return stateList; +} + +AtspiRelationType qAccessibleRelationToAtSpiRelation(QAccessible::Relation relation) +{ + switch (relation) { + case QAccessible::Label: + return ATSPI_RELATION_LABELLED_BY; + case QAccessible::Labelled: + return ATSPI_RELATION_LABEL_FOR; + case QAccessible::Controller: + return ATSPI_RELATION_CONTROLLED_BY; + case QAccessible::Controlled: + return ATSPI_RELATION_CONTROLLER_FOR; + default: + qWarning() << "Cannot return AT-SPI relation for:" << relation; + } + return ATSPI_RELATION_NULL; +} + +QT_END_NAMESPACE +#endif //QT_NO_ACCESSIBILITY diff --git a/src/gui/accessible/linux/qspi_constant_mappings_p.h b/src/gui/accessible/linux/qspi_constant_mappings_p.h new file mode 100644 index 0000000000..292b5e1597 --- /dev/null +++ b/src/gui/accessible/linux/qspi_constant_mappings_p.h @@ -0,0 +1,145 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +/* + * This file contains AT-SPI constants and mappings between QAccessible + * and AT-SPI constants such as 'role' and 'state' enumerations. + */ + +#ifndef Q_SPI_CONSTANT_MAPPINGS_H +#define Q_SPI_CONSTANT_MAPPINGS_H + +#include "qspi_struct_marshallers_p.h" + +#include +#include +#include + +QT_REQUIRE_CONFIG(accessibility); + +// interface names from at-spi2-core/atspi/atspi-misc-private.h +#define ATSPI_DBUS_NAME_REGISTRY "org.a11y.atspi.Registry" +#define ATSPI_DBUS_PATH_REGISTRY "/org/a11y/atspi/registry" +#define ATSPI_DBUS_INTERFACE_REGISTRY "org.a11y.atspi.Registry" + +#define ATSPI_DBUS_PATH_ROOT "/org/a11y/atspi/accessible/root" + +#define ATSPI_DBUS_PATH_DEC "/org/a11y/atspi/registry/deviceeventcontroller" +#define ATSPI_DBUS_INTERFACE_DEC "org.a11y.atspi.DeviceEventController" +#define ATSPI_DBUS_INTERFACE_DEVICE_EVENT_LISTENER "org.a11y.atspi.DeviceEventListener" + +#define ATSPI_DBUS_INTERFACE_CACHE "org.a11y.atspi.Cache" +#define ATSPI_DBUS_INTERFACE_ACCESSIBLE "org.a11y.atspi.Accessible" +#define ATSPI_DBUS_INTERFACE_ACTION "org.a11y.atspi.Action" +#define ATSPI_DBUS_INTERFACE_APPLICATION "org.a11y.atspi.Application" +#define ATSPI_DBUS_INTERFACE_COLLECTION "org.a11y.atspi.Collection" +#define ATSPI_DBUS_INTERFACE_COMPONENT "org.a11y.atspi.Component" +#define ATSPI_DBUS_INTERFACE_DOCUMENT "org.a11y.atspi.Document" +#define ATSPI_DBUS_INTERFACE_EDITABLE_TEXT "org.a11y.atspi.EditableText" +#define ATSPI_DBUS_INTERFACE_EVENT_KEYBOARD "org.a11y.atspi.Event.Keyboard" +#define ATSPI_DBUS_INTERFACE_EVENT_MOUSE "org.a11y.atspi.Event.Mouse" +#define ATSPI_DBUS_INTERFACE_EVENT_OBJECT "org.a11y.atspi.Event.Object" +#define ATSPI_DBUS_INTERFACE_HYPERLINK "org.a11y.atspi.Hyperlink" +#define ATSPI_DBUS_INTERFACE_HYPERTEXT "org.a11y.atspi.Hypertext" +#define ATSPI_DBUS_INTERFACE_IMAGE "org.a11y.atspi.Image" +#define ATSPI_DBUS_INTERFACE_SELECTION "org.a11y.atspi.Selection" +#define ATSPI_DBUS_INTERFACE_TABLE "org.a11y.atspi.Table" +#define ATSPI_DBUS_INTERFACE_TEXT "org.a11y.atspi.Text" +#define ATSPI_DBUS_INTERFACE_VALUE "org.a11y.atspi.Value" +#define ATSPI_DBUS_INTERFACE_SOCKET "org.a11y.atspi.Socket" + +// missing from at-spi2-core: +#define ATSPI_DBUS_INTERFACE_EVENT_WINDOW "org.a11y.atspi.Event.Window" +#define ATSPI_DBUS_INTERFACE_EVENT_FOCUS "org.a11y.atspi.Event.Focus" + +#define QSPI_OBJECT_PATH_ACCESSIBLE "/org/a11y/atspi/accessible" +#define QSPI_OBJECT_PATH_PREFIX "/org/a11y/atspi/accessible/" +#define QSPI_OBJECT_PATH_ROOT QSPI_OBJECT_PATH_PREFIX "root" + +#define QSPI_REGISTRY_NAME "org.a11y.atspi.Registry" + +QT_BEGIN_NAMESPACE + +struct RoleNames { + RoleNames() {} + RoleNames(AtspiRole r, const QString& n, const QString& ln) + :m_spiRole(r), m_name(n), m_localizedName(ln) + {} + + AtspiRole spiRole() const {return m_spiRole;} + QString name() const {return m_name;} + QString localizedName() const {return m_localizedName;} + +private: + AtspiRole m_spiRole = ATSPI_ROLE_INVALID; + QString m_name; + QString m_localizedName; +}; + +extern QHash qSpiRoleMapping; + +inline void setSpiStateBit(quint64* state, AtspiStateType spiState) +{ + *state |= quint64(1) << spiState; +} + +inline void unsetSpiStateBit(quint64* state, AtspiStateType spiState) +{ + *state &= ~(quint64(1) << spiState); +} + +quint64 spiStatesFromQState(QAccessible::State state); +QSpiUIntList spiStateSetFromSpiStates(quint64 states); + +AtspiRelationType qAccessibleRelationToAtSpiRelation(QAccessible::Relation relation); + +QT_END_NAMESPACE + +#endif /* Q_SPI_CONSTANT_MAPPINGS_H */ diff --git a/src/gui/accessible/linux/qspi_struct_marshallers.cpp b/src/gui/accessible/linux/qspi_struct_marshallers.cpp new file mode 100644 index 0000000000..1f49d8533f --- /dev/null +++ b/src/gui/accessible/linux/qspi_struct_marshallers.cpp @@ -0,0 +1,237 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qspi_struct_marshallers_p.h" + +#include +#include +#include + +#include "qspiaccessiblebridge_p.h" + +#ifndef QT_NO_ACCESSIBILITY +QT_BEGIN_NAMESPACE + +QSpiObjectReference::QSpiObjectReference() + : path(QDBusObjectPath(ATSPI_DBUS_PATH_NULL)) +{} + +/* QSpiAccessibleCacheArray */ +/*---------------------------------------------------------------------------*/ + +QDBusArgument &operator<<(QDBusArgument &argument, const QSpiAccessibleCacheItem &item) +{ + argument.beginStructure(); + argument << item.path; + argument << item.application; + argument << item.parent; + argument << item.children; + argument << item.supportedInterfaces; + argument << item.name; + argument << item.role; + argument << item.description; + argument << item.state; + argument.endStructure(); + return argument; +} + +const QDBusArgument &operator>>(const QDBusArgument &argument, QSpiAccessibleCacheItem &item) +{ + argument.beginStructure(); + argument >> item.path; + argument >> item.application; + argument >> item.parent; + argument >> item.children; + argument >> item.supportedInterfaces; + argument >> item.name; + argument >> item.role; + argument >> item.description; + argument >> item.state; + argument.endStructure(); + return argument; +} + +/* QSpiObjectReference */ +/*---------------------------------------------------------------------------*/ + +QDBusArgument &operator<<(QDBusArgument &argument, const QSpiObjectReference &address) +{ + argument.beginStructure(); + argument << address.service; + argument << address.path; + argument.endStructure(); + return argument; +} + +const QDBusArgument &operator>>(const QDBusArgument &argument, QSpiObjectReference &address) +{ + argument.beginStructure(); + argument >> address.service; + argument >> address.path; + argument.endStructure(); + return argument; +} + +/* QSpiAction */ +/*---------------------------------------------------------------------------*/ + +QDBusArgument &operator<<(QDBusArgument &argument, const QSpiAction &action) +{ + argument.beginStructure(); + argument << action.name; + argument << action.description; + argument << action.keyBinding; + argument.endStructure(); + return argument; +} + +const QDBusArgument &operator>>(const QDBusArgument &argument, QSpiAction &action) +{ + argument.beginStructure(); + argument >> action.name; + argument >> action.description; + argument >> action.keyBinding; + argument.endStructure(); + return argument; +} + + +QDBusArgument &operator<<(QDBusArgument &argument, const QSpiEventListener &ev) +{ + argument.beginStructure(); + argument << ev.listenerAddress; + argument << ev.eventName; + argument.endStructure(); + return argument; +} + +const QDBusArgument &operator>>(const QDBusArgument &argument, QSpiEventListener &ev) +{ + argument.beginStructure(); + argument >> ev.listenerAddress; + argument >> ev.eventName; + argument.endStructure(); + return argument; +} + +/* QSpiAppUpdate */ +/*---------------------------------------------------------------------------*/ + +QDBusArgument &operator<<(QDBusArgument &argument, const QSpiAppUpdate &update) { + argument.beginStructure(); + argument << update.type << update.address; + argument.endStructure(); + return argument; +} + +const QDBusArgument &operator>>(const QDBusArgument &argument, QSpiAppUpdate &update) { + argument.beginStructure(); + argument >> update.type >> update.address; + argument.endStructure(); + return argument; +} + +/* QSpiRelationArrayEntry */ +/*---------------------------------------------------------------------------*/ + +QDBusArgument &operator<<(QDBusArgument &argument, const QSpiRelationArrayEntry &entry) { + argument.beginStructure(); + argument << entry.first << entry.second; + argument.endStructure(); + return argument; +} + +const QDBusArgument &operator>>(const QDBusArgument &argument, QSpiRelationArrayEntry &entry) { + argument.beginStructure(); + argument >> entry.first >> entry.second; + argument.endStructure(); + return argument; +} + +/* QSpiDeviceEvent */ +/*---------------------------------------------------------------------------*/ + +QDBusArgument &operator<<(QDBusArgument &argument, const QSpiDeviceEvent &event) { + argument.beginStructure(); + argument << event.type + << event.id + << event.hardwareCode + << event.modifiers + << event.timestamp + << event.text + << event.isText; + argument.endStructure(); + return argument; +} + +const QDBusArgument &operator>>(const QDBusArgument &argument, QSpiDeviceEvent &event) { + argument.beginStructure(); + argument >> event.type + >> event.id + >> event.hardwareCode + >> event.modifiers + >> event.timestamp + >> event.text + >> event.isText; + argument.endStructure(); + return argument; +} + +void qSpiInitializeStructTypes() +{ + qDBusRegisterMetaType(); + qDBusRegisterMetaType(); + qDBusRegisterMetaType(); + qDBusRegisterMetaType(); + qDBusRegisterMetaType(); + qDBusRegisterMetaType(); + qDBusRegisterMetaType(); + qDBusRegisterMetaType(); + qDBusRegisterMetaType(); + qDBusRegisterMetaType(); + qDBusRegisterMetaType(); + qDBusRegisterMetaType(); + qDBusRegisterMetaType(); + qDBusRegisterMetaType(); + qDBusRegisterMetaType(); +} + +QT_END_NAMESPACE +#endif //QT_NO_ACCESSIBILITY diff --git a/src/gui/accessible/linux/qspi_struct_marshallers_p.h b/src/gui/accessible/linux/qspi_struct_marshallers_p.h new file mode 100644 index 0000000000..876c2bf700 --- /dev/null +++ b/src/gui/accessible/linux/qspi_struct_marshallers_p.h @@ -0,0 +1,200 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef Q_SPI_STRUCT_MARSHALLERS_H +#define Q_SPI_STRUCT_MARSHALLERS_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include +#include +#include + +QT_REQUIRE_CONFIG(accessibility); + +QT_BEGIN_NAMESPACE + +typedef QVector QSpiIntList; +typedef QVector QSpiUIntList; + +// FIXME: make this copy on write +struct QSpiObjectReference +{ + QString service; + QDBusObjectPath path; + + QSpiObjectReference(); + QSpiObjectReference(const QDBusConnection& connection, const QDBusObjectPath& path) + : service(connection.baseService()), path(path) {} +}; +Q_DECLARE_TYPEINFO(QSpiObjectReference, Q_MOVABLE_TYPE); // QDBusObjectPath is movable, even though it + // cannot be marked that way until Qt 6 + +QDBusArgument &operator<<(QDBusArgument &argument, const QSpiObjectReference &address); +const QDBusArgument &operator>>(const QDBusArgument &argument, QSpiObjectReference &address); + +typedef QVector QSpiObjectReferenceArray; + +struct QSpiAccessibleCacheItem +{ + QSpiObjectReference path; + QSpiObjectReference application; + QSpiObjectReference parent; + QSpiObjectReferenceArray children; + QStringList supportedInterfaces; + QString name; + uint role; + QString description; + QSpiUIntList state; +}; +Q_DECLARE_TYPEINFO(QSpiAccessibleCacheItem, Q_MOVABLE_TYPE); + +typedef QVector QSpiAccessibleCacheArray; + +QDBusArgument &operator<<(QDBusArgument &argument, const QSpiAccessibleCacheItem &item); +const QDBusArgument &operator>>(const QDBusArgument &argument, QSpiAccessibleCacheItem &item); + +struct QSpiAction +{ + QString name; + QString description; + QString keyBinding; +}; +Q_DECLARE_TYPEINFO(QSpiAction, Q_MOVABLE_TYPE); + +typedef QVector QSpiActionArray; + +QDBusArgument &operator<<(QDBusArgument &argument, const QSpiAction &action); +const QDBusArgument &operator>>(const QDBusArgument &argument, QSpiAction &action); + +struct QSpiEventListener +{ + QString listenerAddress; + QString eventName; +}; +Q_DECLARE_TYPEINFO(QSpiEventListener, Q_MOVABLE_TYPE); + +typedef QVector QSpiEventListenerArray; + +QDBusArgument &operator<<(QDBusArgument &argument, const QSpiEventListener &action); +const QDBusArgument &operator>>(const QDBusArgument &argument, QSpiEventListener &action); + +typedef QPair QSpiRelationArrayEntry; +typedef QVector QSpiRelationArray; + +//a(iisv) +struct QSpiTextRange { + int startOffset; + int endOffset; + QString contents; + QVariant v; +}; +Q_DECLARE_TYPEINFO(QSpiTextRange, Q_MOVABLE_TYPE); + +typedef QVector QSpiTextRangeList; +typedef QMap QSpiAttributeSet; + +enum QSpiAppUpdateType { + QSPI_APP_UPDATE_ADDED = 0, + QSPI_APP_UPDATE_REMOVED = 1 +}; +Q_DECLARE_TYPEINFO(QSpiAppUpdateType, Q_PRIMITIVE_TYPE); + +struct QSpiAppUpdate { + int type; /* Is an application added or removed */ + QString address; /* D-Bus address of application added or removed */ +}; +Q_DECLARE_TYPEINFO(QSpiAppUpdate, Q_MOVABLE_TYPE); + +QDBusArgument &operator<<(QDBusArgument &argument, const QSpiAppUpdate &update); +const QDBusArgument &operator>>(const QDBusArgument &argument, QSpiAppUpdate &update); + +struct QSpiDeviceEvent { + unsigned int type; + int id; + int hardwareCode; + int modifiers; + int timestamp; + QString text; + bool isText; +}; +Q_DECLARE_TYPEINFO(QSpiDeviceEvent, Q_MOVABLE_TYPE); + +QDBusArgument &operator<<(QDBusArgument &argument, const QSpiDeviceEvent &event); +const QDBusArgument &operator>>(const QDBusArgument &argument, QSpiDeviceEvent &event); + +void qSpiInitializeStructTypes(); + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QSpiIntList) +Q_DECLARE_METATYPE(QSpiUIntList) +Q_DECLARE_METATYPE(QSpiObjectReference) +Q_DECLARE_METATYPE(QSpiObjectReferenceArray) +Q_DECLARE_METATYPE(QSpiAccessibleCacheItem) +Q_DECLARE_METATYPE(QSpiAccessibleCacheArray) +Q_DECLARE_METATYPE(QSpiAction) +Q_DECLARE_METATYPE(QSpiActionArray) +Q_DECLARE_METATYPE(QSpiEventListener) +Q_DECLARE_METATYPE(QSpiEventListenerArray) +Q_DECLARE_METATYPE(QSpiRelationArrayEntry) +Q_DECLARE_METATYPE(QSpiRelationArray) +Q_DECLARE_METATYPE(QSpiTextRange) +Q_DECLARE_METATYPE(QSpiTextRangeList) +Q_DECLARE_METATYPE(QSpiAttributeSet) +Q_DECLARE_METATYPE(QSpiAppUpdate) +Q_DECLARE_METATYPE(QSpiDeviceEvent) + +// For qdbusxml2cpp-generated code +QT_USE_NAMESPACE + +#endif /* Q_SPI_STRUCT_MARSHALLERS_H */ diff --git a/src/gui/accessible/linux/qspiaccessiblebridge.cpp b/src/gui/accessible/linux/qspiaccessiblebridge.cpp new file mode 100644 index 0000000000..9ae8767b66 --- /dev/null +++ b/src/gui/accessible/linux/qspiaccessiblebridge.cpp @@ -0,0 +1,286 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qspiaccessiblebridge_p.h" + +#include +#include + +#include "atspiadaptor_p.h" + +#include "qspidbuscache_p.h" +#include "qspi_constant_mappings_p.h" +#include "dbusconnection_p.h" +#include "qspi_struct_marshallers_p.h" + +#ifndef QT_NO_ACCESSIBILITY +#include "deviceeventcontroller_adaptor.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QSpiAccessibleBridge + \internal +*/ + +QSpiAccessibleBridge::QSpiAccessibleBridge() + : cache(0), dec(0), dbusAdaptor(0) +{ + dbusConnection = new DBusConnection(); + connect(dbusConnection, SIGNAL(enabledChanged(bool)), this, SLOT(enabledChanged(bool))); +} + +void QSpiAccessibleBridge::enabledChanged(bool enabled) +{ + setActive(enabled); + updateStatus(); +} + +QSpiAccessibleBridge::~QSpiAccessibleBridge() +{ + delete dbusConnection; +} // Qt currently doesn't delete plugins. + +QDBusConnection QSpiAccessibleBridge::dBusConnection() const +{ + return dbusConnection->connection(); +} + +void QSpiAccessibleBridge::updateStatus() +{ + // create the adaptor to handle everything if we are in enabled state + if (!dbusAdaptor && isActive()) { + 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) +{ + if (!dbusAdaptor) + return; + if (isActive() && event->accessibleInterface()) + dbusAdaptor->notify(event); +} + +struct RoleMapping { + QAccessible::Role role; + AtspiRole spiRole; + const char *name; +}; + +static RoleMapping map[] = { + //: Role of an accessible object - the object is in an invalid state or could not be constructed + { QAccessible::NoRole, ATSPI_ROLE_INVALID, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "invalid role") }, + //: Role of an accessible object + { QAccessible::TitleBar, ATSPI_ROLE_TEXT, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "title bar") }, + //: Role of an accessible object + { QAccessible::MenuBar, ATSPI_ROLE_MENU_BAR, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "menu bar") }, + //: Role of an accessible object + { QAccessible::ScrollBar, ATSPI_ROLE_SCROLL_BAR, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "scroll bar") }, + //: Role of an accessible object - the grip is usually used for resizing another object + { QAccessible::Grip, ATSPI_ROLE_UNKNOWN, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "grip") }, + //: Role of an accessible object + { QAccessible::Sound, ATSPI_ROLE_UNKNOWN, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "sound") }, + //: Role of an accessible object + { QAccessible::Cursor, ATSPI_ROLE_UNKNOWN, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "cursor") }, + //: Role of an accessible object + { QAccessible::Caret, ATSPI_ROLE_UNKNOWN, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "text caret") }, + //: Role of an accessible object + { QAccessible::AlertMessage, ATSPI_ROLE_ALERT, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "alert message") }, + //: Role of an accessible object: a window with frame and title + { QAccessible::Window, ATSPI_ROLE_FRAME, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "frame") }, + //: Role of an accessible object + { QAccessible::Client, ATSPI_ROLE_FILLER, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "filler") }, + //: Role of an accessible object + { QAccessible::PopupMenu, ATSPI_ROLE_POPUP_MENU, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "popup menu") }, + //: Role of an accessible object + { QAccessible::MenuItem, ATSPI_ROLE_MENU_ITEM, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "menu item") }, + //: Role of an accessible object + { QAccessible::ToolTip, ATSPI_ROLE_TOOL_TIP, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "tool tip") }, + //: Role of an accessible object + { QAccessible::Application, ATSPI_ROLE_APPLICATION, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "application") }, + //: Role of an accessible object + { QAccessible::Document, ATSPI_ROLE_DOCUMENT_FRAME, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "document") }, + //: Role of an accessible object + { QAccessible::Pane, ATSPI_ROLE_PANEL, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "panel") }, + //: Role of an accessible object + { QAccessible::Chart, ATSPI_ROLE_CHART, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "chart") }, + //: Role of an accessible object + { QAccessible::Dialog, ATSPI_ROLE_DIALOG, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "dialog") }, + //: Role of an accessible object + { QAccessible::Border, ATSPI_ROLE_FRAME, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "frame") }, + //: Role of an accessible object + { QAccessible::Grouping, ATSPI_ROLE_PANEL, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "panel") }, + //: Role of an accessible object + { QAccessible::Separator, ATSPI_ROLE_SEPARATOR, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "separator") }, + //: Role of an accessible object + { QAccessible::ToolBar, ATSPI_ROLE_TOOL_BAR, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "tool bar") }, + //: Role of an accessible object + { QAccessible::StatusBar, ATSPI_ROLE_STATUS_BAR, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "status bar") }, + //: Role of an accessible object + { QAccessible::Table, ATSPI_ROLE_TABLE, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "table") }, + //: Role of an accessible object - part of a table + { QAccessible::ColumnHeader, ATSPI_ROLE_TABLE_COLUMN_HEADER, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "column header") }, + //: Role of an accessible object - part of a table + { QAccessible::RowHeader, ATSPI_ROLE_TABLE_ROW_HEADER, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "row header") }, + //: Role of an accessible object - part of a table + { QAccessible::Column, ATSPI_ROLE_TABLE_CELL, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "column") }, + //: Role of an accessible object - part of a table + { QAccessible::Row, ATSPI_ROLE_TABLE_ROW, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "row") }, + //: Role of an accessible object - part of a table + { QAccessible::Cell, ATSPI_ROLE_TABLE_CELL, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "cell") }, + //: Role of an accessible object + { QAccessible::Link, ATSPI_ROLE_LINK, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "link") }, + //: Role of an accessible object + { QAccessible::HelpBalloon, ATSPI_ROLE_DIALOG, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "help balloon") }, + //: Role of an accessible object - a helper dialog + { QAccessible::Assistant, ATSPI_ROLE_DIALOG, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "assistant") }, + //: Role of an accessible object + { QAccessible::List, ATSPI_ROLE_LIST, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "list") }, + //: Role of an accessible object + { QAccessible::ListItem, ATSPI_ROLE_LIST_ITEM, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "list item") }, + //: Role of an accessible object + { QAccessible::Tree, ATSPI_ROLE_TREE, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "tree") }, + //: Role of an accessible object + { QAccessible::TreeItem, ATSPI_ROLE_TABLE_CELL, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "tree item") }, + //: Role of an accessible object + { QAccessible::PageTab, ATSPI_ROLE_PAGE_TAB, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "page tab") }, + //: Role of an accessible object + { QAccessible::PropertyPage, ATSPI_ROLE_PAGE_TAB, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "property page") }, + //: Role of an accessible object + { QAccessible::Indicator, ATSPI_ROLE_UNKNOWN, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "indicator") }, + //: Role of an accessible object + { QAccessible::Graphic, ATSPI_ROLE_IMAGE, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "graphic") }, + //: Role of an accessible object + { QAccessible::StaticText, ATSPI_ROLE_LABEL, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "label") }, + //: Role of an accessible object + { QAccessible::EditableText, ATSPI_ROLE_TEXT, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "text") }, + //: Role of an accessible object + { QAccessible::PushButton, ATSPI_ROLE_PUSH_BUTTON, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "push button") }, + //: Role of an accessible object + { QAccessible::CheckBox, ATSPI_ROLE_CHECK_BOX, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "check box") }, + //: Role of an accessible object + { QAccessible::RadioButton, ATSPI_ROLE_RADIO_BUTTON, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "radio button") }, + //: Role of an accessible object + { QAccessible::ComboBox, ATSPI_ROLE_COMBO_BOX, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "combo box") }, + //: Role of an accessible object + { QAccessible::ProgressBar, ATSPI_ROLE_PROGRESS_BAR, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "progress bar") }, + //: Role of an accessible object + { QAccessible::Dial, ATSPI_ROLE_DIAL, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "dial") }, + //: Role of an accessible object + { QAccessible::HotkeyField, ATSPI_ROLE_TEXT, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "hotkey field") }, + //: Role of an accessible object + { QAccessible::Slider, ATSPI_ROLE_SLIDER, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "slider") }, + //: Role of an accessible object + { QAccessible::SpinBox, ATSPI_ROLE_SPIN_BUTTON, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "spin box") }, + //: Role of an accessible object + { QAccessible::Canvas, ATSPI_ROLE_CANVAS, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "canvas") }, + //: Role of an accessible object + { QAccessible::Animation, ATSPI_ROLE_ANIMATION, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "animation") }, + //: Role of an accessible object + { QAccessible::Equation, ATSPI_ROLE_TEXT, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "equation") }, + //: Role of an accessible object + { QAccessible::ButtonDropDown, ATSPI_ROLE_PUSH_BUTTON, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "button with drop down") }, + //: Role of an accessible object + { QAccessible::ButtonMenu, ATSPI_ROLE_PUSH_BUTTON, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "button menu") }, + //: Role of an accessible object - a button that expands a grid. + { QAccessible::ButtonDropGrid, ATSPI_ROLE_PUSH_BUTTON, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "button with drop down grid") }, + //: Role of an accessible object - blank space between other objects. + { QAccessible::Whitespace, ATSPI_ROLE_FILLER, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "space") }, + //: Role of an accessible object + { QAccessible::PageTabList, ATSPI_ROLE_PAGE_TAB_LIST, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "page tab list") }, + //: Role of an accessible object + { QAccessible::Clock, ATSPI_ROLE_UNKNOWN, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "clock") }, + //: Role of an accessible object + { QAccessible::Splitter, ATSPI_ROLE_SPLIT_PANE, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "splitter") }, + //: Role of an accessible object + { QAccessible::LayeredPane, ATSPI_ROLE_LAYERED_PANE, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "layered pane") }, + //: Role of an accessible object + { QAccessible::WebDocument, ATSPI_ROLE_DOCUMENT_WEB, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "web document") }, + //: Role of an accessible object + { QAccessible::Paragraph, ATSPI_ROLE_PARAGRAPH, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "paragraph") }, + //: Role of an accessible object + { QAccessible::Section, ATSPI_ROLE_SECTION, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "section") }, + //: Role of an accessible object + { QAccessible::ColorChooser, ATSPI_ROLE_COLOR_CHOOSER, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "color chooser") }, + //: Role of an accessible object + { QAccessible::Footer, ATSPI_ROLE_FOOTER, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "footer") }, + //: Role of an accessible object + { QAccessible::Form, ATSPI_ROLE_FORM, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "form") }, + //: Role of an accessible object + { QAccessible::Heading, ATSPI_ROLE_HEADING, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "heading") }, + //: Role of an accessible object + { QAccessible::Note, ATSPI_ROLE_COMMENT, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "note") }, + //: Role of an accessible object + { QAccessible::ComplementaryContent, ATSPI_ROLE_SECTION, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "complementary content") }, + //: Role of an accessible object + { QAccessible::Terminal, ATSPI_ROLE_TERMINAL, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "terminal") }, + //: Role of an accessible object + { QAccessible::Desktop, ATSPI_ROLE_DESKTOP_FRAME, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "desktop") }, + //: Role of an accessible object + { QAccessible::Notification, ATSPI_ROLE_NOTIFICATION, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "notification") }, + //: Role of an accessible object + { QAccessible::UserRole, ATSPI_ROLE_UNKNOWN, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "unknown") } +}; + +void QSpiAccessibleBridge::initializeConstantMappings() +{ + for (uint i = 0; i < sizeof(map) / sizeof(RoleMapping); ++i) + qSpiRoleMapping.insert(map[i].role, RoleNames(map[i].spiRole, QLatin1String(map[i].name), tr(map[i].name))); + + // -1 because we have button duplicated, as PushButton and Button. + Q_ASSERT_X(qSpiRoleMapping.size() == + QAccessible::staticMetaObject.enumerator( + QAccessible::staticMetaObject.indexOfEnumerator("Role")).keyCount() - 1, + "", "Handle all QAccessible::Role members in qSpiRoleMapping"); +} + +QT_END_NAMESPACE +#endif //QT_NO_ACCESSIBILITY diff --git a/src/gui/accessible/linux/qspiaccessiblebridge_p.h b/src/gui/accessible/linux/qspiaccessiblebridge_p.h new file mode 100644 index 0000000000..ce1b755826 --- /dev/null +++ b/src/gui/accessible/linux/qspiaccessiblebridge_p.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QSPIACCESSIBLEBRIDGE_H +#define QSPIACCESSIBLEBRIDGE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include + +class DeviceEventControllerAdaptor; + +QT_REQUIRE_CONFIG(accessibility); + +QT_BEGIN_NAMESPACE + +class DBusConnection; +class QSpiDBusCache; +class AtSpiAdaptor; + +class Q_GUI_EXPORT QSpiAccessibleBridge: public QObject, public QPlatformAccessibility +{ + Q_OBJECT +public: + QSpiAccessibleBridge(); + + virtual ~QSpiAccessibleBridge(); + + void notifyAccessibilityUpdate(QAccessibleEvent *event) override; + QDBusConnection dBusConnection() const; + +public Q_SLOTS: + void enabledChanged(bool enabled); + +private: + void initializeConstantMappings(); + void updateStatus(); + + QSpiDBusCache *cache; + DeviceEventControllerAdaptor *dec; + AtSpiAdaptor *dbusAdaptor; + DBusConnection* dbusConnection; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/gui/accessible/linux/qspiapplicationadaptor.cpp b/src/gui/accessible/linux/qspiapplicationadaptor.cpp new file mode 100644 index 0000000000..430dc13b00 --- /dev/null +++ b/src/gui/accessible/linux/qspiapplicationadaptor.cpp @@ -0,0 +1,240 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qspiapplicationadaptor_p.h" + +#include +#include +#include + +#ifndef QT_NO_ACCESSIBILITY +#include "deviceeventcontroller_adaptor.h" +#include "atspi/atspi-constants.h" + +//#define KEYBOARD_DEBUG + +QT_BEGIN_NAMESPACE + +/*! + \class QSpiApplicationAdaptor + \internal + + \brief QSpiApplicationAdaptor + + QSpiApplicationAdaptor +*/ + +QSpiApplicationAdaptor::QSpiApplicationAdaptor(const QDBusConnection &connection, QObject *parent) + : QObject(parent), dbusConnection(connection), inCapsLock(false) +{ +} + +enum QSpiKeyEventType { + QSPI_KEY_EVENT_PRESS, + QSPI_KEY_EVENT_RELEASE, + QSPI_KEY_EVENT_LAST_DEFINED +}; + +void QSpiApplicationAdaptor::sendEvents(bool active) +{ + if (active) { + qApp->installEventFilter(this); + } else { + qApp->removeEventFilter(this); + } +} + + +bool QSpiApplicationAdaptor::eventFilter(QObject *target, QEvent *event) +{ + if (!event->spontaneous()) + return false; + + switch (event->type()) { + case QEvent::WindowActivate: + emit windowActivated(target, true); + break; + case QEvent::WindowDeactivate: + emit windowActivated(target, false); + break; + case QEvent::KeyPress: + case QEvent::KeyRelease: { + QKeyEvent *keyEvent = static_cast (event); + QSpiDeviceEvent de; + + if (event->type() == QEvent::KeyPress) + de.type = QSPI_KEY_EVENT_PRESS; + else + de.type = QSPI_KEY_EVENT_RELEASE; + + de.id = keyEvent->nativeVirtualKey(); + de.hardwareCode = keyEvent->nativeScanCode(); + + de.timestamp = QDateTime::currentMSecsSinceEpoch(); + + if (keyEvent->key() == Qt::Key_Tab) + de.text = QStringLiteral("Tab"); + else if (keyEvent->key() == Qt::Key_Backtab) + de.text = QStringLiteral("Backtab"); + else if (keyEvent->key() == Qt::Key_Control) + de.text = QStringLiteral("Control_L"); + else if (keyEvent->key() == Qt::Key_Left) + de.text = (keyEvent->modifiers() & Qt::KeypadModifier) ? QStringLiteral("KP_Left") : QStringLiteral("Left"); + else if (keyEvent->key() == Qt::Key_Right) + de.text = (keyEvent->modifiers() & Qt::KeypadModifier) ? QStringLiteral("KP_Right") : QStringLiteral("Right"); + else if (keyEvent->key() == Qt::Key_Up) + de.text = (keyEvent->modifiers() & Qt::KeypadModifier) ? QStringLiteral("KP_Up") : QStringLiteral("Up"); + else if (keyEvent->key() == Qt::Key_Down) + de.text = (keyEvent->modifiers() & Qt::KeypadModifier) ? QStringLiteral("KP_Down") : QStringLiteral("Down"); + else if (keyEvent->key() == Qt::Key_Enter || keyEvent->key() == Qt::Key_Return) + de.text = QStringLiteral("Return"); + else if (keyEvent->key() == Qt::Key_Backspace) + de.text = QStringLiteral("BackSpace"); + else if (keyEvent->key() == Qt::Key_Delete) + de.text = QStringLiteral("Delete"); + else if (keyEvent->key() == Qt::Key_PageUp) + de.text = (keyEvent->modifiers() & Qt::KeypadModifier) ? QStringLiteral("KP_Page_Up") : QStringLiteral("Page_Up"); + else if (keyEvent->key() == Qt::Key_PageDown) + de.text = (keyEvent->modifiers() & Qt::KeypadModifier) ? QStringLiteral("KP_Page_Up") : QStringLiteral("Page_Down"); + else if (keyEvent->key() == Qt::Key_Home) + de.text = (keyEvent->modifiers() & Qt::KeypadModifier) ? QStringLiteral("KP_Home") : QStringLiteral("Home"); + else if (keyEvent->key() == Qt::Key_End) + de.text = (keyEvent->modifiers() & Qt::KeypadModifier) ? QStringLiteral("KP_End") : QStringLiteral("End"); + else if (keyEvent->key() == Qt::Key_Clear && (keyEvent->modifiers() & Qt::KeypadModifier)) + de.text = QStringLiteral("KP_Begin"); // Key pad 5 + else if (keyEvent->key() == Qt::Key_Escape) + de.text = QStringLiteral("Escape"); + else if (keyEvent->key() == Qt::Key_Space) + de.text = QStringLiteral("space"); + else if (keyEvent->key() == Qt::Key_CapsLock) { + de.text = QStringLiteral("Caps_Lock"); + if (event->type() == QEvent::KeyPress) + inCapsLock = true; + else + inCapsLock = false; + } else if (keyEvent->key() == Qt::Key_NumLock) + de.text = QStringLiteral("Num_Lock"); + else if (keyEvent->key() == Qt::Key_Insert) + de.text = QStringLiteral("Insert"); + else + de.text = keyEvent->text(); + + // This is a bit dubious, Gnome uses some gtk function here. + // Long term the spec will hopefully change to just use keycodes. + de.isText = !de.text.isEmpty(); + + de.modifiers = 0; + if (!inCapsLock && keyEvent->modifiers() & Qt::ShiftModifier) + de.modifiers |= 1 << ATSPI_MODIFIER_SHIFT; + if (inCapsLock && (keyEvent->key() != Qt::Key_CapsLock)) + de.modifiers |= 1 << ATSPI_MODIFIER_SHIFTLOCK; + if ((keyEvent->modifiers() & Qt::ControlModifier) && (keyEvent->key() != Qt::Key_Control)) + de.modifiers |= 1 << ATSPI_MODIFIER_CONTROL; + if ((keyEvent->modifiers() & Qt::AltModifier) && (keyEvent->key() != Qt::Key_Alt)) + de.modifiers |= 1 << ATSPI_MODIFIER_ALT; + if ((keyEvent->modifiers() & Qt::MetaModifier) && (keyEvent->key() != Qt::Key_Meta)) + de.modifiers |= 1 << ATSPI_MODIFIER_META; + +#ifdef KEYBOARD_DEBUG + qDebug() << "Key event text:" << event->type() << de.text + << "native virtual key:" << de.id + << "hardware code/scancode:" << de.hardwareCode + << "modifiers:" << de.modifiers + << "text:" << de.text; +#endif + + QDBusMessage m = QDBusMessage::createMethodCall(QStringLiteral("org.a11y.atspi.Registry"), + QStringLiteral("/org/a11y/atspi/registry/deviceeventcontroller"), + QStringLiteral("org.a11y.atspi.DeviceEventController"), QStringLiteral("NotifyListenersSync")); + m.setArguments(QVariantList() << QVariant::fromValue(de)); + + // FIXME: this is critical, the timeout should probably be pretty low to allow normal processing + int timeout = 100; + bool sent = dbusConnection.callWithCallback(m, this, SLOT(notifyKeyboardListenerCallback(QDBusMessage)), + SLOT(notifyKeyboardListenerError(QDBusError,QDBusMessage)), timeout); + if (sent) { + //queue the event and send it after callback + keyEvents.enqueue(QPair, QKeyEvent*> (QPointer(target), copyKeyEvent(keyEvent))); + return true; + } + } + default: + break; + } + return false; +} + +QKeyEvent* QSpiApplicationAdaptor::copyKeyEvent(QKeyEvent* old) +{ + return new QKeyEvent(old->type(), old->key(), old->modifiers(), + old->nativeScanCode(), old->nativeVirtualKey(), old->nativeModifiers(), + old->text(), old->isAutoRepeat(), old->count()); +} + +void QSpiApplicationAdaptor::notifyKeyboardListenerCallback(const QDBusMessage& message) +{ + if (!keyEvents.length()) { + qWarning("QSpiApplication::notifyKeyboardListenerCallback with no queued key called"); + return; + } + Q_ASSERT(message.arguments().length() == 1); + if (message.arguments().at(0).toBool() == true) { + QPair, QKeyEvent*> event = keyEvents.dequeue(); + delete event.second; + } else { + QPair, QKeyEvent*> event = keyEvents.dequeue(); + if (event.first) + QCoreApplication::postEvent(event.first.data(), event.second); + } +} + +void QSpiApplicationAdaptor::notifyKeyboardListenerError(const QDBusError& error, const QDBusMessage& /*message*/) +{ + qWarning() << "QSpiApplication::keyEventError " << error.name() << error.message(); + while (!keyEvents.isEmpty()) { + QPair, QKeyEvent*> event = keyEvents.dequeue(); + if (event.first) + QCoreApplication::postEvent(event.first.data(), event.second); + } +} + +QT_END_NAMESPACE + +#endif //QT_NO_ACCESSIBILITY diff --git a/src/gui/accessible/linux/qspiapplicationadaptor_p.h b/src/gui/accessible/linux/qspiapplicationadaptor_p.h new file mode 100644 index 0000000000..ca206edcd4 --- /dev/null +++ b/src/gui/accessible/linux/qspiapplicationadaptor_p.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef Q_SPI_APPLICATION_H +#define Q_SPI_APPLICATION_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include +#include +Q_MOC_INCLUDE() + +QT_REQUIRE_CONFIG(accessibility); + +QT_BEGIN_NAMESPACE + +/* + * Used for the root object. + * + * Uses the root object reference and reports its parent as the desktop object. + */ +class QSpiApplicationAdaptor :public QObject +{ + Q_OBJECT + +public: + QSpiApplicationAdaptor(const QDBusConnection &connection, QObject *parent); + virtual ~QSpiApplicationAdaptor() {} + void sendEvents(bool active); + +Q_SIGNALS: + void windowActivated(QObject* window, bool active); + +protected: + bool eventFilter(QObject *obj, QEvent *event) override; + +private Q_SLOTS: + void notifyKeyboardListenerCallback(const QDBusMessage& message); + void notifyKeyboardListenerError(const QDBusError& error, const QDBusMessage& message); + +private: + static QKeyEvent* copyKeyEvent(QKeyEvent*); + + QQueue, QKeyEvent*> > keyEvents; + QDBusConnection dbusConnection; + bool inCapsLock; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/gui/accessible/linux/qspidbuscache.cpp b/src/gui/accessible/linux/qspidbuscache.cpp new file mode 100644 index 0000000000..189cd2adf8 --- /dev/null +++ b/src/gui/accessible/linux/qspidbuscache.cpp @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qspidbuscache_p.h" +#include "qspiaccessiblebridge_p.h" + +#ifndef QT_NO_ACCESSIBILITY +#include "cache_adaptor.h" + +#define QSPI_OBJECT_PATH_CACHE "/org/a11y/atspi/cache" + +QT_BEGIN_NAMESPACE + +/*! + \class QSpiDBusCache + \internal + \brief This class is responsible for the AT-SPI cache interface. + + The idea behind the cache is that starting an application would + result in many dbus calls. The way GTK/Gail/ATK work is that + they create accessibles for all objects on startup. + In order to avoid querying all the objects individually via DBus + they get sent by using the GetItems call of the cache. + + Additionally the AddAccessible and RemoveAccessible signals + are responsible for adding/removing objects from the cache. + + Currently the Qt bridge chooses to ignore these. +*/ + +QSpiDBusCache::QSpiDBusCache(QDBusConnection c, QObject* parent) + : QObject(parent) +{ + new CacheAdaptor(this); + c.registerObject(QLatin1String(QSPI_OBJECT_PATH_CACHE), this, QDBusConnection::ExportAdaptors); +} + +void QSpiDBusCache::emitAddAccessible(const QSpiAccessibleCacheItem& item) +{ + emit AddAccessible(item); +} + +void QSpiDBusCache::emitRemoveAccessible(const QSpiObjectReference& item) +{ + emit RemoveAccessible(item); +} + +QSpiAccessibleCacheArray QSpiDBusCache::GetItems() +{ + return QSpiAccessibleCacheArray(); +} + +QT_END_NAMESPACE +#endif //QT_NO_ACCESSIBILITY diff --git a/src/gui/accessible/linux/qspidbuscache_p.h b/src/gui/accessible/linux/qspidbuscache_p.h new file mode 100644 index 0000000000..89bee59d3a --- /dev/null +++ b/src/gui/accessible/linux/qspidbuscache_p.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef Q_SPI_CACHE_H +#define Q_SPI_CACHE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include "qspi_struct_marshallers_p.h" + +QT_REQUIRE_CONFIG(accessibility); + +QT_BEGIN_NAMESPACE + +class QSpiDBusCache : public QObject +{ + Q_OBJECT + +public: + explicit QSpiDBusCache(QDBusConnection c, QObject* parent = nullptr); + void emitAddAccessible(const QSpiAccessibleCacheItem& item); + void emitRemoveAccessible(const QSpiObjectReference& item); + +Q_SIGNALS: + void AddAccessible(const QSpiAccessibleCacheItem &nodeAdded); + void RemoveAccessible(const QSpiObjectReference &nodeRemoved); + +public Q_SLOTS: + QSpiAccessibleCacheArray GetItems(); +}; + +QT_END_NAMESPACE + +#endif /* Q_SPI_CACHE_H */ diff --git a/src/gui/platform/unix/dbusmenu/qdbusmenuconnection_p.h b/src/gui/platform/unix/dbusmenu/qdbusmenuconnection_p.h index bbdaad1e89..acd34c20ac 100644 --- a/src/gui/platform/unix/dbusmenu/qdbusmenuconnection_p.h +++ b/src/gui/platform/unix/dbusmenu/qdbusmenuconnection_p.h @@ -37,8 +37,8 @@ ** ****************************************************************************/ -#ifndef DBUSCONNECTION_H -#define DBUSCONNECTION_H +#ifndef QDBUSMENUCONNECTION_H +#define QDBUSMENUCONNECTION_H // // W A R N I N G @@ -98,4 +98,4 @@ private: QT_END_NAMESPACE -#endif // DBUSCONNECTION_H +#endif // QDBUSMENUCONNECTION_H diff --git a/src/platformsupport/.prev_CMakeLists.txt b/src/platformsupport/.prev_CMakeLists.txt index c43707948b..9e2f19bee6 100644 --- a/src/platformsupport/.prev_CMakeLists.txt +++ b/src/platformsupport/.prev_CMakeLists.txt @@ -15,6 +15,3 @@ endif() if(QT_FEATURE_kms) add_subdirectory(kmsconvenience) endif() -if(QT_FEATURE_accessibility AND QT_FEATURE_accessibility_atspi_bridge) - add_subdirectory(linuxaccessibility) -endif() diff --git a/src/platformsupport/CMakeLists.txt b/src/platformsupport/CMakeLists.txt index fd308c99fd..db2fb62261 100644 --- a/src/platformsupport/CMakeLists.txt +++ b/src/platformsupport/CMakeLists.txt @@ -19,6 +19,3 @@ endif() if(QT_FEATURE_kms) add_subdirectory(kmsconvenience) endif() -if(QT_FEATURE_accessibility AND QT_FEATURE_accessibility_atspi_bridge) - add_subdirectory(linuxaccessibility) -endif() diff --git a/src/platformsupport/linuxaccessibility/.prev_CMakeLists.txt b/src/platformsupport/linuxaccessibility/.prev_CMakeLists.txt deleted file mode 100644 index ffb0ca9610..0000000000 --- a/src/platformsupport/linuxaccessibility/.prev_CMakeLists.txt +++ /dev/null @@ -1,40 +0,0 @@ -# Generated from linuxaccessibility.pro. - -##################################################################### -## LinuxAccessibilitySupport Module: -##################################################################### - -qt_add_module(LinuxAccessibilitySupport - STATIC - INTERNAL_MODULE - SOURCES - application.cpp application_p.h - atspiadaptor.cpp atspiadaptor_p.h - bridge.cpp bridge_p.h - cache.cpp cache_p.h - constant_mappings.cpp constant_mappings_p.h - dbusconnection.cpp dbusconnection_p.h - struct_marshallers.cpp struct_marshallers_p.h - DBUS_ADAPTOR_SOURCES - dbusxml/Cache.xml - dbusxml/DeviceEventController.xml - DBUS_ADAPTOR_FLAGS - "-i" "struct_marshallers_p.h" - DBUS_INTERFACE_SOURCES - dbusxml/Bus.xml - dbusxml/Socket.xml - DBUS_INTERFACE_FLAGS - "-i" "struct_marshallers_p.h" - DEFINES - QT_NO_CAST_FROM_ASCII - PUBLIC_LIBRARIES - PkgConfig::ATSPI2_nolink - Qt::CorePrivate - Qt::DBus - Qt::GuiPrivate - PRECOMPILED_HEADER - "../../corelib/global/qt_pch.h" -) - -#### Keys ignored in scope 1:.:.:linuxaccessibility.pro:: -# MODULE = "linuxaccessibility_support" diff --git a/src/platformsupport/linuxaccessibility/CMakeLists.txt b/src/platformsupport/linuxaccessibility/CMakeLists.txt deleted file mode 100644 index 0e1a7a9d16..0000000000 --- a/src/platformsupport/linuxaccessibility/CMakeLists.txt +++ /dev/null @@ -1,42 +0,0 @@ -# Generated from linuxaccessibility.pro. - -qt_find_package(ATSPI2) # special case - -##################################################################### -## LinuxAccessibilitySupport Module: -##################################################################### - -qt_add_module(LinuxAccessibilitySupport - STATIC - INTERNAL_MODULE - SOURCES - application.cpp application_p.h - atspiadaptor.cpp atspiadaptor_p.h - bridge.cpp bridge_p.h - cache.cpp cache_p.h - constant_mappings.cpp constant_mappings_p.h - dbusconnection.cpp dbusconnection_p.h - struct_marshallers.cpp struct_marshallers_p.h - DBUS_ADAPTOR_SOURCES - dbusxml/Cache.xml - dbusxml/DeviceEventController.xml - DBUS_ADAPTOR_FLAGS - "-i" "struct_marshallers_p.h" - DBUS_INTERFACE_SOURCES - dbusxml/Bus.xml - dbusxml/Socket.xml - DBUS_INTERFACE_FLAGS - "-i" "struct_marshallers_p.h" - DEFINES - QT_NO_CAST_FROM_ASCII - PUBLIC_LIBRARIES - PkgConfig::ATSPI2_nolink - Qt::CorePrivate - Qt::DBus - Qt::GuiPrivate - PRECOMPILED_HEADER - "../../corelib/global/qt_pch.h" -) - -#### Keys ignored in scope 1:.:.:linuxaccessibility.pro:: -# MODULE = "linuxaccessibility_support" diff --git a/src/platformsupport/linuxaccessibility/application.cpp b/src/platformsupport/linuxaccessibility/application.cpp deleted file mode 100644 index 7852e407ad..0000000000 --- a/src/platformsupport/linuxaccessibility/application.cpp +++ /dev/null @@ -1,240 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - - -#include "application_p.h" - -#include -#include -#include - -#ifndef QT_NO_ACCESSIBILITY -#include "deviceeventcontroller_adaptor.h" -#include "atspi/atspi-constants.h" - -//#define KEYBOARD_DEBUG - -QT_BEGIN_NAMESPACE - -/*! - \class QSpiApplicationAdaptor - \internal - - \brief QSpiApplicationAdaptor - - QSpiApplicationAdaptor -*/ - -QSpiApplicationAdaptor::QSpiApplicationAdaptor(const QDBusConnection &connection, QObject *parent) - : QObject(parent), dbusConnection(connection), inCapsLock(false) -{ -} - -enum QSpiKeyEventType { - QSPI_KEY_EVENT_PRESS, - QSPI_KEY_EVENT_RELEASE, - QSPI_KEY_EVENT_LAST_DEFINED -}; - -void QSpiApplicationAdaptor::sendEvents(bool active) -{ - if (active) { - qApp->installEventFilter(this); - } else { - qApp->removeEventFilter(this); - } -} - - -bool QSpiApplicationAdaptor::eventFilter(QObject *target, QEvent *event) -{ - if (!event->spontaneous()) - return false; - - switch (event->type()) { - case QEvent::WindowActivate: - emit windowActivated(target, true); - break; - case QEvent::WindowDeactivate: - emit windowActivated(target, false); - break; - case QEvent::KeyPress: - case QEvent::KeyRelease: { - QKeyEvent *keyEvent = static_cast (event); - QSpiDeviceEvent de; - - if (event->type() == QEvent::KeyPress) - de.type = QSPI_KEY_EVENT_PRESS; - else - de.type = QSPI_KEY_EVENT_RELEASE; - - de.id = keyEvent->nativeVirtualKey(); - de.hardwareCode = keyEvent->nativeScanCode(); - - de.timestamp = QDateTime::currentMSecsSinceEpoch(); - - if (keyEvent->key() == Qt::Key_Tab) - de.text = QStringLiteral("Tab"); - else if (keyEvent->key() == Qt::Key_Backtab) - de.text = QStringLiteral("Backtab"); - else if (keyEvent->key() == Qt::Key_Control) - de.text = QStringLiteral("Control_L"); - else if (keyEvent->key() == Qt::Key_Left) - de.text = (keyEvent->modifiers() & Qt::KeypadModifier) ? QStringLiteral("KP_Left") : QStringLiteral("Left"); - else if (keyEvent->key() == Qt::Key_Right) - de.text = (keyEvent->modifiers() & Qt::KeypadModifier) ? QStringLiteral("KP_Right") : QStringLiteral("Right"); - else if (keyEvent->key() == Qt::Key_Up) - de.text = (keyEvent->modifiers() & Qt::KeypadModifier) ? QStringLiteral("KP_Up") : QStringLiteral("Up"); - else if (keyEvent->key() == Qt::Key_Down) - de.text = (keyEvent->modifiers() & Qt::KeypadModifier) ? QStringLiteral("KP_Down") : QStringLiteral("Down"); - else if (keyEvent->key() == Qt::Key_Enter || keyEvent->key() == Qt::Key_Return) - de.text = QStringLiteral("Return"); - else if (keyEvent->key() == Qt::Key_Backspace) - de.text = QStringLiteral("BackSpace"); - else if (keyEvent->key() == Qt::Key_Delete) - de.text = QStringLiteral("Delete"); - else if (keyEvent->key() == Qt::Key_PageUp) - de.text = (keyEvent->modifiers() & Qt::KeypadModifier) ? QStringLiteral("KP_Page_Up") : QStringLiteral("Page_Up"); - else if (keyEvent->key() == Qt::Key_PageDown) - de.text = (keyEvent->modifiers() & Qt::KeypadModifier) ? QStringLiteral("KP_Page_Up") : QStringLiteral("Page_Down"); - else if (keyEvent->key() == Qt::Key_Home) - de.text = (keyEvent->modifiers() & Qt::KeypadModifier) ? QStringLiteral("KP_Home") : QStringLiteral("Home"); - else if (keyEvent->key() == Qt::Key_End) - de.text = (keyEvent->modifiers() & Qt::KeypadModifier) ? QStringLiteral("KP_End") : QStringLiteral("End"); - else if (keyEvent->key() == Qt::Key_Clear && (keyEvent->modifiers() & Qt::KeypadModifier)) - de.text = QStringLiteral("KP_Begin"); // Key pad 5 - else if (keyEvent->key() == Qt::Key_Escape) - de.text = QStringLiteral("Escape"); - else if (keyEvent->key() == Qt::Key_Space) - de.text = QStringLiteral("space"); - else if (keyEvent->key() == Qt::Key_CapsLock) { - de.text = QStringLiteral("Caps_Lock"); - if (event->type() == QEvent::KeyPress) - inCapsLock = true; - else - inCapsLock = false; - } else if (keyEvent->key() == Qt::Key_NumLock) - de.text = QStringLiteral("Num_Lock"); - else if (keyEvent->key() == Qt::Key_Insert) - de.text = QStringLiteral("Insert"); - else - de.text = keyEvent->text(); - - // This is a bit dubious, Gnome uses some gtk function here. - // Long term the spec will hopefully change to just use keycodes. - de.isText = !de.text.isEmpty(); - - de.modifiers = 0; - if (!inCapsLock && keyEvent->modifiers() & Qt::ShiftModifier) - de.modifiers |= 1 << ATSPI_MODIFIER_SHIFT; - if (inCapsLock && (keyEvent->key() != Qt::Key_CapsLock)) - de.modifiers |= 1 << ATSPI_MODIFIER_SHIFTLOCK; - if ((keyEvent->modifiers() & Qt::ControlModifier) && (keyEvent->key() != Qt::Key_Control)) - de.modifiers |= 1 << ATSPI_MODIFIER_CONTROL; - if ((keyEvent->modifiers() & Qt::AltModifier) && (keyEvent->key() != Qt::Key_Alt)) - de.modifiers |= 1 << ATSPI_MODIFIER_ALT; - if ((keyEvent->modifiers() & Qt::MetaModifier) && (keyEvent->key() != Qt::Key_Meta)) - de.modifiers |= 1 << ATSPI_MODIFIER_META; - -#ifdef KEYBOARD_DEBUG - qDebug() << "Key event text:" << event->type() << de.text - << "native virtual key:" << de.id - << "hardware code/scancode:" << de.hardwareCode - << "modifiers:" << de.modifiers - << "text:" << de.text; -#endif - - QDBusMessage m = QDBusMessage::createMethodCall(QStringLiteral("org.a11y.atspi.Registry"), - QStringLiteral("/org/a11y/atspi/registry/deviceeventcontroller"), - QStringLiteral("org.a11y.atspi.DeviceEventController"), QStringLiteral("NotifyListenersSync")); - m.setArguments(QVariantList() << QVariant::fromValue(de)); - - // FIXME: this is critical, the timeout should probably be pretty low to allow normal processing - int timeout = 100; - bool sent = dbusConnection.callWithCallback(m, this, SLOT(notifyKeyboardListenerCallback(QDBusMessage)), - SLOT(notifyKeyboardListenerError(QDBusError,QDBusMessage)), timeout); - if (sent) { - //queue the event and send it after callback - keyEvents.enqueue(QPair, QKeyEvent*> (QPointer(target), copyKeyEvent(keyEvent))); - return true; - } - } - default: - break; - } - return false; -} - -QKeyEvent* QSpiApplicationAdaptor::copyKeyEvent(QKeyEvent* old) -{ - return new QKeyEvent(old->type(), old->key(), old->modifiers(), - old->nativeScanCode(), old->nativeVirtualKey(), old->nativeModifiers(), - old->text(), old->isAutoRepeat(), old->count()); -} - -void QSpiApplicationAdaptor::notifyKeyboardListenerCallback(const QDBusMessage& message) -{ - if (!keyEvents.length()) { - qWarning("QSpiApplication::notifyKeyboardListenerCallback with no queued key called"); - return; - } - Q_ASSERT(message.arguments().length() == 1); - if (message.arguments().at(0).toBool() == true) { - QPair, QKeyEvent*> event = keyEvents.dequeue(); - delete event.second; - } else { - QPair, QKeyEvent*> event = keyEvents.dequeue(); - if (event.first) - QCoreApplication::postEvent(event.first.data(), event.second); - } -} - -void QSpiApplicationAdaptor::notifyKeyboardListenerError(const QDBusError& error, const QDBusMessage& /*message*/) -{ - qWarning() << "QSpiApplication::keyEventError " << error.name() << error.message(); - while (!keyEvents.isEmpty()) { - QPair, QKeyEvent*> event = keyEvents.dequeue(); - if (event.first) - QCoreApplication::postEvent(event.first.data(), event.second); - } -} - -QT_END_NAMESPACE - -#endif //QT_NO_ACCESSIBILITY diff --git a/src/platformsupport/linuxaccessibility/application_p.h b/src/platformsupport/linuxaccessibility/application_p.h deleted file mode 100644 index 6a8786dc07..0000000000 --- a/src/platformsupport/linuxaccessibility/application_p.h +++ /dev/null @@ -1,99 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef Q_SPI_APPLICATION_H -#define Q_SPI_APPLICATION_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include -#include -#include -#include -#include -Q_MOC_INCLUDE() - -QT_REQUIRE_CONFIG(accessibility); - -QT_BEGIN_NAMESPACE - -/* - * Used for the root object. - * - * Uses the root object reference and reports its parent as the desktop object. - */ -class QSpiApplicationAdaptor :public QObject -{ - Q_OBJECT - -public: - QSpiApplicationAdaptor(const QDBusConnection &connection, QObject *parent); - virtual ~QSpiApplicationAdaptor() {} - void sendEvents(bool active); - -Q_SIGNALS: - void windowActivated(QObject* window, bool active); - -protected: - bool eventFilter(QObject *obj, QEvent *event) override; - -private Q_SLOTS: - void notifyKeyboardListenerCallback(const QDBusMessage& message); - void notifyKeyboardListenerError(const QDBusError& error, const QDBusMessage& message); - -private: - static QKeyEvent* copyKeyEvent(QKeyEvent*); - - QQueue, QKeyEvent*> > keyEvents; - QDBusConnection dbusConnection; - bool inCapsLock; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/platformsupport/linuxaccessibility/atspiadaptor.cpp b/src/platformsupport/linuxaccessibility/atspiadaptor.cpp deleted file mode 100644 index c4ff9a211b..0000000000 --- a/src/platformsupport/linuxaccessibility/atspiadaptor.cpp +++ /dev/null @@ -1,2482 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "atspiadaptor_p.h" - -#include -#include -#include -#include -#include - -#include - -#ifndef QT_NO_ACCESSIBILITY -#include "socket_interface.h" -#include "constant_mappings_p.h" -#include - -#include "application_p.h" -/*! - \class AtSpiAdaptor - \internal - - \brief AtSpiAdaptor is the main class to forward between QAccessibleInterface and AT-SPI DBus - - AtSpiAdaptor implements the functions specified in all at-spi interfaces. - It sends notifications coming from Qt via dbus and listens to incoming dbus requests. -*/ - -QT_BEGIN_NAMESPACE - -Q_LOGGING_CATEGORY(lcAccessibilityAtspi, "qt.accessibility.atspi") -Q_LOGGING_CATEGORY(lcAccessibilityAtspiCreation, "qt.accessibility.atspi.creation") - -AtSpiAdaptor::AtSpiAdaptor(DBusConnection *connection, QObject *parent) - : QDBusVirtualObject(parent), m_dbus(connection) - , sendFocus(0) - , sendObject(0) - , sendObject_active_descendant_changed(0) - , sendObject_attributes_changed(0) - , sendObject_bounds_changed(0) - , sendObject_children_changed(0) -// , sendObject_children_changed_add(0) -// , sendObject_children_changed_remove(0) - , sendObject_column_deleted(0) - , sendObject_column_inserted(0) - , sendObject_column_reordered(0) - , sendObject_link_selected(0) - , sendObject_model_changed(0) - , sendObject_property_change(0) - , sendObject_property_change_accessible_description(0) - , sendObject_property_change_accessible_name(0) - , sendObject_property_change_accessible_parent(0) - , sendObject_property_change_accessible_role(0) - , sendObject_property_change_accessible_table_caption(0) - , sendObject_property_change_accessible_table_column_description(0) - , sendObject_property_change_accessible_table_column_header(0) - , sendObject_property_change_accessible_table_row_description(0) - , sendObject_property_change_accessible_table_row_header(0) - , sendObject_property_change_accessible_table_summary(0) - , sendObject_property_change_accessible_value(0) - , sendObject_row_deleted(0) - , sendObject_row_inserted(0) - , sendObject_row_reordered(0) - , sendObject_selection_changed(0) - , sendObject_state_changed(0) - , sendObject_text_attributes_changed(0) - , sendObject_text_bounds_changed(0) - , sendObject_text_caret_moved(0) - , sendObject_text_changed(0) -// , sendObject_text_changed_delete(0) -// , sendObject_text_changed_insert(0) - , sendObject_text_selection_changed(0) - , sendObject_value_changed(0) - , sendObject_visible_data_changed(0) - , sendWindow(0) - , sendWindow_activate(0) - , sendWindow_close(0) - , sendWindow_create(0) - , sendWindow_deactivate(0) -// , sendWindow_desktop_create(0) -// , sendWindow_desktop_destroy(0) - , sendWindow_lower(0) - , sendWindow_maximize(0) - , sendWindow_minimize(0) - , sendWindow_move(0) - , sendWindow_raise(0) - , sendWindow_reparent(0) - , sendWindow_resize(0) - , sendWindow_restore(0) - , sendWindow_restyle(0) - , sendWindow_shade(0) - , sendWindow_unshade(0) -{ - 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))); -} - -AtSpiAdaptor::~AtSpiAdaptor() -{ -} - -/*! - Provide DBus introspection. - */ -QString AtSpiAdaptor::introspect(const QString &path) const -{ - static const QLatin1String accessibleIntrospection( - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - ); - - static const QLatin1String actionIntrospection( - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - ); - - static const QLatin1String applicationIntrospection( - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - ); - - static const QLatin1String componentIntrospection( - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - ); - - static const QLatin1String editableTextIntrospection( - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - ); - - static const QLatin1String tableIntrospection( - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - ); - - static const QLatin1String textIntrospection( - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - ); - - static const QLatin1String valueIntrospection( - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - ); - - QAccessibleInterface * interface = interfaceFromPath(path); - if (!interface) { - qCDebug(lcAccessibilityAtspi) << "WARNING Qt AtSpiAdaptor: Could not find accessible on path: " << path; - return QString(); - } - - QStringList interfaces = accessibleInterfaces(interface); - - QString xml; - xml.append(accessibleIntrospection); - - if (interfaces.contains(QLatin1String(ATSPI_DBUS_INTERFACE_COMPONENT))) - xml.append(componentIntrospection); - if (interfaces.contains(QLatin1String(ATSPI_DBUS_INTERFACE_TEXT))) - xml.append(textIntrospection); - if (interfaces.contains(QLatin1String(ATSPI_DBUS_INTERFACE_EDITABLE_TEXT))) - xml.append(editableTextIntrospection); - if (interfaces.contains(QLatin1String(ATSPI_DBUS_INTERFACE_ACTION))) - xml.append(actionIntrospection); - if (interfaces.contains(QLatin1String(ATSPI_DBUS_INTERFACE_TABLE))) - xml.append(tableIntrospection); - if (interfaces.contains(QLatin1String(ATSPI_DBUS_INTERFACE_VALUE))) - xml.append(valueIntrospection); - if (path == QLatin1String(QSPI_OBJECT_PATH_ROOT)) - xml.append(applicationIntrospection); - - return xml; -} - -void AtSpiAdaptor::setBitFlag(const QString &flag) -{ - Q_ASSERT(flag.size()); - - // assume we don't get nonsense - look at first letter only - switch (flag.at(0).toLower().toLatin1()) { - case 'o': { - if (flag.size() <= 8) { // Object:: - sendObject = 1; - } else { // Object:Foo:Bar - QString right = flag.mid(7); - if (false) { - } else if (right.startsWith(QLatin1String("ActiveDescendantChanged"))) { - sendObject_active_descendant_changed = 1; - } else if (right.startsWith(QLatin1String("AttributesChanged"))) { - sendObject_attributes_changed = 1; - } else if (right.startsWith(QLatin1String("BoundsChanged"))) { - sendObject_bounds_changed = 1; - } else if (right.startsWith(QLatin1String("ChildrenChanged"))) { - sendObject_children_changed = 1; - } else if (right.startsWith(QLatin1String("ColumnDeleted"))) { - sendObject_column_deleted = 1; - } else if (right.startsWith(QLatin1String("ColumnInserted"))) { - sendObject_column_inserted = 1; - } else if (right.startsWith(QLatin1String("ColumnReordered"))) { - sendObject_column_reordered = 1; - } else if (right.startsWith(QLatin1String("LinkSelected"))) { - sendObject_link_selected = 1; - } else if (right.startsWith(QLatin1String("ModelChanged"))) { - sendObject_model_changed = 1; - } else if (right.startsWith(QLatin1String("PropertyChange"))) { - if (right == QLatin1String("PropertyChange:AccessibleDescription")) { - sendObject_property_change_accessible_description = 1; - } else if (right == QLatin1String("PropertyChange:AccessibleName")) { - sendObject_property_change_accessible_name = 1; - } else if (right == QLatin1String("PropertyChange:AccessibleParent")) { - sendObject_property_change_accessible_parent = 1; - } else if (right == QLatin1String("PropertyChange:AccessibleRole")) { - sendObject_property_change_accessible_role = 1; - } else if (right == QLatin1String("PropertyChange:TableCaption")) { - sendObject_property_change_accessible_table_caption = 1; - } else if (right == QLatin1String("PropertyChange:TableColumnDescription")) { - sendObject_property_change_accessible_table_column_description = 1; - } else if (right == QLatin1String("PropertyChange:TableColumnHeader")) { - sendObject_property_change_accessible_table_column_header = 1; - } else if (right == QLatin1String("PropertyChange:TableRowDescription")) { - sendObject_property_change_accessible_table_row_description = 1; - } else if (right == QLatin1String("PropertyChange:TableRowHeader")) { - sendObject_property_change_accessible_table_row_header = 1; - } else if (right == QLatin1String("PropertyChange:TableSummary")) { - sendObject_property_change_accessible_table_summary = 1; - } else if (right == QLatin1String("PropertyChange:AccessibleValue")) { - sendObject_property_change_accessible_value = 1; - } else { - sendObject_property_change = 1; - } - } else if (right.startsWith(QLatin1String("RowDeleted"))) { - sendObject_row_deleted = 1; - } else if (right.startsWith(QLatin1String("RowInserted"))) { - sendObject_row_inserted = 1; - } else if (right.startsWith(QLatin1String("RowReordered"))) { - sendObject_row_reordered = 1; - } else if (right.startsWith(QLatin1String("SelectionChanged"))) { - sendObject_selection_changed = 1; - } else if (right.startsWith(QLatin1String("StateChanged"))) { - sendObject_state_changed = 1; - } else if (right.startsWith(QLatin1String("TextAttributesChanged"))) { - sendObject_text_attributes_changed = 1; - } else if (right.startsWith(QLatin1String("TextBoundsChanged"))) { - sendObject_text_bounds_changed = 1; - } else if (right.startsWith(QLatin1String("TextCaretMoved"))) { - sendObject_text_caret_moved = 1; - } else if (right.startsWith(QLatin1String("TextChanged"))) { - sendObject_text_changed = 1; - } else if (right.startsWith(QLatin1String("TextSelectionChanged"))) { - sendObject_text_selection_changed = 1; - } else if (right.startsWith(QLatin1String("ValueChanged"))) { - sendObject_value_changed = 1; - } else if (right.startsWith(QLatin1String("VisibleDataChanged")) - || right.startsWith(QLatin1String("VisibledataChanged"))) { // typo in libatspi - sendObject_visible_data_changed = 1; - } else { - qCDebug(lcAccessibilityAtspi) << "WARNING: subscription string not handled:" << flag; - } - } - break; - } - case 'w': { // window - if (flag.size() <= 8) { - sendWindow = 1; - } else { // object:Foo:Bar - QString right = flag.mid(7); - if (false) { - } else if (right.startsWith(QLatin1String("Activate"))) { - sendWindow_activate = 1; - } else if (right.startsWith(QLatin1String("Close"))) { - sendWindow_close= 1; - } else if (right.startsWith(QLatin1String("Create"))) { - sendWindow_create = 1; - } else if (right.startsWith(QLatin1String("Deactivate"))) { - sendWindow_deactivate = 1; - } else if (right.startsWith(QLatin1String("Lower"))) { - sendWindow_lower = 1; - } else if (right.startsWith(QLatin1String("Maximize"))) { - sendWindow_maximize = 1; - } else if (right.startsWith(QLatin1String("Minimize"))) { - sendWindow_minimize = 1; - } else if (right.startsWith(QLatin1String("Move"))) { - sendWindow_move = 1; - } else if (right.startsWith(QLatin1String("Raise"))) { - sendWindow_raise = 1; - } else if (right.startsWith(QLatin1String("Reparent"))) { - sendWindow_reparent = 1; - } else if (right.startsWith(QLatin1String("Resize"))) { - sendWindow_resize = 1; - } else if (right.startsWith(QLatin1String("Restore"))) { - sendWindow_restore = 1; - } else if (right.startsWith(QLatin1String("Restyle"))) { - sendWindow_restyle = 1; - } else if (right.startsWith(QLatin1String("Shade"))) { - sendWindow_shade = 1; - } else if (right.startsWith(QLatin1String("Unshade"))) { - sendWindow_unshade = 1; - } else if (right.startsWith(QLatin1String("DesktopCreate"))) { - // ignore this one - } else if (right.startsWith(QLatin1String("DesktopDestroy"))) { - // ignore this one - } else { - qCDebug(lcAccessibilityAtspi) << "WARNING: subscription string not handled:" << flag; - } - } - break; - } - case 'f': { - sendFocus = 1; - break; - } - case 'd': { // document is not implemented - break; - } - case 't': { // terminal is not implemented - break; - } - case 'm': { // mouse* is handled in a different way by the gnome atspi stack - break; - } - default: - qCDebug(lcAccessibilityAtspi) << "WARNING: subscription string not handled:" << flag; - } -} - -/*! - Checks via dbus which events should be sent. - */ -void AtSpiAdaptor::updateEventListeners() -{ - QDBusMessage m = QDBusMessage::createMethodCall(QLatin1String("org.a11y.atspi.Registry"), - QLatin1String("/org/a11y/atspi/registry"), - QLatin1String("org.a11y.atspi.Registry"), QLatin1String("GetRegisteredEvents")); - QDBusReply listenersReply = m_dbus->connection().call(m); - if (listenersReply.isValid()) { - const QSpiEventListenerArray evList = listenersReply.value(); - for (const QSpiEventListener &ev : evList) - setBitFlag(ev.eventName); - m_applicationAdaptor->sendEvents(!evList.isEmpty()); - } else { - qCDebug(lcAccessibilityAtspi) << "Could not query active accessibility event listeners."; - } -} - -void AtSpiAdaptor::eventListenerDeregistered(const QString &/*bus*/, const QString &/*path*/) -{ -// qCDebug(lcAccessibilityAtspi) << "AtSpiAdaptor::eventListenerDeregistered: " << bus << path; - updateEventListeners(); -} - -void AtSpiAdaptor::eventListenerRegistered(const QString &/*bus*/, const QString &/*path*/) -{ -// qCDebug(lcAccessibilityAtspi) << "AtSpiAdaptor::eventListenerRegistered: " << bus << path; - updateEventListeners(); -} - -/*! - This slot needs to get called when a \a window has be activated or deactivated (become focused). - When \a active is true, the window just received focus, otherwise it lost the focus. - */ -void AtSpiAdaptor::windowActivated(QObject* window, bool active) -{ - if (!(sendWindow || sendWindow_activate)) - return; - - QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(window); - Q_ASSERT(iface); - Q_ASSERT(!active || iface->isValid()); - - QString windowTitle; - // in dtor it may be invalid - if (iface->isValid()) - windowTitle = iface->text(QAccessible::Name); - - QDBusVariant data; - data.setVariant(windowTitle); - - QVariantList args = packDBusSignalArguments(QString(), 0, 0, QVariant::fromValue(data)); - - QString status = active ? QLatin1String("Activate") : QLatin1String("Deactivate"); - QString path = pathForObject(window); - sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_WINDOW), status, args); - - QVariantList stateArgs = packDBusSignalArguments(QLatin1String("active"), active ? 1 : 0, 0, variantForPath(path)); - sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT), - QLatin1String("StateChanged"), stateArgs); -} - -QVariantList AtSpiAdaptor::packDBusSignalArguments(const QString &type, int data1, int data2, const QVariant &variantData) const -{ - QVariantList arguments; - arguments << type << data1 << data2 << variantData - << QVariant::fromValue(QSpiObjectReference(m_dbus->connection(), QDBusObjectPath(QSPI_OBJECT_PATH_ROOT))); - return arguments; -} - -QVariant AtSpiAdaptor::variantForPath(const QString &path) const -{ - QDBusVariant data; - data.setVariant(QVariant::fromValue(QSpiObjectReference(m_dbus->connection(), QDBusObjectPath(path)))); - return QVariant::fromValue(data); -} - -bool AtSpiAdaptor::sendDBusSignal(const QString &path, const QString &interface, const QString &signalName, const QVariantList &arguments) const -{ - QDBusMessage message = QDBusMessage::createSignal(path, interface, signalName); - message.setArguments(arguments); - return m_dbus->connection().send(message); -} - -QAccessibleInterface *AtSpiAdaptor::interfaceFromPath(const QString& dbusPath) const -{ - if (dbusPath == QLatin1String(QSPI_OBJECT_PATH_ROOT)) - return QAccessible::queryAccessibleInterface(qApp); - - QStringList parts = dbusPath.split(QLatin1Char('/')); - if (parts.size() != 6) { - qCDebug(lcAccessibilityAtspi) << "invalid path: " << dbusPath; - return 0; - } - - QString objectString = parts.at(5); - QAccessible::Id id = objectString.toUInt(); - - // The id is always in the range [INT_MAX+1, UINT_MAX] - if ((int)id >= 0) - qCWarning(lcAccessibilityAtspi) << "No accessible object found for id: " << id; - - return QAccessible::accessibleInterface(id); -} - -void AtSpiAdaptor::notifyStateChange(QAccessibleInterface *interface, const QString &state, int value) -{ - QString path = pathForInterface(interface); - QVariantList stateArgs = packDBusSignalArguments(state, value, 0, variantForPath(path)); - sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT), - QLatin1String("StateChanged"), stateArgs); -} - - -/*! - This function gets called when Qt notifies about accessibility updates. -*/ -void AtSpiAdaptor::notify(QAccessibleEvent *event) -{ - switch (event->type()) { - case QAccessible::ObjectCreated: - if (sendObject || sendObject_children_changed) - notifyAboutCreation(event->accessibleInterface()); - break; - case QAccessible::ObjectShow: { - if (sendObject || sendObject_state_changed) { - notifyStateChange(event->accessibleInterface(), QLatin1String("showing"), 1); - } - break; - } - case QAccessible::ObjectHide: { - if (sendObject || sendObject_state_changed) { - notifyStateChange(event->accessibleInterface(), QLatin1String("showing"), 0); - } - break; - } - case QAccessible::ObjectDestroyed: { - if (sendObject || sendObject_state_changed) - notifyAboutDestruction(event->accessibleInterface()); - break; - } - case QAccessible::ObjectReorder: { - if (sendObject || sendObject_children_changed) - childrenChanged(event->accessibleInterface()); - break; - } - case QAccessible::NameChanged: { - if (sendObject || sendObject_property_change || sendObject_property_change_accessible_name) { - QString path = pathForInterface(event->accessibleInterface()); - QVariantList args = packDBusSignalArguments(QLatin1String("accessible-name"), 0, 0, variantForPath(path)); - sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT), - QLatin1String("PropertyChange"), args); - } - break; - } - case QAccessible::DescriptionChanged: { - if (sendObject || sendObject_property_change || sendObject_property_change_accessible_description) { - QString path = pathForInterface(event->accessibleInterface()); - QVariantList args = packDBusSignalArguments(QLatin1String("accessible-description"), 0, 0, variantForPath(path)); - sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT), - QLatin1String("PropertyChange"), args); - } - break; - } - case QAccessible::Focus: { - if (sendFocus || sendObject || sendObject_state_changed) - sendFocusChanged(event->accessibleInterface()); - break; - } - case QAccessible::TextInserted: - case QAccessible::TextRemoved: - case QAccessible::TextUpdated: { - if (sendObject || sendObject_text_changed) { - QAccessibleInterface * iface = event->accessibleInterface(); - if (!iface || !iface->textInterface()) { - qCDebug(lcAccessibilityAtspi) << "Received text event for invalid interface."; - return; - } - QString path = pathForInterface(iface); - - int changePosition = 0; - int cursorPosition = 0; - QString textRemoved; - QString textInserted; - - if (event->type() == QAccessible::TextInserted) { - QAccessibleTextInsertEvent *textEvent = static_cast(event); - textInserted = textEvent->textInserted(); - changePosition = textEvent->changePosition(); - cursorPosition = textEvent->cursorPosition(); - } else if (event->type() == QAccessible::TextRemoved) { - QAccessibleTextRemoveEvent *textEvent = static_cast(event); - textRemoved = textEvent->textRemoved(); - changePosition = textEvent->changePosition(); - cursorPosition = textEvent->cursorPosition(); - } else if (event->type() == QAccessible::TextUpdated) { - QAccessibleTextUpdateEvent *textEvent = static_cast(event); - textInserted = textEvent->textInserted(); - textRemoved = textEvent->textRemoved(); - changePosition = textEvent->changePosition(); - cursorPosition = textEvent->cursorPosition(); - } - - QDBusVariant data; - - if (!textRemoved.isEmpty()) { - data.setVariant(QVariant::fromValue(textRemoved)); - QVariantList args = packDBusSignalArguments(QLatin1String("delete"), changePosition, textRemoved.length(), QVariant::fromValue(data)); - sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT), - QLatin1String("TextChanged"), args); - } - - if (!textInserted.isEmpty()) { - data.setVariant(QVariant::fromValue(textInserted)); - QVariantList args = packDBusSignalArguments(QLatin1String("insert"), changePosition, textInserted.length(), QVariant::fromValue(data)); - sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT), - QLatin1String("TextChanged"), args); - } - - // send a cursor update - Q_UNUSED(cursorPosition) -// QDBusVariant cursorData; -// cursorData.setVariant(QVariant::fromValue(cursorPosition)); -// QVariantList args = packDBusSignalArguments(QString(), cursorPosition, 0, QVariant::fromValue(cursorData)); -// sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT), -// QLatin1String("TextCaretMoved"), args); - } - break; - } - case QAccessible::TextCaretMoved: { - if (sendObject || sendObject_text_caret_moved) { - QAccessibleInterface * iface = event->accessibleInterface(); - if (!iface || !iface->textInterface()) { - qCWarning(lcAccessibilityAtspi) << "Sending TextCaretMoved from object that does not implement text interface: " << iface; - return; - } - - QString path = pathForInterface(iface); - QDBusVariant cursorData; - int pos = iface->textInterface()->cursorPosition(); - cursorData.setVariant(QVariant::fromValue(pos)); - QVariantList args = packDBusSignalArguments(QString(), pos, 0, QVariant::fromValue(cursorData)); - sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT), - QLatin1String("TextCaretMoved"), args); - } - break; - } - case QAccessible::TextSelectionChanged: { - if (sendObject || sendObject_text_selection_changed) { - QAccessibleInterface * iface = event->accessibleInterface(); - QString path = pathForInterface(iface); - QVariantList args = packDBusSignalArguments(QString(), 0, 0, QVariant::fromValue(QDBusVariant(QVariant(QString())))); - sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT), - QLatin1String("TextSelectionChanged"), args); - } - break; - } - case QAccessible::ValueChanged: { - if (sendObject || sendObject_value_changed || sendObject_property_change_accessible_value) { - QAccessibleInterface * iface = event->accessibleInterface(); - if (!iface) { - qCWarning(lcAccessibilityAtspi) << "ValueChanged event from invalid accessible."; - return; - } - if (iface->valueInterface()) { - QString path = pathForInterface(iface); - QVariantList args = packDBusSignalArguments(QLatin1String("accessible-value"), 0, 0, variantForPath(path)); - sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT), - QLatin1String("PropertyChange"), args); - } else if (iface->role() == QAccessible::ComboBox) { - // Combo Box with AT-SPI likes to be special - // It requires a name-change to update caches and then selection-changed - QString path = pathForInterface(iface); - QVariantList args1 = packDBusSignalArguments(QLatin1String("accessible-name"), 0, 0, variantForPath(path)); - sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT), - QLatin1String("PropertyChange"), args1); - QVariantList args2 = packDBusSignalArguments(QString(), 0, 0, QVariant::fromValue(QDBusVariant(QVariant(0)))); - sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT), - QLatin1String("SelectionChanged"), args2); - } else { - qCWarning(lcAccessibilityAtspi) << "ValueChanged event and no ValueInterface or ComboBox: " << iface; - } - } - break; - } - case QAccessible::SelectionAdd: - case QAccessible::SelectionRemove: - case QAccessible::Selection: { - QAccessibleInterface * iface = event->accessibleInterface(); - if (!iface) { - qCWarning(lcAccessibilityAtspi) << "Selection event from invalid accessible."; - return; - } - QString path = pathForInterface(iface); - int selected = iface->state().selected ? 1 : 0; - QVariantList stateArgs = packDBusSignalArguments(QLatin1String("selected"), selected, 0, variantForPath(path)); - sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT), - QLatin1String("StateChanged"), stateArgs); - break; - } - - case QAccessible::StateChanged: { - if (sendObject || sendObject_state_changed || sendWindow || sendWindow_activate) { - QAccessible::State stateChange = static_cast(event)->changedStates(); - if (stateChange.checked) { - QAccessibleInterface * iface = event->accessibleInterface(); - if (!iface) { - qCWarning(lcAccessibilityAtspi) << "StateChanged event from invalid accessible."; - return; - } - int checked = iface->state().checked; - notifyStateChange(iface, QLatin1String("checked"), checked); - } else if (stateChange.active) { - QAccessibleInterface * iface = event->accessibleInterface(); - if (!iface || !(iface->role() == QAccessible::Window && (sendWindow || sendWindow_activate))) - return; - int isActive = iface->state().active; - QString windowTitle = iface->text(QAccessible::Name); - QDBusVariant data; - data.setVariant(windowTitle); - QVariantList args = packDBusSignalArguments(QString(), 0, 0, QVariant::fromValue(data)); - QString status = isActive ? QLatin1String("Activate") : QLatin1String("Deactivate"); - QString path = pathForInterface(iface); - sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_WINDOW), status, args); - notifyStateChange(iface, QLatin1String("active"), isActive); - } else if (stateChange.disabled) { - QAccessibleInterface *iface = event->accessibleInterface(); - QAccessible::State state = iface->state(); - bool enabled = !state.disabled; - - notifyStateChange(iface, QLatin1String("enabled"), enabled); - notifyStateChange(iface, QLatin1String("sensitive"), enabled); - } - } - break; - } - // For now we ignore these events - case QAccessible::TableModelChanged: - // For tables, setting manages_descendants should - // indicate to the client that it cannot cache these - // interfaces. - case QAccessible::ParentChanged: - case QAccessible::DialogStart: - case QAccessible::DialogEnd: - case QAccessible::PopupMenuStart: - case QAccessible::PopupMenuEnd: - case QAccessible::SoundPlayed: - case QAccessible::Alert: - case QAccessible::ForegroundChanged: - case QAccessible::MenuStart: - case QAccessible::MenuEnd: - case QAccessible::ContextHelpStart: - case QAccessible::ContextHelpEnd: - case QAccessible::DragDropStart: - case QAccessible::DragDropEnd: - case QAccessible::ScrollingStart: - case QAccessible::ScrollingEnd: - case QAccessible::MenuCommand: - case QAccessible::ActionChanged: - case QAccessible::ActiveDescendantChanged: - case QAccessible::AttributeChanged: - case QAccessible::DocumentContentChanged: - case QAccessible::DocumentLoadComplete: - case QAccessible::DocumentLoadStopped: - case QAccessible::DocumentReload: - case QAccessible::HyperlinkEndIndexChanged: - case QAccessible::HyperlinkNumberOfAnchorsChanged: - case QAccessible::HyperlinkSelectedLinkChanged: - case QAccessible::HypertextLinkActivated: - case QAccessible::HypertextLinkSelected: - case QAccessible::HyperlinkStartIndexChanged: - case QAccessible::HypertextChanged: - case QAccessible::HypertextNLinksChanged: - case QAccessible::ObjectAttributeChanged: - case QAccessible::PageChanged: - case QAccessible::SectionChanged: - case QAccessible::TableCaptionChanged: - case QAccessible::TableColumnDescriptionChanged: - case QAccessible::TableColumnHeaderChanged: - case QAccessible::TableRowDescriptionChanged: - case QAccessible::TableRowHeaderChanged: - case QAccessible::TableSummaryChanged: - case QAccessible::TextAttributeChanged: - case QAccessible::TextColumnChanged: - case QAccessible::VisibleDataChanged: - case QAccessible::SelectionWithin: - case QAccessible::LocationChanged: - case QAccessible::HelpChanged: - case QAccessible::DefaultActionChanged: - case QAccessible::AcceleratorChanged: - case QAccessible::InvalidEvent: - break; - } -} - -void AtSpiAdaptor::sendFocusChanged(QAccessibleInterface *interface) const -{ - static QString lastFocusPath; - // "remove" old focus - if (!lastFocusPath.isEmpty()) { - QVariantList stateArgs = packDBusSignalArguments(QLatin1String("focused"), 0, 0, variantForPath(lastFocusPath)); - sendDBusSignal(lastFocusPath, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT), - QLatin1String("StateChanged"), stateArgs); - } - // send new focus - { - QString path = pathForInterface(interface); - - QVariantList stateArgs = packDBusSignalArguments(QLatin1String("focused"), 1, 0, variantForPath(path)); - sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT), - QLatin1String("StateChanged"), stateArgs); - - QVariantList focusArgs = packDBusSignalArguments(QString(), 0, 0, variantForPath(path)); - sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_FOCUS), - QLatin1String("Focus"), focusArgs); - lastFocusPath = path; - } -} - -void AtSpiAdaptor::childrenChanged(QAccessibleInterface *interface) const -{ - QString parentPath = pathForInterface(interface); - int childCount = interface->childCount(); - for (int i = 0; i < interface->childCount(); ++i) { - QString childPath = pathForInterface(interface->child(i)); - QVariantList args = packDBusSignalArguments(QLatin1String("add"), childCount, 0, childPath); - sendDBusSignal(parentPath, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT), QLatin1String("ChildrenChanged"), args); - } -} - -void AtSpiAdaptor::notifyAboutCreation(QAccessibleInterface *interface) const -{ -// // say hello to d-bus -// cache->emitAddAccessible(accessible->getCacheItem()); - - // notify about the new child of our parent - QAccessibleInterface * parent = interface->parent(); - if (!parent) { - qCDebug(lcAccessibilityAtspi) << "AtSpiAdaptor::notifyAboutCreation: Could not find parent for " << interface->object(); - return; - } - QString path = pathForInterface(interface); - int childCount = parent->childCount(); - QString parentPath = pathForInterface(parent); - QVariantList args = packDBusSignalArguments(QLatin1String("add"), childCount, 0, variantForPath(path)); - sendDBusSignal(parentPath, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT), QLatin1String("ChildrenChanged"), args); -} - -void AtSpiAdaptor::notifyAboutDestruction(QAccessibleInterface *interface) const -{ - if (!interface || !interface->isValid()) - return; - - QAccessibleInterface * parent = interface->parent(); - if (!parent) { - qCDebug(lcAccessibilityAtspi) << "AtSpiAdaptor::notifyAboutDestruction: Could not find parent for " << interface->object(); - return; - } - QString path = pathForInterface(interface); - - // this is in the destructor. we have no clue which child we used to be. - // FIXME - int childIndex = -1; - // if (child) { - // childIndex = child; - // } else { - // childIndex = parent->indexOfChild(interface); - // } - - QString parentPath = pathForInterface(parent); - QVariantList args = packDBusSignalArguments(QLatin1String("remove"), childIndex, 0, variantForPath(path)); - sendDBusSignal(parentPath, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT), QLatin1String("ChildrenChanged"), args); -} - -/*! - Handle incoming DBus message. - This function dispatches the dbus message to the right interface handler. - */ -bool AtSpiAdaptor::handleMessage(const QDBusMessage &message, const QDBusConnection &connection) -{ - // get accessible interface - QAccessibleInterface * accessible = interfaceFromPath(message.path()); - if (!accessible) { - qCDebug(lcAccessibilityAtspi) << "WARNING Qt AtSpiAdaptor: Could not find accessible on path: " << message.path(); - return false; - } - if (!accessible->isValid()) { - qCWarning(lcAccessibilityAtspi) << "WARNING Qt AtSpiAdaptor: Accessible invalid: " << accessible << message.path(); - return false; - } - - QString interface = message.interface(); - QString function = message.member(); - - // qCDebug(lcAccessibilityAtspi) << "AtSpiAdaptor::handleMessage: " << interface << function; - - if (function == QLatin1String("Introspect")) { - //introspect(message.path()); - return false; - } - - // handle properties like regular functions - if (interface == QLatin1String("org.freedesktop.DBus.Properties")) { - interface = message.arguments().at(0).toString(); - // Get/Set + Name - function = message.member() + message.arguments().at(1).toString(); - } - - // switch interface to call - if (interface == QLatin1String(ATSPI_DBUS_INTERFACE_ACCESSIBLE)) - return accessibleInterface(accessible, function, message, connection); - if (interface == QLatin1String(ATSPI_DBUS_INTERFACE_APPLICATION)) - return applicationInterface(accessible, function, message, connection); - if (interface == QLatin1String(ATSPI_DBUS_INTERFACE_COMPONENT)) - return componentInterface(accessible, function, message, connection); - if (interface == QLatin1String(ATSPI_DBUS_INTERFACE_ACTION)) - return actionInterface(accessible, function, message, connection); - if (interface == QLatin1String(ATSPI_DBUS_INTERFACE_TEXT)) - return textInterface(accessible, function, message, connection); - if (interface == QLatin1String(ATSPI_DBUS_INTERFACE_EDITABLE_TEXT)) - return editableTextInterface(accessible, function, message, connection); - if (interface == QLatin1String(ATSPI_DBUS_INTERFACE_VALUE)) - return valueInterface(accessible, function, message, connection); - if (interface == QLatin1String(ATSPI_DBUS_INTERFACE_TABLE)) - return tableInterface(accessible, function, message, connection); - - qCDebug(lcAccessibilityAtspi) << "AtSpiAdaptor::handleMessage with unknown interface: " << message.path() << interface << function; - return false; -} - -// Application -bool AtSpiAdaptor::applicationInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection) -{ - if (message.path() != QLatin1String(ATSPI_DBUS_PATH_ROOT)) { - qCDebug(lcAccessibilityAtspi) << "WARNING Qt AtSpiAdaptor: Could not find application interface for: " << message.path() << interface; - return false; - } - - if (function == QLatin1String("SetId")) { - Q_ASSERT(message.signature() == QLatin1String("ssv")); - QVariant value = qvariant_cast(message.arguments().at(2)).variant(); - - m_applicationId = value.toInt(); - return true; - } - if (function == QLatin1String("GetId")) { - Q_ASSERT(message.signature() == QLatin1String("ss")); - QDBusMessage reply = message.createReply(QVariant::fromValue(QDBusVariant(m_applicationId))); - return connection.send(reply); - } - if (function == QLatin1String("GetToolkitName")) { - Q_ASSERT(message.signature() == QLatin1String("ss")); - QDBusMessage reply = message.createReply(QVariant::fromValue(QDBusVariant(QLatin1String("Qt")))); - return connection.send(reply); - } - if (function == QLatin1String("GetVersion")) { - Q_ASSERT(message.signature() == QLatin1String("ss")); - QDBusMessage reply = message.createReply(QVariant::fromValue(QDBusVariant(QLatin1String(qVersion())))); - return connection.send(reply); - } - if (function == QLatin1String("GetLocale")) { - Q_ASSERT(message.signature() == QLatin1String("u")); - QDBusMessage reply = message.createReply(QVariant::fromValue(QLocale().name())); - return connection.send(reply); - } - qCDebug(lcAccessibilityAtspi) << "AtSpiAdaptor::applicationInterface " << message.path() << interface << function; - return false; -} - -/*! - Register this application as accessible on the accessibility DBus. - */ -void AtSpiAdaptor::registerApplication() -{ - OrgA11yAtspiSocketInterface *registry; - registry = new OrgA11yAtspiSocketInterface(QLatin1String(QSPI_REGISTRY_NAME), - QLatin1String(QSPI_OBJECT_PATH_ROOT), m_dbus->connection()); - - QDBusPendingReply reply; - QSpiObjectReference ref = QSpiObjectReference(m_dbus->connection(), QDBusObjectPath(QSPI_OBJECT_PATH_ROOT)); - reply = registry->Embed(ref); - reply.waitForFinished(); // TODO: make this async - if (reply.isValid ()) { - const QSpiObjectReference &socket = reply.value(); - accessibilityRegistry = QSpiObjectReference(socket); - } else { - qCDebug(lcAccessibilityAtspi) << "Error in contacting registry: " - << reply.error().name() - << reply.error().message(); - } - delete registry; -} - -// Accessible -bool AtSpiAdaptor::accessibleInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection) -{ - if (function == QLatin1String("GetRole")) { - sendReply(connection, message, (uint) getRole(interface)); - } else if (function == QLatin1String("GetName")) { - sendReply(connection, message, QVariant::fromValue(QDBusVariant(interface->text(QAccessible::Name)))); - } else if (function == QLatin1String("GetRoleName")) { - sendReply(connection, message, qSpiRoleMapping[interface->role()].name()); - } else if (function == QLatin1String("GetLocalizedRoleName")) { - sendReply(connection, message, QVariant::fromValue(qSpiRoleMapping[interface->role()].localizedName())); - } else if (function == QLatin1String("GetChildCount")) { - sendReply(connection, message, QVariant::fromValue(QDBusVariant(interface->childCount()))); - } else if (function == QLatin1String("GetIndexInParent")) { - int childIndex = -1; - QAccessibleInterface * parent = interface->parent(); - if (parent) { - childIndex = parent->indexOfChild(interface); - if (childIndex < 0) { - qCDebug(lcAccessibilityAtspi) << "GetIndexInParent get invalid index: " << childIndex << interface; - } - } - sendReply(connection, message, childIndex); - } else if (function == QLatin1String("GetParent")) { - QString path; - QAccessibleInterface * parent = interface->parent(); - if (!parent) { - path = QLatin1String(ATSPI_DBUS_PATH_NULL); - } else if (parent->role() == QAccessible::Application) { - path = QLatin1String(ATSPI_DBUS_PATH_ROOT); - } else { - path = pathForInterface(parent); - } - // Parent is a property, so it needs to be wrapped inside an extra variant. - sendReply(connection, message, QVariant::fromValue( - QDBusVariant(QVariant::fromValue(QSpiObjectReference(connection, QDBusObjectPath(path)))))); - } else if (function == QLatin1String("GetChildAtIndex")) { - const int index = message.arguments().at(0).toInt(); - if (index < 0) { - sendReply(connection, message, QVariant::fromValue( - QSpiObjectReference(connection, QDBusObjectPath(ATSPI_DBUS_PATH_NULL)))); - } else { - QAccessibleInterface * childInterface = interface->child(index); - sendReply(connection, message, QVariant::fromValue( - QSpiObjectReference(connection, QDBusObjectPath(pathForInterface(childInterface))))); - } - } else if (function == QLatin1String("GetInterfaces")) { - sendReply(connection, message, accessibleInterfaces(interface)); - } else if (function == QLatin1String("GetDescription")) { - sendReply(connection, message, QVariant::fromValue(QDBusVariant(interface->text(QAccessible::Description)))); - } else if (function == QLatin1String("GetState")) { - quint64 spiState = spiStatesFromQState(interface->state()); - if (interface->tableInterface()) { - setSpiStateBit(&spiState, ATSPI_STATE_MANAGES_DESCENDANTS); - } - QAccessible::Role role = interface->role(); - if (role == QAccessible::TreeItem || - role == QAccessible::ListItem) { - /* Transient means libatspi2 will not cache items. - This is important because when adding/removing an item - the cache becomes outdated and we don't change the paths of - items in lists/trees/tables. */ - setSpiStateBit(&spiState, ATSPI_STATE_TRANSIENT); - } - sendReply(connection, message, - QVariant::fromValue(spiStateSetFromSpiStates(spiState))); - } else if (function == QLatin1String("GetAttributes")) { - sendReply(connection, message, QVariant::fromValue(QSpiAttributeSet())); - } else if (function == QLatin1String("GetRelationSet")) { - sendReply(connection, message, QVariant::fromValue(relationSet(interface, connection))); - } else if (function == QLatin1String("GetApplication")) { - sendReply(connection, message, QVariant::fromValue( - QSpiObjectReference(connection, QDBusObjectPath(QSPI_OBJECT_PATH_ROOT)))); - } else if (function == QLatin1String("GetChildren")) { - QSpiObjectReferenceArray children; - const int numChildren = interface->childCount(); - children.reserve(numChildren); - for (int i = 0; i < numChildren; ++i) { - QString childPath = pathForInterface(interface->child(i)); - QSpiObjectReference ref(connection, QDBusObjectPath(childPath)); - children << ref; - } - connection.send(message.createReply(QVariant::fromValue(children))); - } else { - qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::accessibleInterface does not implement " << function << message.path(); - return false; - } - return true; -} - -AtspiRole AtSpiAdaptor::getRole(QAccessibleInterface *interface) const -{ - if ((interface->role() == QAccessible::EditableText) && interface->state().passwordEdit) - return ATSPI_ROLE_PASSWORD_TEXT; - return qSpiRoleMapping[interface->role()].spiRole(); -} - -QStringList AtSpiAdaptor::accessibleInterfaces(QAccessibleInterface *interface) const -{ - QStringList ifaces; - qCDebug(lcAccessibilityAtspiCreation) << "AtSpiAdaptor::accessibleInterfaces create: " << interface->object(); - ifaces << QLatin1String(ATSPI_DBUS_INTERFACE_ACCESSIBLE); - - if ( (!interface->rect().isEmpty()) || - (interface->object() && interface->object()->isWidgetType()) || - (interface->role() == QAccessible::ListItem) || - (interface->role() == QAccessible::Cell) || - (interface->role() == QAccessible::TreeItem) || - (interface->role() == QAccessible::Row) || - (interface->object() && interface->object()->inherits("QSGItem")) - ) { - ifaces << QLatin1String(ATSPI_DBUS_INTERFACE_COMPONENT); - } else { - qCDebug(lcAccessibilityAtspiCreation) << " IS NOT a component"; - } - if (interface->role() == QAccessible::Application) - ifaces << QLatin1String(ATSPI_DBUS_INTERFACE_APPLICATION); - - if (interface->actionInterface() || interface->valueInterface()) - ifaces << QLatin1String(ATSPI_DBUS_INTERFACE_ACTION); - - if (interface->textInterface()) - ifaces << QLatin1String(ATSPI_DBUS_INTERFACE_TEXT); - - if (interface->editableTextInterface()) - ifaces << QLatin1String(ATSPI_DBUS_INTERFACE_EDITABLE_TEXT); - - if (interface->valueInterface()) - ifaces << QLatin1String(ATSPI_DBUS_INTERFACE_VALUE); - - if (interface->tableInterface()) - ifaces << QLatin1String(ATSPI_DBUS_INTERFACE_TABLE); - - return ifaces; -} - -QSpiRelationArray AtSpiAdaptor::relationSet(QAccessibleInterface *interface, const QDBusConnection &connection) const -{ - typedef QPair RelationPair; - const QVector relationInterfaces = interface->relations(); - - QSpiRelationArray relations; - for (const RelationPair &pair : relationInterfaces) { -// FIXME: this loop seems a bit strange... "related" always have one item when we check. -//And why is it a list, when it always have one item? And it seems to assume that the QAccessible::Relation enum maps directly to AtSpi - QSpiObjectReferenceArray related; - - QDBusObjectPath path = QDBusObjectPath(pathForInterface(pair.first)); - related.append(QSpiObjectReference(connection, path)); - - if (!related.isEmpty()) - relations.append(QSpiRelationArrayEntry(qAccessibleRelationToAtSpiRelation(pair.second), related)); - } - return relations; -} - -void AtSpiAdaptor::sendReply(const QDBusConnection &connection, const QDBusMessage &message, const QVariant &argument) const -{ - QDBusMessage reply = message.createReply(argument); - connection.send(reply); -} - - -QString AtSpiAdaptor::pathForObject(QObject *object) const -{ - Q_ASSERT(object); - - if (inheritsQAction(object)) { - qCDebug(lcAccessibilityAtspi) << "AtSpiAdaptor::pathForObject: warning: creating path with QAction as object."; - } - - QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(object); - return pathForInterface(iface); -} - -QString AtSpiAdaptor::pathForInterface(QAccessibleInterface *interface) const -{ - if (!interface || !interface->isValid()) - return QLatin1String(ATSPI_DBUS_PATH_NULL); - if (interface->role() == QAccessible::Application) - return QLatin1String(QSPI_OBJECT_PATH_ROOT); - - QAccessible::Id id = QAccessible::uniqueId(interface); - Q_ASSERT((int)id < 0); - return QLatin1String(QSPI_OBJECT_PATH_PREFIX) + QString::number(id); -} - -bool AtSpiAdaptor::inheritsQAction(QObject *object) -{ - const QMetaObject *mo = object->metaObject(); - while (mo) { - const QLatin1String cn(mo->className()); - if (cn == QLatin1String("QAction")) - return true; - mo = mo->superClass(); - } - return false; -} - -// Component -static QAccessibleInterface * getWindow(QAccessibleInterface * interface) -{ - if (interface->role() == QAccessible::Window) - return interface; - - QAccessibleInterface * parent = interface->parent(); - while (parent && parent->role() != QAccessible::Window) - parent = parent->parent(); - - return parent; -} - -static QRect getRelativeRect(QAccessibleInterface *interface) -{ - QAccessibleInterface * window; - QRect wr, cr; - - cr = interface->rect(); - - window = getWindow(interface); - if (window) { - wr = window->rect(); - - cr.setX(cr.x() - wr.x()); - cr.setY(cr.x() - wr.y()); - } - return cr; -} - -bool AtSpiAdaptor::componentInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection) -{ - if (function == QLatin1String("Contains")) { - bool ret = false; - int x = message.arguments().at(0).toInt(); - int y = message.arguments().at(1).toInt(); - uint coordType = message.arguments().at(2).toUInt(); - if (coordType == ATSPI_COORD_TYPE_SCREEN) - ret = interface->rect().contains(x, y); - else - ret = getRelativeRect(interface).contains(x, y); - sendReply(connection, message, ret); - } else if (function == QLatin1String("GetAccessibleAtPoint")) { - int x = message.arguments().at(0).toInt(); - int y = message.arguments().at(1).toInt(); - uint coordType = message.arguments().at(2).toUInt(); - if (coordType == ATSPI_COORD_TYPE_WINDOW) { - QWindow * window = interface->window(); - if (window) { - x += window->position().x(); - y += window->position().y(); - } - } - - QAccessibleInterface * childInterface(interface->childAt(x, y)); - QAccessibleInterface * iface = 0; - while (childInterface) { - iface = childInterface; - childInterface = iface->childAt(x, y); - } - if (iface) { - QString path = pathForInterface(iface); - sendReply(connection, message, QVariant::fromValue( - QSpiObjectReference(connection, QDBusObjectPath(path)))); - } else { - sendReply(connection, message, QVariant::fromValue( - QSpiObjectReference(connection, QDBusObjectPath(ATSPI_DBUS_PATH_NULL)))); - } - } else if (function == QLatin1String("GetAlpha")) { - sendReply(connection, message, (double) 1.0); - } else if (function == QLatin1String("GetExtents")) { - uint coordType = message.arguments().at(0).toUInt(); - sendReply(connection, message, QVariant::fromValue(getExtents(interface, coordType))); - } else if (function == QLatin1String("GetLayer")) { - sendReply(connection, message, QVariant::fromValue((uint)1)); - } else if (function == QLatin1String("GetMDIZOrder")) { - sendReply(connection, message, QVariant::fromValue((short)0)); - } else if (function == QLatin1String("GetPosition")) { - uint coordType = message.arguments().at(0).toUInt(); - QRect rect; - if (coordType == ATSPI_COORD_TYPE_SCREEN) - rect = interface->rect(); - else - rect = getRelativeRect(interface); - QVariantList pos; - pos << rect.x() << rect.y(); - connection.send(message.createReply(pos)); - } else if (function == QLatin1String("GetSize")) { - QRect rect = interface->rect(); - QVariantList size; - size << rect.width() << rect.height(); - connection.send(message.createReply(size)); - } else if (function == QLatin1String("GrabFocus")) { - QAccessibleActionInterface *actionIface = interface->actionInterface(); - if (actionIface && actionIface->actionNames().contains(QAccessibleActionInterface::setFocusAction())) { - actionIface->doAction(QAccessibleActionInterface::setFocusAction()); - sendReply(connection, message, true); - } else { - sendReply(connection, message, false); - } - } else if (function == QLatin1String("SetExtents")) { -// int x = message.arguments().at(0).toInt(); -// int y = message.arguments().at(1).toInt(); -// int width = message.arguments().at(2).toInt(); -// int height = message.arguments().at(3).toInt(); -// uint coordinateType = message.arguments().at(4).toUInt(); - qCDebug(lcAccessibilityAtspi) << "SetExtents is not implemented."; - sendReply(connection, message, false); - } else if (function == QLatin1String("SetPosition")) { -// int x = message.arguments().at(0).toInt(); -// int y = message.arguments().at(1).toInt(); -// uint coordinateType = message.arguments().at(2).toUInt(); - qCDebug(lcAccessibilityAtspi) << "SetPosition is not implemented."; - sendReply(connection, message, false); - } else if (function == QLatin1String("SetSize")) { -// int width = message.arguments().at(0).toInt(); -// int height = message.arguments().at(1).toInt(); - qCDebug(lcAccessibilityAtspi) << "SetSize is not implemented."; - sendReply(connection, message, false); - } else { - qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::componentInterface does not implement " << function << message.path(); - return false; - } - return true; -} - -QRect AtSpiAdaptor::getExtents(QAccessibleInterface *interface, uint coordType) -{ - return (coordType == ATSPI_COORD_TYPE_SCREEN) ? interface->rect() : getRelativeRect(interface); -} - -// Action interface -bool AtSpiAdaptor::actionInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection) -{ - if (function == QLatin1String("GetNActions")) { - int count = QAccessibleBridgeUtils::effectiveActionNames(interface).count(); - sendReply(connection, message, QVariant::fromValue(QDBusVariant(QVariant::fromValue(count)))); - } else if (function == QLatin1String("DoAction")) { - int index = message.arguments().at(0).toInt(); - const QStringList actionNames = QAccessibleBridgeUtils::effectiveActionNames(interface); - if (index < 0 || index >= actionNames.count()) - return false; - const QString actionName = actionNames.at(index); - bool success = QAccessibleBridgeUtils::performEffectiveAction(interface, actionName); - sendReply(connection, message, success); - } else if (function == QLatin1String("GetActions")) { - sendReply(connection, message, QVariant::fromValue(getActions(interface))); - } else if (function == QLatin1String("GetName")) { - int index = message.arguments().at(0).toInt(); - const QStringList actionNames = QAccessibleBridgeUtils::effectiveActionNames(interface); - if (index < 0 || index >= actionNames.count()) - return false; - sendReply(connection, message, actionNames.at(index)); - } else if (function == QLatin1String("GetDescription")) { - int index = message.arguments().at(0).toInt(); - const QStringList actionNames = QAccessibleBridgeUtils::effectiveActionNames(interface); - if (index < 0 || index >= actionNames.count()) - return false; - QString description; - if (QAccessibleActionInterface *actionIface = interface->actionInterface()) - description = actionIface->localizedActionDescription(actionNames.at(index)); - else - description = qAccessibleLocalizedActionDescription(actionNames.at(index)); - sendReply(connection, message, description); - } else if (function == QLatin1String("GetKeyBinding")) { - int index = message.arguments().at(0).toInt(); - const QStringList actionNames = QAccessibleBridgeUtils::effectiveActionNames(interface); - if (index < 0 || index >= actionNames.count()) - return false; - QStringList keyBindings; - if (QAccessibleActionInterface *actionIface = interface->actionInterface()) - keyBindings = actionIface->keyBindingsForAction(actionNames.at(index)); - if (keyBindings.isEmpty()) { - QString acc = interface->text(QAccessible::Accelerator); - if (!acc.isEmpty()) - keyBindings.append(acc); - } - if (keyBindings.length() > 0) - sendReply(connection, message, keyBindings.join(QLatin1Char(';'))); - else - sendReply(connection, message, QString()); - } else { - qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::actionInterface does not implement " << function << message.path(); - return false; - } - return true; -} - -QSpiActionArray AtSpiAdaptor::getActions(QAccessibleInterface *interface) const -{ - QAccessibleActionInterface *actionInterface = interface->actionInterface(); - QSpiActionArray actions; - const QStringList actionNames = QAccessibleBridgeUtils::effectiveActionNames(interface); - actions.reserve(actionNames.size()); - for (const QString &actionName : actionNames) { - QSpiAction action; - - action.name = actionName; - if (actionInterface) { - action.description = actionInterface->localizedActionDescription(actionName); - const QStringList keyBindings = actionInterface->keyBindingsForAction(actionName); - if (!keyBindings.isEmpty()) - action.keyBinding = keyBindings.front(); - } else { - action.description = qAccessibleLocalizedActionDescription(actionName); - } - - actions.append(std::move(action)); - } - return actions; -} - -// Text interface -bool AtSpiAdaptor::textInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection) -{ - if (!interface->textInterface()) - return false; - - // properties - if (function == QLatin1String("GetCaretOffset")) { - sendReply(connection, message, QVariant::fromValue(QDBusVariant(QVariant::fromValue(interface->textInterface()->cursorPosition())))); - } else if (function == QLatin1String("GetCharacterCount")) { - sendReply(connection, message, QVariant::fromValue(QDBusVariant(QVariant::fromValue(interface->textInterface()->characterCount())))); - - // functions - } else if (function == QLatin1String("AddSelection")) { - int startOffset = message.arguments().at(0).toInt(); - int endOffset = message.arguments().at(1).toInt(); - int lastSelection = interface->textInterface()->selectionCount(); - interface->textInterface()->setSelection(lastSelection, startOffset, endOffset); - sendReply(connection, message, (interface->textInterface()->selectionCount() > lastSelection)); - } else if (function == QLatin1String("GetAttributeRun")) { - int offset = message.arguments().at(0).toInt(); - bool includeDefaults = message.arguments().at(1).toBool(); - Q_UNUSED(includeDefaults) - connection.send(message.createReply(getAttributes(interface, offset, includeDefaults))); - } else if (function == QLatin1String("GetAttributeValue")) { - int offset = message.arguments().at(0).toInt(); - QString attributeName = message.arguments().at(1).toString(); - connection.send(message.createReply(getAttributeValue(interface, offset, attributeName))); - } else if (function == QLatin1String("GetAttributes")) { - int offset = message.arguments().at(0).toInt(); - connection.send(message.createReply(getAttributes(interface, offset, true))); - } else if (function == QLatin1String("GetBoundedRanges")) { - int x = message.arguments().at(0).toInt(); - int y = message.arguments().at(1).toInt(); - int width = message.arguments().at(2).toInt(); - int height = message.arguments().at(3).toInt(); - uint coordType = message.arguments().at(4).toUInt(); - uint xClipType = message.arguments().at(5).toUInt(); - uint yClipType = message.arguments().at(6).toUInt(); - Q_UNUSED(x); - Q_UNUSED(y); - Q_UNUSED(width); - Q_UNUSED(height); - Q_UNUSED(coordType); - Q_UNUSED(xClipType); - Q_UNUSED(yClipType); - qCDebug(lcAccessibilityAtspi) << "Not implemented: QSpiAdaptor::GetBoundedRanges"; - sendReply(connection, message, QVariant::fromValue(QSpiTextRangeList())); - } else if (function == QLatin1String("GetCharacterAtOffset")) { - int offset = message.arguments().at(0).toInt(); - int start; - int end; - QString result = interface->textInterface()->textAtOffset(offset, QAccessible::CharBoundary, &start, &end); - sendReply(connection, message, (int) *(qPrintable (result))); - } else if (function == QLatin1String("GetCharacterExtents")) { - int offset = message.arguments().at(0).toInt(); - int coordType = message.arguments().at(1).toUInt(); - connection.send(message.createReply(getCharacterExtents(interface, offset, coordType))); - } else if (function == QLatin1String("GetDefaultAttributeSet") || function == QLatin1String("GetDefaultAttributes")) { - // GetDefaultAttributes is deprecated in favour of GetDefaultAttributeSet. - // Empty set seems reasonable. There is no default attribute set. - sendReply(connection, message, QVariant::fromValue(QSpiAttributeSet())); - } else if (function == QLatin1String("GetNSelections")) { - sendReply(connection, message, interface->textInterface()->selectionCount()); - } else if (function == QLatin1String("GetOffsetAtPoint")) { - qCDebug(lcAccessibilityAtspi) << message.signature(); - Q_ASSERT(!message.signature().isEmpty()); - QPoint point(message.arguments().at(0).toInt(), message.arguments().at(1).toInt()); - uint coordType = message.arguments().at(2).toUInt(); - if (coordType == ATSPI_COORD_TYPE_WINDOW) { - QWindow *win = interface->window(); - point -= QPoint(win->x(), win->y()); - } - int offset = interface->textInterface()->offsetAtPoint(point); - sendReply(connection, message, offset); - } else if (function == QLatin1String("GetRangeExtents")) { - int startOffset = message.arguments().at(0).toInt(); - int endOffset = message.arguments().at(1).toInt(); - uint coordType = message.arguments().at(2).toUInt(); - connection.send(message.createReply(getRangeExtents(interface, startOffset, endOffset, coordType))); - } else if (function == QLatin1String("GetSelection")) { - int selectionNum = message.arguments().at(0).toInt(); - int start, end; - interface->textInterface()->selection(selectionNum, &start, &end); - if (start < 0) - start = end = interface->textInterface()->cursorPosition(); - QVariantList sel; - sel << start << end; - connection.send(message.createReply(sel)); - } else if (function == QLatin1String("GetText")) { - int startOffset = message.arguments().at(0).toInt(); - int endOffset = message.arguments().at(1).toInt(); - if (endOffset == -1) // AT-SPI uses -1 to signal all characters - endOffset = interface->textInterface()->characterCount(); - sendReply(connection, message, interface->textInterface()->text(startOffset, endOffset)); - } else if (function == QLatin1String("GetTextAfterOffset")) { - int offset = message.arguments().at(0).toInt(); - int type = message.arguments().at(1).toUInt(); - int startOffset, endOffset; - QString text = interface->textInterface()->textAfterOffset(offset, qAccessibleBoundaryType(type), &startOffset, &endOffset); - QVariantList ret; - ret << text << startOffset << endOffset; - connection.send(message.createReply(ret)); - } else if (function == QLatin1String("GetTextAtOffset")) { - int offset = message.arguments().at(0).toInt(); - int type = message.arguments().at(1).toUInt(); - int startOffset, endOffset; - QString text = interface->textInterface()->textAtOffset(offset, qAccessibleBoundaryType(type), &startOffset, &endOffset); - QVariantList ret; - ret << text << startOffset << endOffset; - connection.send(message.createReply(ret)); - } else if (function == QLatin1String("GetTextBeforeOffset")) { - int offset = message.arguments().at(0).toInt(); - int type = message.arguments().at(1).toUInt(); - int startOffset, endOffset; - QString text = interface->textInterface()->textBeforeOffset(offset, qAccessibleBoundaryType(type), &startOffset, &endOffset); - QVariantList ret; - ret << text << startOffset << endOffset; - connection.send(message.createReply(ret)); - } else if (function == QLatin1String("RemoveSelection")) { - int selectionNum = message.arguments().at(0).toInt(); - interface->textInterface()->removeSelection(selectionNum); - sendReply(connection, message, true); - } else if (function == QLatin1String("SetCaretOffset")) { - int offset = message.arguments().at(0).toInt(); - interface->textInterface()->setCursorPosition(offset); - sendReply(connection, message, true); - } else if (function == QLatin1String("SetSelection")) { - int selectionNum = message.arguments().at(0).toInt(); - int startOffset = message.arguments().at(1).toInt(); - int endOffset = message.arguments().at(2).toInt(); - interface->textInterface()->setSelection(selectionNum, startOffset, endOffset); - sendReply(connection, message, true); - } else { - qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::textInterface does not implement " << function << message.path(); - return false; - } - return true; -} - -QAccessible::TextBoundaryType AtSpiAdaptor::qAccessibleBoundaryType(int atspiTextBoundaryType) const -{ - switch (atspiTextBoundaryType) { - case ATSPI_TEXT_BOUNDARY_CHAR: - return QAccessible::CharBoundary; - case ATSPI_TEXT_BOUNDARY_WORD_START: - case ATSPI_TEXT_BOUNDARY_WORD_END: - return QAccessible::WordBoundary; - case ATSPI_TEXT_BOUNDARY_SENTENCE_START: - case ATSPI_TEXT_BOUNDARY_SENTENCE_END: - return QAccessible::SentenceBoundary; - case ATSPI_TEXT_BOUNDARY_LINE_START: - case ATSPI_TEXT_BOUNDARY_LINE_END: - return QAccessible::LineBoundary; - } - Q_ASSERT_X(0, "", "Requested invalid boundary type."); - return QAccessible::CharBoundary; -} - -namespace -{ - struct AtSpiAttribute { - QString name; - QString value; - AtSpiAttribute(const QString &aName, const QString &aValue) : name(aName), value(aValue) {} - bool isNull() const { return name.isNull() || value.isNull(); } - }; - - QString atspiColor(const QString &ia2Color) - { - // "rgb(%u,%u,%u)" -> "%u,%u,%u" - return ia2Color.mid(4, ia2Color.length() - (4+1)); - } - - QString atspiSize(const QString &ia2Size) - { - // "%fpt" -> "%f" - return ia2Size.left(ia2Size.length() - 2); - } - - AtSpiAttribute atspiTextAttribute(const QString &ia2Name, const QString &ia2Value) - { - QString name = ia2Name; - QString value = ia2Value; - - // IAccessible2: http://www.linuxfoundation.org/collaborate/workgroups/accessibility/iaccessible2/textattributes - // ATK attribute names: https://git.gnome.org/browse/orca/tree/src/orca/text_attribute_names.py - // ATK attribute values: https://developer.gnome.org/atk/unstable/AtkText.html#AtkTextAttribute - - // https://bugzilla.gnome.org/show_bug.cgi?id=744553 "ATK docs provide no guidance for allowed values of some text attributes" - // specifically for "weight", "invalid", "language" and value range for colors - - if (ia2Name == QLatin1String("background-color")) { - name = QStringLiteral("bg-color"); - value = atspiColor(value); - } else if (ia2Name == QLatin1String("font-family")) { - name = QStringLiteral("family-name"); - } else if (ia2Name == QLatin1String("color")) { - name = QStringLiteral("fg-color"); - value = atspiColor(value); - } else if (ia2Name == QLatin1String("text-align")) { - name = QStringLiteral("justification"); - if (value == QLatin1String("justify")) { - value = QStringLiteral("fill"); - } else { - if (value != QLatin1String("left") && - value != QLatin1String("right") && - value != QLatin1String("center") - ) { - value = QString(); - qCDebug(lcAccessibilityAtspi) << "Unknown text-align attribute value \"" << value << "\" cannot be translated to AT-SPI."; - } - } - } else if (ia2Name == QLatin1String("font-size")) { - name = QStringLiteral("size"); - value = atspiSize(value); - } else if (ia2Name == QLatin1String("font-style")) { - name = QStringLiteral("style"); - if (value != QLatin1String("normal") && - value != QLatin1String("italic") && - value != QLatin1String("oblique") - ) { - value = QString(); - qCDebug(lcAccessibilityAtspi) << "Unknown font-style attribute value \"" << value << "\" cannot be translated to AT-SPI."; - } - } else if (ia2Name == QLatin1String("text-underline-type")) { - name = QStringLiteral("underline"); - if (value != QLatin1String("none") && - value != QLatin1String("single") && - value != QLatin1String("double") - ) { - value = QString(); - qCDebug(lcAccessibilityAtspi) << "Unknown text-underline-type attribute value \"" << value << "\" cannot be translated to AT-SPI."; - } - } else if (ia2Name == QLatin1String("font-weight")) { - name = QStringLiteral("weight"); - if (value == QLatin1String("normal")) - // Orca seems to accept all IAccessible2 values except for "normal" - // (on which it produces traceback and fails to read any following text attributes), - // but that is the default value, so omit it anyway - value = QString(); - } else if (ia2Name == QLatin1String("text-position")) { - name = QStringLiteral("vertical-align"); - if (value != QLatin1String("baseline") && - value != QLatin1String("super") && - value != QLatin1String("sub") - ) { - value = QString(); - qCDebug(lcAccessibilityAtspi) << "Unknown text-position attribute value \"" << value << "\" cannot be translated to AT-SPI."; - } - } else if (ia2Name == QLatin1String("writing-mode")) { - name = QStringLiteral("direction"); - if (value == QLatin1String("lr")) - value = QStringLiteral("ltr"); - else if (value == QLatin1String("rl")) - value = QStringLiteral("rtl"); - else if (value == QLatin1String("tb")) { - // IAccessible2 docs refer to XSL, which specifies "tb" is shorthand for "tb-rl"; so at least give a hint about the horizontal direction (ATK does not support vertical direction in this attribute (yet)) - value = QStringLiteral("rtl"); - qCDebug(lcAccessibilityAtspi) << "writing-mode attribute value \"tb\" translated only w.r.t. horizontal direction; vertical direction ignored"; - } else { - value = QString(); - qCDebug(lcAccessibilityAtspi) << "Unknown writing-mode attribute value \"" << value << "\" cannot be translated to AT-SPI."; - } - } else if (ia2Name == QLatin1String("language")) { - // OK - ATK has no docs on the format of the value, IAccessible2 has reasonable format - leave it at that now - } else if (ia2Name == QLatin1String("invalid")) { - // OK - ATK docs are vague but suggest they support the same range of values as IAccessible2 - } else { - // attribute we know nothing about - name = QString(); - value = QString(); - } - return AtSpiAttribute(name, value); - } -} - -// FIXME all attribute methods below should share code -QVariantList AtSpiAdaptor::getAttributes(QAccessibleInterface *interface, int offset, bool includeDefaults) const -{ - Q_UNUSED(includeDefaults); - - QSpiAttributeSet set; - int startOffset; - int endOffset; - - QString joined = interface->textInterface()->attributes(offset, &startOffset, &endOffset); - const QStringList attributes = joined.split (QLatin1Char(';'), Qt::SkipEmptyParts, Qt::CaseSensitive); - for (const QString &attr : attributes) { - QStringList items; - items = attr.split(QLatin1Char(':'), Qt::SkipEmptyParts, Qt::CaseSensitive); - AtSpiAttribute attribute = atspiTextAttribute(items[0], items[1]); - if (!attribute.isNull()) - set[attribute.name] = attribute.value; - } - - QVariantList list; - list << QVariant::fromValue(set) << startOffset << endOffset; - - return list; -} - -QVariantList AtSpiAdaptor::getAttributeValue(QAccessibleInterface *interface, int offset, const QString &attributeName) const -{ - QString mapped; - QString joined; - QSpiAttributeSet map; - int startOffset; - int endOffset; - - joined = interface->textInterface()->attributes(offset, &startOffset, &endOffset); - const QStringList attributes = joined.split (QLatin1Char(';'), Qt::SkipEmptyParts, Qt::CaseSensitive); - for (const QString& attr : attributes) { - QStringList items; - items = attr.split(QLatin1Char(':'), Qt::SkipEmptyParts, Qt::CaseSensitive); - AtSpiAttribute attribute = atspiTextAttribute(items[0], items[1]); - if (!attribute.isNull()) - map[attribute.name] = attribute.value; - } - mapped = map[attributeName]; - const bool defined = !mapped.isEmpty(); - QVariantList list; - list << mapped << startOffset << endOffset << defined; - return list; -} - -QList AtSpiAdaptor::getCharacterExtents(QAccessibleInterface *interface, int offset, uint coordType) const -{ - QRect rect = interface->textInterface()->characterRect(offset); - - if (coordType == ATSPI_COORD_TYPE_WINDOW) - rect = translateRectToWindowCoordinates(interface, rect); - - return QList() << rect.x() << rect.y() << rect.width() << rect.height(); -} - -QList AtSpiAdaptor::getRangeExtents(QAccessibleInterface *interface, - int startOffset, int endOffset, uint coordType) const -{ - if (endOffset == -1) - endOffset = interface->textInterface()->characterCount(); - - QAccessibleTextInterface *textInterface = interface->textInterface(); - if (endOffset <= startOffset || !textInterface) - return QList() << -1 << -1 << 0 << 0; - - QRect rect = textInterface->characterRect(startOffset); - for (int i=startOffset + 1; i <= endOffset; i++) - rect = rect | textInterface->characterRect(i); - - // relative to window - if (coordType == ATSPI_COORD_TYPE_WINDOW) - rect = translateRectToWindowCoordinates(interface, rect); - - return QList() << rect.x() << rect.y() << rect.width() << rect.height(); -} - -QRect AtSpiAdaptor::translateRectToWindowCoordinates(QAccessibleInterface *interface, const QRect &rect) -{ - QAccessibleInterface * window = getWindow(interface); - if (window) - return rect.translated(-window->rect().x(), -window->rect().y()); - - return rect; -} - - -// Editable Text interface -static QString textForRange(QAccessibleInterface *accessible, int startOffset, int endOffset) -{ - if (QAccessibleTextInterface *textIface = accessible->textInterface()) { - if (endOffset == -1) - endOffset = textIface->characterCount(); - return textIface->text(startOffset, endOffset); - } - QString txt = accessible->text(QAccessible::Value); - if (endOffset == -1) - endOffset = txt.length(); - return txt.mid(startOffset, endOffset - startOffset); -} - -static void replaceTextFallback(QAccessibleInterface *accessible, long startOffset, long endOffset, const QString &txt) -{ - QString t = textForRange(accessible, 0, -1); - if (endOffset == -1) - endOffset = t.length(); - if (endOffset - startOffset == 0) - t.insert(startOffset, txt); - else - t.replace(startOffset, endOffset - startOffset, txt); - accessible->setText(QAccessible::Value, t); -} - -bool AtSpiAdaptor::editableTextInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection) -{ - if (function == QLatin1String("CopyText")) { -#ifndef QT_NO_CLIPBOARD - int startOffset = message.arguments().at(0).toInt(); - int endOffset = message.arguments().at(1).toInt(); - const QString t = textForRange(interface, startOffset, endOffset); - QGuiApplication::clipboard()->setText(t); -#endif - connection.send(message.createReply(true)); - } else if (function == QLatin1String("CutText")) { -#ifndef QT_NO_CLIPBOARD - int startOffset = message.arguments().at(0).toInt(); - int endOffset = message.arguments().at(1).toInt(); - const QString t = textForRange(interface, startOffset, endOffset); - if (QAccessibleEditableTextInterface *editableTextIface = interface->editableTextInterface()) - editableTextIface->deleteText(startOffset, endOffset); - else - replaceTextFallback(interface, startOffset, endOffset, QString()); - QGuiApplication::clipboard()->setText(t); -#endif - connection.send(message.createReply(true)); - } else if (function == QLatin1String("DeleteText")) { - int startOffset = message.arguments().at(0).toInt(); - int endOffset = message.arguments().at(1).toInt(); - if (QAccessibleEditableTextInterface *editableTextIface = interface->editableTextInterface()) - editableTextIface->deleteText(startOffset, endOffset); - else - replaceTextFallback(interface, startOffset, endOffset, QString()); - connection.send(message.createReply(true)); - } else if (function == QLatin1String("InsertText")) { - int position = message.arguments().at(0).toInt(); - QString text = message.arguments().at(1).toString(); - int length = message.arguments().at(2).toInt(); - text.resize(length); - if (QAccessibleEditableTextInterface *editableTextIface = interface->editableTextInterface()) - editableTextIface->insertText(position, text); - else - replaceTextFallback(interface, position, position, text); - connection.send(message.createReply(true)); - } else if (function == QLatin1String("PasteText")) { -#ifndef QT_NO_CLIPBOARD - int position = message.arguments().at(0).toInt(); - const QString txt = QGuiApplication::clipboard()->text(); - if (QAccessibleEditableTextInterface *editableTextIface = interface->editableTextInterface()) - editableTextIface->insertText(position, txt); - else - replaceTextFallback(interface, position, position, txt); -#endif - connection.send(message.createReply(true)); - } else if (function == QLatin1String("SetTextContents")) { - QString newContents = message.arguments().at(0).toString(); - if (QAccessibleEditableTextInterface *editableTextIface = interface->editableTextInterface()) - editableTextIface->replaceText(0, interface->textInterface()->characterCount(), newContents); - else - replaceTextFallback(interface, 0, -1, newContents); - connection.send(message.createReply(true)); - } else if (function == QLatin1String("")) { - connection.send(message.createReply()); - } else { - qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::editableTextInterface does not implement " << function << message.path(); - return false; - } - return true; -} - -// Value interface -bool AtSpiAdaptor::valueInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection) -{ - QAccessibleValueInterface *valueIface = interface->valueInterface(); - if (!valueIface) - return false; - - if (function == QLatin1String("SetCurrentValue")) { - QDBusVariant v = qvariant_cast(message.arguments().at(2)); - double value = v.variant().toDouble(); - //Temporary fix - //See https://bugzilla.gnome.org/show_bug.cgi?id=652596 - valueIface->setCurrentValue(value); - connection.send(message.createReply()); // FIXME is the reply needed? - } else { - QVariant value; - if (function == QLatin1String("GetCurrentValue")) - value = valueIface->currentValue(); - else if (function == QLatin1String("GetMaximumValue")) - value = valueIface->maximumValue(); - else if (function == QLatin1String("GetMinimumIncrement")) - value = valueIface->minimumStepSize(); - else if (function == QLatin1String("GetMinimumValue")) - value = valueIface->minimumValue(); - else { - qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::valueInterface does not implement " << function << message.path(); - return false; - } - if (!value.canConvert(QMetaType::Double)) { - qCDebug(lcAccessibilityAtspi) << "AtSpiAdaptor::valueInterface: Could not convert to double: " << function; - } - - // explicitly convert to dbus-variant containing one double since atspi expects that - // everything else might fail to convert back on the other end - connection.send(message.createReply( - QVariant::fromValue(QDBusVariant(QVariant::fromValue(value.toDouble()))))); - } - return true; -} - -// Table interface -bool AtSpiAdaptor::tableInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection) -{ - if (!(interface->tableInterface() || interface->tableCellInterface())) { - qCDebug(lcAccessibilityAtspi) << "WARNING Qt AtSpiAdaptor: Could not find table interface for: " << message.path() << interface; - return false; - } - - if (0) { - // properties - } else if (function == QLatin1String("GetCaption")) { - QAccessibleInterface * captionInterface= interface->tableInterface()->caption(); - if (captionInterface) { - QSpiObjectReference ref = QSpiObjectReference(connection, QDBusObjectPath(pathForInterface(captionInterface))); - sendReply(connection, message, QVariant::fromValue(ref)); - } else { - sendReply(connection, message, QVariant::fromValue( - QSpiObjectReference(connection, QDBusObjectPath(ATSPI_DBUS_PATH_NULL)))); - } - } else if (function == QLatin1String("GetNColumns")) { - connection.send(message.createReply(QVariant::fromValue(QDBusVariant( - QVariant::fromValue(interface->tableInterface()->columnCount()))))); - } else if (function == QLatin1String("GetNRows")) { - connection.send(message.createReply(QVariant::fromValue(QDBusVariant( - QVariant::fromValue(interface->tableInterface()->rowCount()))))); - } else if (function == QLatin1String("GetNSelectedColumns")) { - connection.send(message.createReply(QVariant::fromValue(QDBusVariant( - QVariant::fromValue(interface->tableInterface()->selectedColumnCount()))))); - } else if (function == QLatin1String("GetNSelectedRows")) { - connection.send(message.createReply(QVariant::fromValue(QDBusVariant( - QVariant::fromValue(interface->tableInterface()->selectedRowCount()))))); - } else if (function == QLatin1String("GetSummary")) { - QAccessibleInterface * summary = interface->tableInterface() ? interface->tableInterface()->summary() : 0; - QSpiObjectReference ref(connection, QDBusObjectPath(pathForInterface(summary))); - connection.send(message.createReply(QVariant::fromValue(QDBusVariant(QVariant::fromValue(ref))))); - } else if (function == QLatin1String("GetAccessibleAt")) { - int row = message.arguments().at(0).toInt(); - int column = message.arguments().at(1).toInt(); - if ((row < 0) || - (column < 0) || - (row >= interface->tableInterface()->rowCount()) || - (column >= interface->tableInterface()->columnCount())) { - qCDebug(lcAccessibilityAtspi) << "WARNING: invalid index for tableInterface GetAccessibleAt (" << row << ", " << column << ')'; - return false; - } - - QSpiObjectReference ref; - QAccessibleInterface * cell(interface->tableInterface()->cellAt(row, column)); - if (cell) { - ref = QSpiObjectReference(connection, QDBusObjectPath(pathForInterface(cell))); - } else { - qCDebug(lcAccessibilityAtspi) << "WARNING: no cell interface returned for " << interface->object() << row << column; - ref = QSpiObjectReference(); - } - connection.send(message.createReply(QVariant::fromValue(ref))); - - } else if (function == QLatin1String("GetIndexAt")) { - int row = message.arguments().at(0).toInt(); - int column = message.arguments().at(1).toInt(); - QAccessibleInterface *cell = interface->tableInterface()->cellAt(row, column); - if (!cell) { - qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::GetIndexAt(" << row << ',' << column << ") did not find a cell. " << interface; - return false; - } - int index = interface->indexOfChild(cell); - qCDebug(lcAccessibilityAtspi) << "QSpiAdaptor::GetIndexAt row:" << row << " col:" << column << " logical index:" << index; - Q_ASSERT(index > 0); - connection.send(message.createReply(index)); - } else if ((function == QLatin1String("GetColumnAtIndex")) || (function == QLatin1String("GetRowAtIndex"))) { - int index = message.arguments().at(0).toInt(); - int ret = -1; - if (index >= 0) { - QAccessibleInterface * cell = interface->child(index); - if (cell) { - if (function == QLatin1String("GetColumnAtIndex")) { - if (cell->role() == QAccessible::ColumnHeader) { - ret = index; - } else if (cell->role() == QAccessible::RowHeader) { - ret = -1; - } else { - if (!cell->tableCellInterface()) { - qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::" << function << " No table cell interface: " << cell; - return false; - } - ret = cell->tableCellInterface()->columnIndex(); - } - } else { - if (cell->role() == QAccessible::ColumnHeader) { - ret = -1; - } else if (cell->role() == QAccessible::RowHeader) { - ret = index % interface->tableInterface()->columnCount(); - } else { - if (!cell->tableCellInterface()) { - qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::" << function << " No table cell interface: " << cell; - return false; - } - ret = cell->tableCellInterface()->rowIndex(); - } - } - } else { - qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::" << function << " No cell at index: " << index << interface; - return false; - } - } - connection.send(message.createReply(ret)); - - } else if (function == QLatin1String("GetColumnDescription")) { - int column = message.arguments().at(0).toInt(); - connection.send(message.createReply(interface->tableInterface()->columnDescription(column))); - } else if (function == QLatin1String("GetRowDescription")) { - int row = message.arguments().at(0).toInt(); - connection.send(message.createReply(interface->tableInterface()->rowDescription(row))); - - - - } else if (function == QLatin1String("GetRowColumnExtentsAtIndex")) { - int index = message.arguments().at(0).toInt(); - bool success = false; - - int row = -1; - int col = -1; - int rowExtents = -1; - int colExtents = -1; - bool isSelected = false; - - int cols = interface->tableInterface()->columnCount(); - if (cols > 0) { - row = index / cols; - col = index % cols; - QAccessibleTableCellInterface *cell = interface->tableInterface()->cellAt(row, col)->tableCellInterface(); - if (cell) { - row = cell->rowIndex(); - col = cell->columnIndex(); - rowExtents = cell->rowExtent(); - colExtents = cell->columnExtent(); - isSelected = cell->isSelected(); - success = true; - } - } - QVariantList list; - list << success << row << col << rowExtents << colExtents << isSelected; - connection.send(message.createReply(list)); - - } else if (function == QLatin1String("GetColumnExtentAt")) { - int row = message.arguments().at(0).toInt(); - int column = message.arguments().at(1).toInt(); - connection.send(message.createReply(interface->tableInterface()->cellAt(row, column)->tableCellInterface()->columnExtent())); - - } else if (function == QLatin1String("GetRowExtentAt")) { - int row = message.arguments().at(0).toInt(); - int column = message.arguments().at(1).toInt(); - connection.send(message.createReply(interface->tableInterface()->cellAt(row, column)->tableCellInterface()->rowExtent())); - - } else if (function == QLatin1String("GetColumnHeader")) { - int column = message.arguments().at(0).toInt(); - QSpiObjectReference ref; - - QAccessibleInterface * cell(interface->tableInterface()->cellAt(0, column)); - if (cell && cell->tableCellInterface()) { - QList header = cell->tableCellInterface()->columnHeaderCells(); - if (header.size() > 0) { - ref = QSpiObjectReference(connection, QDBusObjectPath(pathForInterface(header.takeAt(0)))); - } - } - connection.send(message.createReply(QVariant::fromValue(ref))); - - } else if (function == QLatin1String("GetRowHeader")) { - int row = message.arguments().at(0).toInt(); - QSpiObjectReference ref; - QAccessibleTableCellInterface *cell = interface->tableInterface()->cellAt(row, 0)->tableCellInterface(); - if (cell) { - QList header = cell->rowHeaderCells(); - if (header.size() > 0) { - ref = QSpiObjectReference(connection, QDBusObjectPath(pathForInterface(header.takeAt(0)))); - } - } - connection.send(message.createReply(QVariant::fromValue(ref))); - - } else if (function == QLatin1String("GetSelectedColumns")) { - connection.send(message.createReply(QVariant::fromValue(interface->tableInterface()->selectedColumns()))); - } else if (function == QLatin1String("GetSelectedRows")) { - connection.send(message.createReply(QVariant::fromValue(interface->tableInterface()->selectedRows()))); - } else if (function == QLatin1String("IsColumnSelected")) { - int column = message.arguments().at(0).toInt(); - connection.send(message.createReply(interface->tableInterface()->isColumnSelected(column))); - } else if (function == QLatin1String("IsRowSelected")) { - int row = message.arguments().at(0).toInt(); - connection.send(message.createReply(interface->tableInterface()->isRowSelected(row))); - } else if (function == QLatin1String("IsSelected")) { - int row = message.arguments().at(0).toInt(); - int column = message.arguments().at(1).toInt(); - QAccessibleTableCellInterface* cell = interface->tableInterface()->cellAt(row, column)->tableCellInterface(); - connection.send(message.createReply(cell->isSelected())); - } else if (function == QLatin1String("AddColumnSelection")) { - int column = message.arguments().at(0).toInt(); - connection.send(message.createReply(interface->tableInterface()->selectColumn(column))); - } else if (function == QLatin1String("AddRowSelection")) { - int row = message.arguments().at(0).toInt(); - connection.send(message.createReply(interface->tableInterface()->selectRow(row))); - } else if (function == QLatin1String("RemoveColumnSelection")) { - int column = message.arguments().at(0).toInt(); - connection.send(message.createReply(interface->tableInterface()->unselectColumn(column))); - } else if (function == QLatin1String("RemoveRowSelection")) { - int row = message.arguments().at(0).toInt(); - connection.send(message.createReply(interface->tableInterface()->unselectRow(row))); - } else { - qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::tableInterface does not implement " << function << message.path(); - return false; - } - return true; -} - -QT_END_NAMESPACE -#endif //QT_NO_ACCESSIBILITY diff --git a/src/platformsupport/linuxaccessibility/atspiadaptor_p.h b/src/platformsupport/linuxaccessibility/atspiadaptor_p.h deleted file mode 100644 index 97d302a779..0000000000 --- a/src/platformsupport/linuxaccessibility/atspiadaptor_p.h +++ /dev/null @@ -1,228 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - - -#ifndef ATSPIADAPTOR_H -#define ATSPIADAPTOR_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include - -#include -#include -#include -#include - -#include "dbusconnection_p.h" -#include "struct_marshallers_p.h" - -QT_REQUIRE_CONFIG(accessibility); - -QT_BEGIN_NAMESPACE - -class QAccessibleInterface; -class QSpiAccessibleInterface; -class QSpiApplicationAdaptor; - - -class AtSpiAdaptor :public QDBusVirtualObject -{ - Q_OBJECT - -public: - explicit AtSpiAdaptor(DBusConnection *connection, QObject *parent = nullptr); - ~AtSpiAdaptor(); - - void registerApplication(); - QString introspect(const QString &path) const override; - bool handleMessage(const QDBusMessage &message, const QDBusConnection &connection) override; - void notify(QAccessibleEvent *event); - - void init(); - void checkInitializedAndEnabled(); -public Q_SLOTS: - void eventListenerRegistered(const QString &bus, const QString &path); - void eventListenerDeregistered(const QString &bus, const QString &path); - void windowActivated(QObject* window, bool active); - -private: - void updateEventListeners(); - void setBitFlag(const QString &flag); - - // sending messages - QVariantList packDBusSignalArguments(const QString &type, int data1, int data2, const QVariant &variantData) const; - bool sendDBusSignal(const QString &path, const QString &interface, const QString &name, const QVariantList &arguments) const; - QVariant variantForPath(const QString &path) const; - - void sendFocusChanged(QAccessibleInterface *interface) const; - void notifyAboutCreation(QAccessibleInterface *interface) const; - void notifyAboutDestruction(QAccessibleInterface *interface) const; - void childrenChanged(QAccessibleInterface *interface) const; - - // handlers for the different accessible interfaces - bool applicationInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection); - bool accessibleInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection); - bool componentInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection); - bool actionInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection); - bool textInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection); - bool editableTextInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection); - bool valueInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection); - bool tableInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection); - - void sendReply(const QDBusConnection &connection, const QDBusMessage &message, const QVariant &argument) const; - - QAccessibleInterface *interfaceFromPath(const QString& dbusPath) const; - QString pathForInterface(QAccessibleInterface *interface) const; - QString pathForObject(QObject *object) const; - - void notifyStateChange(QAccessibleInterface *interface, const QString& state, int value); - - // accessible helper functions - AtspiRole getRole(QAccessibleInterface *interface) const; - QSpiRelationArray relationSet(QAccessibleInterface *interface, const QDBusConnection &connection) const; - QStringList accessibleInterfaces(QAccessibleInterface *interface) const; - - // component helper functions - static QRect getExtents(QAccessibleInterface *interface, uint coordType); - static QRect translateRectToWindowCoordinates(QAccessibleInterface *interface, const QRect &rect); - - // action helper functions - QSpiActionArray getActions(QAccessibleInterface *interface) const; - - // text helper functions - QVariantList getAttributes(QAccessibleInterface *, int offset, bool includeDefaults) const; - QVariantList getAttributeValue(QAccessibleInterface *, int offset, const QString &attributeName) const; - QList getCharacterExtents(QAccessibleInterface *, int offset, uint coordType) const; - QList getRangeExtents(QAccessibleInterface *, int startOffset, int endOffset, uint coordType) const; - QAccessible::TextBoundaryType qAccessibleBoundaryType(int atspiTextBoundaryType) const; - static bool inheritsQAction(QObject *object); - - // private vars - QSpiObjectReference accessibilityRegistry; - DBusConnection *m_dbus; - QSpiApplicationAdaptor *m_applicationAdaptor; - - /// Assigned from the accessibility registry. - int m_applicationId; - - // Bit fields - which updates to send - - // AT-SPI has some events that we do not care about: - // document - // document-load-complete - // document-load-stopped - // document-reload - uint sendFocus : 1; - // mouse abs/rel/button - - // all of object - uint sendObject : 1; - uint sendObject_active_descendant_changed : 1; - uint sendObject_attributes_changed : 1; - uint sendObject_bounds_changed : 1; - uint sendObject_children_changed : 1; -// uint sendObject_children_changed_add : 1; -// uint sendObject_children_changed_remove : 1; - uint sendObject_column_deleted : 1; - uint sendObject_column_inserted : 1; - uint sendObject_column_reordered : 1; - uint sendObject_link_selected : 1; - uint sendObject_model_changed : 1; - uint sendObject_property_change : 1; - uint sendObject_property_change_accessible_description : 1; - uint sendObject_property_change_accessible_name : 1; - uint sendObject_property_change_accessible_parent : 1; - uint sendObject_property_change_accessible_role : 1; - uint sendObject_property_change_accessible_table_caption : 1; - uint sendObject_property_change_accessible_table_column_description : 1; - uint sendObject_property_change_accessible_table_column_header : 1; - uint sendObject_property_change_accessible_table_row_description : 1; - uint sendObject_property_change_accessible_table_row_header : 1; - uint sendObject_property_change_accessible_table_summary : 1; - uint sendObject_property_change_accessible_value : 1; - uint sendObject_row_deleted : 1; - uint sendObject_row_inserted : 1; - uint sendObject_row_reordered : 1; - uint sendObject_selection_changed : 1; - uint sendObject_state_changed : 1; - uint sendObject_text_attributes_changed : 1; - uint sendObject_text_bounds_changed : 1; - uint sendObject_text_caret_moved : 1; - uint sendObject_text_changed : 1; -// uint sendObject_text_changed_delete : 1; -// uint sendObject_text_changed_insert : 1; - uint sendObject_text_selection_changed : 1; - uint sendObject_value_changed : 1; - uint sendObject_visible_data_changed : 1; - - // we don't implement terminal - // terminal-application_changed/charwidth_changed/columncount_changed/line_changed/linecount_changed - uint sendWindow : 1; - uint sendWindow_activate : 1; - uint sendWindow_close: 1; - uint sendWindow_create : 1; - uint sendWindow_deactivate : 1; -// uint sendWindow_desktop_create : 1; -// uint sendWindow_desktop_destroy : 1; - uint sendWindow_lower : 1; - uint sendWindow_maximize : 1; - uint sendWindow_minimize : 1; - uint sendWindow_move : 1; - uint sendWindow_raise : 1; - uint sendWindow_reparent : 1; - uint sendWindow_resize : 1; - uint sendWindow_restore : 1; - uint sendWindow_restyle : 1; - uint sendWindow_shade : 1; - uint sendWindow_unshade : 1; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/platformsupport/linuxaccessibility/bridge.cpp b/src/platformsupport/linuxaccessibility/bridge.cpp deleted file mode 100644 index fdc8cd3198..0000000000 --- a/src/platformsupport/linuxaccessibility/bridge.cpp +++ /dev/null @@ -1,286 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - - -#include "bridge_p.h" - -#include -#include - -#include "atspiadaptor_p.h" - -#include "cache_p.h" -#include "constant_mappings_p.h" -#include "dbusconnection_p.h" -#include "struct_marshallers_p.h" - -#ifndef QT_NO_ACCESSIBILITY -#include "deviceeventcontroller_adaptor.h" - -QT_BEGIN_NAMESPACE - -/*! - \class QSpiAccessibleBridge - \internal -*/ - -QSpiAccessibleBridge::QSpiAccessibleBridge() - : cache(0), dec(0), dbusAdaptor(0) -{ - dbusConnection = new DBusConnection(); - connect(dbusConnection, SIGNAL(enabledChanged(bool)), this, SLOT(enabledChanged(bool))); -} - -void QSpiAccessibleBridge::enabledChanged(bool enabled) -{ - setActive(enabled); - updateStatus(); -} - -QSpiAccessibleBridge::~QSpiAccessibleBridge() -{ - delete dbusConnection; -} // Qt currently doesn't delete plugins. - -QDBusConnection QSpiAccessibleBridge::dBusConnection() const -{ - return dbusConnection->connection(); -} - -void QSpiAccessibleBridge::updateStatus() -{ - // create the adaptor to handle everything if we are in enabled state - if (!dbusAdaptor && isActive()) { - 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) -{ - if (!dbusAdaptor) - return; - if (isActive() && event->accessibleInterface()) - dbusAdaptor->notify(event); -} - -struct RoleMapping { - QAccessible::Role role; - AtspiRole spiRole; - const char *name; -}; - -static RoleMapping map[] = { - //: Role of an accessible object - the object is in an invalid state or could not be constructed - { QAccessible::NoRole, ATSPI_ROLE_INVALID, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "invalid role") }, - //: Role of an accessible object - { QAccessible::TitleBar, ATSPI_ROLE_TEXT, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "title bar") }, - //: Role of an accessible object - { QAccessible::MenuBar, ATSPI_ROLE_MENU_BAR, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "menu bar") }, - //: Role of an accessible object - { QAccessible::ScrollBar, ATSPI_ROLE_SCROLL_BAR, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "scroll bar") }, - //: Role of an accessible object - the grip is usually used for resizing another object - { QAccessible::Grip, ATSPI_ROLE_UNKNOWN, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "grip") }, - //: Role of an accessible object - { QAccessible::Sound, ATSPI_ROLE_UNKNOWN, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "sound") }, - //: Role of an accessible object - { QAccessible::Cursor, ATSPI_ROLE_UNKNOWN, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "cursor") }, - //: Role of an accessible object - { QAccessible::Caret, ATSPI_ROLE_UNKNOWN, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "text caret") }, - //: Role of an accessible object - { QAccessible::AlertMessage, ATSPI_ROLE_ALERT, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "alert message") }, - //: Role of an accessible object: a window with frame and title - { QAccessible::Window, ATSPI_ROLE_FRAME, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "frame") }, - //: Role of an accessible object - { QAccessible::Client, ATSPI_ROLE_FILLER, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "filler") }, - //: Role of an accessible object - { QAccessible::PopupMenu, ATSPI_ROLE_POPUP_MENU, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "popup menu") }, - //: Role of an accessible object - { QAccessible::MenuItem, ATSPI_ROLE_MENU_ITEM, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "menu item") }, - //: Role of an accessible object - { QAccessible::ToolTip, ATSPI_ROLE_TOOL_TIP, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "tool tip") }, - //: Role of an accessible object - { QAccessible::Application, ATSPI_ROLE_APPLICATION, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "application") }, - //: Role of an accessible object - { QAccessible::Document, ATSPI_ROLE_DOCUMENT_FRAME, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "document") }, - //: Role of an accessible object - { QAccessible::Pane, ATSPI_ROLE_PANEL, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "panel") }, - //: Role of an accessible object - { QAccessible::Chart, ATSPI_ROLE_CHART, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "chart") }, - //: Role of an accessible object - { QAccessible::Dialog, ATSPI_ROLE_DIALOG, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "dialog") }, - //: Role of an accessible object - { QAccessible::Border, ATSPI_ROLE_FRAME, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "frame") }, - //: Role of an accessible object - { QAccessible::Grouping, ATSPI_ROLE_PANEL, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "panel") }, - //: Role of an accessible object - { QAccessible::Separator, ATSPI_ROLE_SEPARATOR, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "separator") }, - //: Role of an accessible object - { QAccessible::ToolBar, ATSPI_ROLE_TOOL_BAR, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "tool bar") }, - //: Role of an accessible object - { QAccessible::StatusBar, ATSPI_ROLE_STATUS_BAR, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "status bar") }, - //: Role of an accessible object - { QAccessible::Table, ATSPI_ROLE_TABLE, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "table") }, - //: Role of an accessible object - part of a table - { QAccessible::ColumnHeader, ATSPI_ROLE_TABLE_COLUMN_HEADER, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "column header") }, - //: Role of an accessible object - part of a table - { QAccessible::RowHeader, ATSPI_ROLE_TABLE_ROW_HEADER, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "row header") }, - //: Role of an accessible object - part of a table - { QAccessible::Column, ATSPI_ROLE_TABLE_CELL, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "column") }, - //: Role of an accessible object - part of a table - { QAccessible::Row, ATSPI_ROLE_TABLE_ROW, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "row") }, - //: Role of an accessible object - part of a table - { QAccessible::Cell, ATSPI_ROLE_TABLE_CELL, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "cell") }, - //: Role of an accessible object - { QAccessible::Link, ATSPI_ROLE_LINK, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "link") }, - //: Role of an accessible object - { QAccessible::HelpBalloon, ATSPI_ROLE_DIALOG, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "help balloon") }, - //: Role of an accessible object - a helper dialog - { QAccessible::Assistant, ATSPI_ROLE_DIALOG, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "assistant") }, - //: Role of an accessible object - { QAccessible::List, ATSPI_ROLE_LIST, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "list") }, - //: Role of an accessible object - { QAccessible::ListItem, ATSPI_ROLE_LIST_ITEM, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "list item") }, - //: Role of an accessible object - { QAccessible::Tree, ATSPI_ROLE_TREE, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "tree") }, - //: Role of an accessible object - { QAccessible::TreeItem, ATSPI_ROLE_TABLE_CELL, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "tree item") }, - //: Role of an accessible object - { QAccessible::PageTab, ATSPI_ROLE_PAGE_TAB, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "page tab") }, - //: Role of an accessible object - { QAccessible::PropertyPage, ATSPI_ROLE_PAGE_TAB, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "property page") }, - //: Role of an accessible object - { QAccessible::Indicator, ATSPI_ROLE_UNKNOWN, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "indicator") }, - //: Role of an accessible object - { QAccessible::Graphic, ATSPI_ROLE_IMAGE, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "graphic") }, - //: Role of an accessible object - { QAccessible::StaticText, ATSPI_ROLE_LABEL, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "label") }, - //: Role of an accessible object - { QAccessible::EditableText, ATSPI_ROLE_TEXT, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "text") }, - //: Role of an accessible object - { QAccessible::PushButton, ATSPI_ROLE_PUSH_BUTTON, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "push button") }, - //: Role of an accessible object - { QAccessible::CheckBox, ATSPI_ROLE_CHECK_BOX, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "check box") }, - //: Role of an accessible object - { QAccessible::RadioButton, ATSPI_ROLE_RADIO_BUTTON, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "radio button") }, - //: Role of an accessible object - { QAccessible::ComboBox, ATSPI_ROLE_COMBO_BOX, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "combo box") }, - //: Role of an accessible object - { QAccessible::ProgressBar, ATSPI_ROLE_PROGRESS_BAR, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "progress bar") }, - //: Role of an accessible object - { QAccessible::Dial, ATSPI_ROLE_DIAL, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "dial") }, - //: Role of an accessible object - { QAccessible::HotkeyField, ATSPI_ROLE_TEXT, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "hotkey field") }, - //: Role of an accessible object - { QAccessible::Slider, ATSPI_ROLE_SLIDER, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "slider") }, - //: Role of an accessible object - { QAccessible::SpinBox, ATSPI_ROLE_SPIN_BUTTON, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "spin box") }, - //: Role of an accessible object - { QAccessible::Canvas, ATSPI_ROLE_CANVAS, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "canvas") }, - //: Role of an accessible object - { QAccessible::Animation, ATSPI_ROLE_ANIMATION, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "animation") }, - //: Role of an accessible object - { QAccessible::Equation, ATSPI_ROLE_TEXT, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "equation") }, - //: Role of an accessible object - { QAccessible::ButtonDropDown, ATSPI_ROLE_PUSH_BUTTON, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "button with drop down") }, - //: Role of an accessible object - { QAccessible::ButtonMenu, ATSPI_ROLE_PUSH_BUTTON, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "button menu") }, - //: Role of an accessible object - a button that expands a grid. - { QAccessible::ButtonDropGrid, ATSPI_ROLE_PUSH_BUTTON, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "button with drop down grid") }, - //: Role of an accessible object - blank space between other objects. - { QAccessible::Whitespace, ATSPI_ROLE_FILLER, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "space") }, - //: Role of an accessible object - { QAccessible::PageTabList, ATSPI_ROLE_PAGE_TAB_LIST, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "page tab list") }, - //: Role of an accessible object - { QAccessible::Clock, ATSPI_ROLE_UNKNOWN, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "clock") }, - //: Role of an accessible object - { QAccessible::Splitter, ATSPI_ROLE_SPLIT_PANE, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "splitter") }, - //: Role of an accessible object - { QAccessible::LayeredPane, ATSPI_ROLE_LAYERED_PANE, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "layered pane") }, - //: Role of an accessible object - { QAccessible::WebDocument, ATSPI_ROLE_DOCUMENT_WEB, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "web document") }, - //: Role of an accessible object - { QAccessible::Paragraph, ATSPI_ROLE_PARAGRAPH, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "paragraph") }, - //: Role of an accessible object - { QAccessible::Section, ATSPI_ROLE_SECTION, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "section") }, - //: Role of an accessible object - { QAccessible::ColorChooser, ATSPI_ROLE_COLOR_CHOOSER, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "color chooser") }, - //: Role of an accessible object - { QAccessible::Footer, ATSPI_ROLE_FOOTER, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "footer") }, - //: Role of an accessible object - { QAccessible::Form, ATSPI_ROLE_FORM, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "form") }, - //: Role of an accessible object - { QAccessible::Heading, ATSPI_ROLE_HEADING, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "heading") }, - //: Role of an accessible object - { QAccessible::Note, ATSPI_ROLE_COMMENT, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "note") }, - //: Role of an accessible object - { QAccessible::ComplementaryContent, ATSPI_ROLE_SECTION, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "complementary content") }, - //: Role of an accessible object - { QAccessible::Terminal, ATSPI_ROLE_TERMINAL, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "terminal") }, - //: Role of an accessible object - { QAccessible::Desktop, ATSPI_ROLE_DESKTOP_FRAME, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "desktop") }, - //: Role of an accessible object - { QAccessible::Notification, ATSPI_ROLE_NOTIFICATION, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "notification") }, - //: Role of an accessible object - { QAccessible::UserRole, ATSPI_ROLE_UNKNOWN, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "unknown") } -}; - -void QSpiAccessibleBridge::initializeConstantMappings() -{ - for (uint i = 0; i < sizeof(map) / sizeof(RoleMapping); ++i) - qSpiRoleMapping.insert(map[i].role, RoleNames(map[i].spiRole, QLatin1String(map[i].name), tr(map[i].name))); - - // -1 because we have button duplicated, as PushButton and Button. - Q_ASSERT_X(qSpiRoleMapping.size() == - QAccessible::staticMetaObject.enumerator( - QAccessible::staticMetaObject.indexOfEnumerator("Role")).keyCount() - 1, - "", "Handle all QAccessible::Role members in qSpiRoleMapping"); -} - -QT_END_NAMESPACE -#endif //QT_NO_ACCESSIBILITY diff --git a/src/platformsupport/linuxaccessibility/bridge_p.h b/src/platformsupport/linuxaccessibility/bridge_p.h deleted file mode 100644 index c48bbd3710..0000000000 --- a/src/platformsupport/linuxaccessibility/bridge_p.h +++ /dev/null @@ -1,95 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - - -#ifndef QSPIACCESSIBLEBRIDGE_H -#define QSPIACCESSIBLEBRIDGE_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include -#include -#include - -class DeviceEventControllerAdaptor; - -QT_REQUIRE_CONFIG(accessibility); - -QT_BEGIN_NAMESPACE - -class DBusConnection; -class QSpiDBusCache; -class AtSpiAdaptor; - -class QSpiAccessibleBridge: public QObject, public QPlatformAccessibility -{ - Q_OBJECT -public: - QSpiAccessibleBridge(); - - virtual ~QSpiAccessibleBridge(); - - void notifyAccessibilityUpdate(QAccessibleEvent *event) override; - QDBusConnection dBusConnection() const; - -public Q_SLOTS: - void enabledChanged(bool enabled); - -private: - void initializeConstantMappings(); - void updateStatus(); - - QSpiDBusCache *cache; - DeviceEventControllerAdaptor *dec; - AtSpiAdaptor *dbusAdaptor; - DBusConnection* dbusConnection; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/platformsupport/linuxaccessibility/cache.cpp b/src/platformsupport/linuxaccessibility/cache.cpp deleted file mode 100644 index fe4d1e26f9..0000000000 --- a/src/platformsupport/linuxaccessibility/cache.cpp +++ /dev/null @@ -1,91 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - - -#include "cache_p.h" -#include "bridge_p.h" - -#ifndef QT_NO_ACCESSIBILITY -#include "cache_adaptor.h" - -#define QSPI_OBJECT_PATH_CACHE "/org/a11y/atspi/cache" - -QT_BEGIN_NAMESPACE - -/*! - \class QSpiDBusCache - \internal - \brief This class is responsible for the AT-SPI cache interface. - - The idea behind the cache is that starting an application would - result in many dbus calls. The way GTK/Gail/ATK work is that - they create accessibles for all objects on startup. - In order to avoid querying all the objects individually via DBus - they get sent by using the GetItems call of the cache. - - Additionally the AddAccessible and RemoveAccessible signals - are responsible for adding/removing objects from the cache. - - Currently the Qt bridge chooses to ignore these. -*/ - -QSpiDBusCache::QSpiDBusCache(QDBusConnection c, QObject* parent) - : QObject(parent) -{ - new CacheAdaptor(this); - c.registerObject(QLatin1String(QSPI_OBJECT_PATH_CACHE), this, QDBusConnection::ExportAdaptors); -} - -void QSpiDBusCache::emitAddAccessible(const QSpiAccessibleCacheItem& item) -{ - emit AddAccessible(item); -} - -void QSpiDBusCache::emitRemoveAccessible(const QSpiObjectReference& item) -{ - emit RemoveAccessible(item); -} - -QSpiAccessibleCacheArray QSpiDBusCache::GetItems() -{ - return QSpiAccessibleCacheArray(); -} - -QT_END_NAMESPACE -#endif //QT_NO_ACCESSIBILITY diff --git a/src/platformsupport/linuxaccessibility/cache_p.h b/src/platformsupport/linuxaccessibility/cache_p.h deleted file mode 100644 index cc55acc6f8..0000000000 --- a/src/platformsupport/linuxaccessibility/cache_p.h +++ /dev/null @@ -1,82 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - - -#ifndef Q_SPI_CACHE_H -#define Q_SPI_CACHE_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include -#include -#include "struct_marshallers_p.h" - -QT_REQUIRE_CONFIG(accessibility); - -QT_BEGIN_NAMESPACE - -class QSpiDBusCache : public QObject -{ - Q_OBJECT - -public: - explicit QSpiDBusCache(QDBusConnection c, QObject* parent = nullptr); - void emitAddAccessible(const QSpiAccessibleCacheItem& item); - void emitRemoveAccessible(const QSpiObjectReference& item); - -Q_SIGNALS: - void AddAccessible(const QSpiAccessibleCacheItem &nodeAdded); - void RemoveAccessible(const QSpiObjectReference &nodeRemoved); - -public Q_SLOTS: - QSpiAccessibleCacheArray GetItems(); -}; - -QT_END_NAMESPACE - -#endif /* Q_SPI_CACHE_H */ diff --git a/src/platformsupport/linuxaccessibility/constant_mappings.cpp b/src/platformsupport/linuxaccessibility/constant_mappings.cpp deleted file mode 100644 index fce2919e73..0000000000 --- a/src/platformsupport/linuxaccessibility/constant_mappings.cpp +++ /dev/null @@ -1,154 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - - -#include "constant_mappings_p.h" - -#include -#include - -// FIXME the assignment of roles is quite arbitrary, at some point go through this list and sort and check that it makes sense -// "calendar" "check menu item" "color chooser" "column header" "dateeditor" "desktop icon" "desktop frame" -// "directory pane" "drawing area" "file chooser" "fontchooser" "frame" "glass pane" "html container" "icon" -// "internal frame" "option pane" "password text" "radio menu item" "root pane" "row header" "scroll pane" -// "tear off menu item" "terminal" "toggle button" "tree table" "unknown" "viewport" "header" "footer" "paragraph" -// "ruler" "autocomplete" "edit bar" "embedded component" "entry" "caption" -// "heading" "page" "section" "redundant object" "form" "input method window" "menu" - -#ifndef QT_NO_ACCESSIBILITY -QT_BEGIN_NAMESPACE - -QHash qSpiRoleMapping; - -quint64 spiStatesFromQState(QAccessible::State state) -{ - quint64 spiState = 0; - - if (state.active) - setSpiStateBit(&spiState, ATSPI_STATE_ACTIVE); - if (state.editable) - setSpiStateBit(&spiState, ATSPI_STATE_EDITABLE); - if (!state.disabled) { - setSpiStateBit(&spiState, ATSPI_STATE_ENABLED); - setSpiStateBit(&spiState, ATSPI_STATE_SENSITIVE); - } - if (state.selected) - setSpiStateBit(&spiState, ATSPI_STATE_SELECTED); - if (state.focused) - setSpiStateBit(&spiState, ATSPI_STATE_FOCUSED); - if (state.pressed) - setSpiStateBit(&spiState, ATSPI_STATE_PRESSED); - if (state.checked) - setSpiStateBit(&spiState, ATSPI_STATE_CHECKED); - if (state.checkStateMixed) - setSpiStateBit(&spiState, ATSPI_STATE_INDETERMINATE); - if (state.readOnly) - setSpiStateBit(&spiState, ATSPI_STATE_READ_ONLY); - // if (state.HotTracked) - if (state.defaultButton) - setSpiStateBit(&spiState, ATSPI_STATE_IS_DEFAULT); - if (state.expandable) - setSpiStateBit(&spiState, ATSPI_STATE_EXPANDABLE); - if (state.expanded) - setSpiStateBit(&spiState, ATSPI_STATE_EXPANDED); - if (state.collapsed) - setSpiStateBit(&spiState, ATSPI_STATE_COLLAPSED); - if (state.busy) - setSpiStateBit(&spiState, ATSPI_STATE_BUSY); - if (state.marqueed || state.animated) - setSpiStateBit(&spiState, ATSPI_STATE_ANIMATED); - if (!state.invisible && !state.offscreen) { - setSpiStateBit(&spiState, ATSPI_STATE_SHOWING); - setSpiStateBit(&spiState, ATSPI_STATE_VISIBLE); - } - if (state.sizeable) - setSpiStateBit(&spiState, ATSPI_STATE_RESIZABLE); - // if (state.Movable) - // if (state.SelfVoicing) - if (state.focusable) - setSpiStateBit(&spiState, ATSPI_STATE_FOCUSABLE); - if (state.selectable) - setSpiStateBit(&spiState, ATSPI_STATE_SELECTABLE); - // if (state.Linked) - if (state.traversed) - setSpiStateBit(&spiState, ATSPI_STATE_VISITED); - if (state.multiSelectable) - setSpiStateBit(&spiState, ATSPI_STATE_MULTISELECTABLE); - if (state.extSelectable) - setSpiStateBit(&spiState, ATSPI_STATE_SELECTABLE); - // if (state.Protected) - // if (state.HasPopup) - if (state.modal) - setSpiStateBit(&spiState, ATSPI_STATE_MODAL); - if (state.multiLine) - setSpiStateBit(&spiState, ATSPI_STATE_MULTI_LINE); - - return spiState; -} - -QSpiUIntList spiStateSetFromSpiStates(quint64 states) -{ - uint low = states & 0xFFFFFFFF; - uint high = (states >> 32) & 0xFFFFFFFF; - - QSpiUIntList stateList; - stateList.append(low); - stateList.append(high); - return stateList; -} - -AtspiRelationType qAccessibleRelationToAtSpiRelation(QAccessible::Relation relation) -{ - switch (relation) { - case QAccessible::Label: - return ATSPI_RELATION_LABELLED_BY; - case QAccessible::Labelled: - return ATSPI_RELATION_LABEL_FOR; - case QAccessible::Controller: - return ATSPI_RELATION_CONTROLLED_BY; - case QAccessible::Controlled: - return ATSPI_RELATION_CONTROLLER_FOR; - default: - qWarning() << "Cannot return AT-SPI relation for:" << relation; - } - return ATSPI_RELATION_NULL; -} - -QT_END_NAMESPACE -#endif //QT_NO_ACCESSIBILITY diff --git a/src/platformsupport/linuxaccessibility/constant_mappings_p.h b/src/platformsupport/linuxaccessibility/constant_mappings_p.h deleted file mode 100644 index 1d4dd833e1..0000000000 --- a/src/platformsupport/linuxaccessibility/constant_mappings_p.h +++ /dev/null @@ -1,146 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -/* - * This file contains AT-SPI constants and mappings between QAccessible - * and AT-SPI constants such as 'role' and 'state' enumerations. - */ - -#ifndef Q_SPI_CONSTANT_MAPPINGS_H -#define Q_SPI_CONSTANT_MAPPINGS_H - -#include "struct_marshallers_p.h" - -#include -#include -#include - -QT_REQUIRE_CONFIG(accessibility); - -// interface names from at-spi2-core/atspi/atspi-misc-private.h -#define ATSPI_DBUS_NAME_REGISTRY "org.a11y.atspi.Registry" -#define ATSPI_DBUS_PATH_REGISTRY "/org/a11y/atspi/registry" -#define ATSPI_DBUS_INTERFACE_REGISTRY "org.a11y.atspi.Registry" - -#define ATSPI_DBUS_PATH_ROOT "/org/a11y/atspi/accessible/root" - -#define ATSPI_DBUS_PATH_DEC "/org/a11y/atspi/registry/deviceeventcontroller" -#define ATSPI_DBUS_INTERFACE_DEC "org.a11y.atspi.DeviceEventController" -#define ATSPI_DBUS_INTERFACE_DEVICE_EVENT_LISTENER "org.a11y.atspi.DeviceEventListener" - -#define ATSPI_DBUS_INTERFACE_CACHE "org.a11y.atspi.Cache" -#define ATSPI_DBUS_INTERFACE_ACCESSIBLE "org.a11y.atspi.Accessible" -#define ATSPI_DBUS_INTERFACE_ACTION "org.a11y.atspi.Action" -#define ATSPI_DBUS_INTERFACE_APPLICATION "org.a11y.atspi.Application" -#define ATSPI_DBUS_INTERFACE_COLLECTION "org.a11y.atspi.Collection" -#define ATSPI_DBUS_INTERFACE_COMPONENT "org.a11y.atspi.Component" -#define ATSPI_DBUS_INTERFACE_DOCUMENT "org.a11y.atspi.Document" -#define ATSPI_DBUS_INTERFACE_EDITABLE_TEXT "org.a11y.atspi.EditableText" -#define ATSPI_DBUS_INTERFACE_EVENT_KEYBOARD "org.a11y.atspi.Event.Keyboard" -#define ATSPI_DBUS_INTERFACE_EVENT_MOUSE "org.a11y.atspi.Event.Mouse" -#define ATSPI_DBUS_INTERFACE_EVENT_OBJECT "org.a11y.atspi.Event.Object" -#define ATSPI_DBUS_INTERFACE_HYPERLINK "org.a11y.atspi.Hyperlink" -#define ATSPI_DBUS_INTERFACE_HYPERTEXT "org.a11y.atspi.Hypertext" -#define ATSPI_DBUS_INTERFACE_IMAGE "org.a11y.atspi.Image" -#define ATSPI_DBUS_INTERFACE_SELECTION "org.a11y.atspi.Selection" -#define ATSPI_DBUS_INTERFACE_TABLE "org.a11y.atspi.Table" -#define ATSPI_DBUS_INTERFACE_TEXT "org.a11y.atspi.Text" -#define ATSPI_DBUS_INTERFACE_VALUE "org.a11y.atspi.Value" -#define ATSPI_DBUS_INTERFACE_SOCKET "org.a11y.atspi.Socket" - -// missing from at-spi2-core: -#define ATSPI_DBUS_INTERFACE_EVENT_WINDOW "org.a11y.atspi.Event.Window" -#define ATSPI_DBUS_INTERFACE_EVENT_FOCUS "org.a11y.atspi.Event.Focus" - -#define QSPI_OBJECT_PATH_ACCESSIBLE "/org/a11y/atspi/accessible" -#define QSPI_OBJECT_PATH_PREFIX "/org/a11y/atspi/accessible/" -#define QSPI_OBJECT_PATH_ROOT QSPI_OBJECT_PATH_PREFIX "root" - -#define QSPI_REGISTRY_NAME "org.a11y.atspi.Registry" - -QT_BEGIN_NAMESPACE - -struct RoleNames { - RoleNames() {} - RoleNames(AtspiRole r, const QString& n, const QString& ln) - :m_spiRole(r), m_name(n), m_localizedName(ln) - {} - - AtspiRole spiRole() const {return m_spiRole;} - QString name() const {return m_name;} - QString localizedName() const {return m_localizedName;} - -private: - AtspiRole m_spiRole = ATSPI_ROLE_INVALID; - QString m_name; - QString m_localizedName; -}; - -extern QHash qSpiRoleMapping; -extern QHash qSpiStateMapping; - -inline void setSpiStateBit(quint64* state, AtspiStateType spiState) -{ - *state |= quint64(1) << spiState; -} - -inline void unsetSpiStateBit(quint64* state, AtspiStateType spiState) -{ - *state &= ~(quint64(1) << spiState); -} - -quint64 spiStatesFromQState(QAccessible::State state); -QSpiUIntList spiStateSetFromSpiStates(quint64 states); - -AtspiRelationType qAccessibleRelationToAtSpiRelation(QAccessible::Relation relation); - -QT_END_NAMESPACE - -#endif /* Q_SPI_CONSTANT_MAPPINGS_H */ diff --git a/src/platformsupport/linuxaccessibility/dbusconnection.cpp b/src/platformsupport/linuxaccessibility/dbusconnection.cpp deleted file mode 100644 index 45ddc8e496..0000000000 --- a/src/platformsupport/linuxaccessibility/dbusconnection.cpp +++ /dev/null @@ -1,172 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - - - -#include "dbusconnection_p.h" - -#include -#include -#include - -#include -#include "bus_interface.h" - -#include -#include - -QT_BEGIN_NAMESPACE - -/* note: do not change these to QStringLiteral; - we are unloaded before QtDBus is done using the strings. - */ -#define A11Y_SERVICE QLatin1String("org.a11y.Bus") -#define A11Y_PATH QLatin1String("/org/a11y/bus") - -/*! - \class DBusConnection - \internal - \brief Connects to the accessibility dbus. - - This is usually a different bus from the session bus. -*/ -DBusConnection::DBusConnection(QObject *parent) - : QObject(parent), m_a11yConnection(QString()), m_enabled(false) -{ - // Start monitoring if "org.a11y.Bus" is registered as DBus service. - QDBusConnection c = QDBusConnection::sessionBus(); - if (!c.isConnected()) { - return; - } - - 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(); - - // In addition try if there is an xatom exposing the bus address, this allows applications run as root to work - QString address = getAddressFromXCB(); - if (!address.isEmpty()) { - m_enabled = true; - connectA11yBus(address); - } -} - -QString DBusConnection::getAddressFromXCB() -{ - QGuiApplication *app = qobject_cast(QCoreApplication::instance()); - if (!app) - return QString(); - QPlatformNativeInterface *platformNativeInterface = app->platformNativeInterface(); - QByteArray *addressByteArray = reinterpret_cast( - platformNativeInterface->nativeResourceForIntegration(QByteArrayLiteral("AtspiBus"))); - if (addressByteArray) { - QString address = QString::fromLatin1(*addressByteArray); - delete addressByteArray; - return address; - } - return QString(); -} - -// 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(); - OrgA11yStatusInterface *a11yStatus = new OrgA11yStatusInterface(A11Y_SERVICE, A11Y_PATH, c, this); - - //The variable was introduced because on some embedded platforms there are custom accessibility - //clients which don't set Status.ScreenReaderEnabled to true. The variable is also useful for - //debugging. - static const bool a11yAlwaysOn = qEnvironmentVariableIsSet("QT_LINUX_ACCESSIBILITY_ALWAYS_ON"); - - bool enabled = a11yAlwaysOn || a11yStatus->screenReaderEnabled() || a11yStatus->isEnabled(); - - if (enabled != m_enabled) { - m_enabled = enabled; - 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))); - } - } - - // connect(a11yStatus, ); QtDbus doesn't support notifications for property changes yet -} - -void DBusConnection::serviceUnregistered() -{ - emit enabledChanged(false); -} - -void DBusConnection::connectA11yBus(const QString &address) -{ - if (address.isEmpty()) { - qWarning("Could not find Accessibility DBus address."); - return; - } - m_a11yConnection = QDBusConnection(QDBusConnection::connectToBus(address, QLatin1String("a11y"))); - - if (m_enabled) - emit enabledChanged(true); -} - -void DBusConnection::dbusError(const QDBusError &error) -{ - qWarning() << "Accessibility encountered a DBus error:" << error; -} - -/*! - Returns the DBus connection that got established. - Or an invalid connection if not yet connected. -*/ -QDBusConnection DBusConnection::connection() const -{ - return m_a11yConnection; -} - -QT_END_NAMESPACE diff --git a/src/platformsupport/linuxaccessibility/dbusconnection_p.h b/src/platformsupport/linuxaccessibility/dbusconnection_p.h deleted file mode 100644 index dde3bbb98f..0000000000 --- a/src/platformsupport/linuxaccessibility/dbusconnection_p.h +++ /dev/null @@ -1,95 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - - -#ifndef DBUSCONNECTION_H -#define DBUSCONNECTION_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include -#include -#include -Q_MOC_INCLUDE() - -QT_BEGIN_NAMESPACE - -class QDBusServiceWatcher; - -class DBusConnection : public QObject -{ - Q_OBJECT - -public: - DBusConnection(QObject *parent = nullptr); - 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: - QString getAddressFromXCB(); - void serviceRegistered(); - void serviceUnregistered(); - void connectA11yBus(const QString &address); - - void dbusError(const QDBusError &error); - -private: - QString getAccessibilityBusAddress() const; - - QDBusServiceWatcher *dbusWatcher; - QDBusConnection m_a11yConnection; - bool m_enabled; -}; - -QT_END_NAMESPACE - -#endif // DBUSCONNECTION_H diff --git a/src/platformsupport/linuxaccessibility/dbusxml/Bus.xml b/src/platformsupport/linuxaccessibility/dbusxml/Bus.xml deleted file mode 100644 index 5a33e335a1..0000000000 --- a/src/platformsupport/linuxaccessibility/dbusxml/Bus.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/src/platformsupport/linuxaccessibility/dbusxml/Cache.xml b/src/platformsupport/linuxaccessibility/dbusxml/Cache.xml deleted file mode 100644 index 01c52810ac..0000000000 --- a/src/platformsupport/linuxaccessibility/dbusxml/Cache.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/src/platformsupport/linuxaccessibility/dbusxml/DeviceEventController.xml b/src/platformsupport/linuxaccessibility/dbusxml/DeviceEventController.xml deleted file mode 100644 index d4c26ef7e7..0000000000 --- a/src/platformsupport/linuxaccessibility/dbusxml/DeviceEventController.xml +++ /dev/null @@ -1,66 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/src/platformsupport/linuxaccessibility/dbusxml/Socket.xml b/src/platformsupport/linuxaccessibility/dbusxml/Socket.xml deleted file mode 100644 index 75ec99f994..0000000000 --- a/src/platformsupport/linuxaccessibility/dbusxml/Socket.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/platformsupport/linuxaccessibility/linuxaccessibility.pro b/src/platformsupport/linuxaccessibility/linuxaccessibility.pro deleted file mode 100644 index 9ccb4893d2..0000000000 --- a/src/platformsupport/linuxaccessibility/linuxaccessibility.pro +++ /dev/null @@ -1,36 +0,0 @@ -TARGET = QtLinuxAccessibilitySupport -MODULE = linuxaccessibility_support - -QT = core-private dbus gui-private -CONFIG += static internal_module - -DEFINES += QT_NO_CAST_FROM_ASCII -PRECOMPILED_HEADER = ../../corelib/global/qt_pch.h - -DBUS_ADAPTORS = $$PWD/dbusxml/Cache.xml $$PWD/dbusxml/DeviceEventController.xml -QDBUSXML2CPP_ADAPTOR_HEADER_FLAGS = -i struct_marshallers_p.h - -DBUS_INTERFACES = $$PWD/dbusxml/Socket.xml $$PWD/dbusxml/Bus.xml -QDBUSXML2CPP_INTERFACE_HEADER_FLAGS = -i struct_marshallers_p.h - -QMAKE_USE += atspi/nolink - -HEADERS += \ - application_p.h \ - bridge_p.h \ - cache_p.h \ - struct_marshallers_p.h \ - constant_mappings_p.h \ - dbusconnection_p.h \ - atspiadaptor_p.h - -SOURCES += \ - application.cpp \ - bridge.cpp \ - cache.cpp \ - struct_marshallers.cpp \ - constant_mappings.cpp \ - dbusconnection.cpp \ - atspiadaptor.cpp - -load(qt_module) diff --git a/src/platformsupport/linuxaccessibility/struct_marshallers.cpp b/src/platformsupport/linuxaccessibility/struct_marshallers.cpp deleted file mode 100644 index 9bbaf23332..0000000000 --- a/src/platformsupport/linuxaccessibility/struct_marshallers.cpp +++ /dev/null @@ -1,237 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - - -#include "struct_marshallers_p.h" - -#include -#include -#include - -#include "bridge_p.h" - -#ifndef QT_NO_ACCESSIBILITY -QT_BEGIN_NAMESPACE - -QSpiObjectReference::QSpiObjectReference() - : path(QDBusObjectPath(ATSPI_DBUS_PATH_NULL)) -{} - -/* QSpiAccessibleCacheArray */ -/*---------------------------------------------------------------------------*/ - -QDBusArgument &operator<<(QDBusArgument &argument, const QSpiAccessibleCacheItem &item) -{ - argument.beginStructure(); - argument << item.path; - argument << item.application; - argument << item.parent; - argument << item.children; - argument << item.supportedInterfaces; - argument << item.name; - argument << item.role; - argument << item.description; - argument << item.state; - argument.endStructure(); - return argument; -} - -const QDBusArgument &operator>>(const QDBusArgument &argument, QSpiAccessibleCacheItem &item) -{ - argument.beginStructure(); - argument >> item.path; - argument >> item.application; - argument >> item.parent; - argument >> item.children; - argument >> item.supportedInterfaces; - argument >> item.name; - argument >> item.role; - argument >> item.description; - argument >> item.state; - argument.endStructure(); - return argument; -} - -/* QSpiObjectReference */ -/*---------------------------------------------------------------------------*/ - -QDBusArgument &operator<<(QDBusArgument &argument, const QSpiObjectReference &address) -{ - argument.beginStructure(); - argument << address.service; - argument << address.path; - argument.endStructure(); - return argument; -} - -const QDBusArgument &operator>>(const QDBusArgument &argument, QSpiObjectReference &address) -{ - argument.beginStructure(); - argument >> address.service; - argument >> address.path; - argument.endStructure(); - return argument; -} - -/* QSpiAction */ -/*---------------------------------------------------------------------------*/ - -QDBusArgument &operator<<(QDBusArgument &argument, const QSpiAction &action) -{ - argument.beginStructure(); - argument << action.name; - argument << action.description; - argument << action.keyBinding; - argument.endStructure(); - return argument; -} - -const QDBusArgument &operator>>(const QDBusArgument &argument, QSpiAction &action) -{ - argument.beginStructure(); - argument >> action.name; - argument >> action.description; - argument >> action.keyBinding; - argument.endStructure(); - return argument; -} - - -QDBusArgument &operator<<(QDBusArgument &argument, const QSpiEventListener &ev) -{ - argument.beginStructure(); - argument << ev.listenerAddress; - argument << ev.eventName; - argument.endStructure(); - return argument; -} - -const QDBusArgument &operator>>(const QDBusArgument &argument, QSpiEventListener &ev) -{ - argument.beginStructure(); - argument >> ev.listenerAddress; - argument >> ev.eventName; - argument.endStructure(); - return argument; -} - -/* QSpiAppUpdate */ -/*---------------------------------------------------------------------------*/ - -QDBusArgument &operator<<(QDBusArgument &argument, const QSpiAppUpdate &update) { - argument.beginStructure(); - argument << update.type << update.address; - argument.endStructure(); - return argument; -} - -const QDBusArgument &operator>>(const QDBusArgument &argument, QSpiAppUpdate &update) { - argument.beginStructure(); - argument >> update.type >> update.address; - argument.endStructure(); - return argument; -} - -/* QSpiRelationArrayEntry */ -/*---------------------------------------------------------------------------*/ - -QDBusArgument &operator<<(QDBusArgument &argument, const QSpiRelationArrayEntry &entry) { - argument.beginStructure(); - argument << entry.first << entry.second; - argument.endStructure(); - return argument; -} - -const QDBusArgument &operator>>(const QDBusArgument &argument, QSpiRelationArrayEntry &entry) { - argument.beginStructure(); - argument >> entry.first >> entry.second; - argument.endStructure(); - return argument; -} - -/* QSpiDeviceEvent */ -/*---------------------------------------------------------------------------*/ - -QDBusArgument &operator<<(QDBusArgument &argument, const QSpiDeviceEvent &event) { - argument.beginStructure(); - argument << event.type - << event.id - << event.hardwareCode - << event.modifiers - << event.timestamp - << event.text - << event.isText; - argument.endStructure(); - return argument; -} - -const QDBusArgument &operator>>(const QDBusArgument &argument, QSpiDeviceEvent &event) { - argument.beginStructure(); - argument >> event.type - >> event.id - >> event.hardwareCode - >> event.modifiers - >> event.timestamp - >> event.text - >> event.isText; - argument.endStructure(); - return argument; -} - -void qSpiInitializeStructTypes() -{ - qDBusRegisterMetaType(); - qDBusRegisterMetaType(); - qDBusRegisterMetaType(); - qDBusRegisterMetaType(); - qDBusRegisterMetaType(); - qDBusRegisterMetaType(); - qDBusRegisterMetaType(); - qDBusRegisterMetaType(); - qDBusRegisterMetaType(); - qDBusRegisterMetaType(); - qDBusRegisterMetaType(); - qDBusRegisterMetaType(); - qDBusRegisterMetaType(); - qDBusRegisterMetaType(); - qDBusRegisterMetaType(); -} - -QT_END_NAMESPACE -#endif //QT_NO_ACCESSIBILITY diff --git a/src/platformsupport/linuxaccessibility/struct_marshallers_p.h b/src/platformsupport/linuxaccessibility/struct_marshallers_p.h deleted file mode 100644 index c8cc05ab5b..0000000000 --- a/src/platformsupport/linuxaccessibility/struct_marshallers_p.h +++ /dev/null @@ -1,197 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - - -#ifndef Q_SPI_STRUCT_MARSHALLERS_H -#define Q_SPI_STRUCT_MARSHALLERS_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include -#include -#include -#include -#include -#include - -QT_REQUIRE_CONFIG(accessibility); - -QT_BEGIN_NAMESPACE - -typedef QVector QSpiIntList; -typedef QVector QSpiUIntList; - -// FIXME: make this copy on write -struct QSpiObjectReference -{ - QString service; - QDBusObjectPath path; - - QSpiObjectReference(); - QSpiObjectReference(const QDBusConnection& connection, const QDBusObjectPath& path) - : service(connection.baseService()), path(path) {} -}; -Q_DECLARE_TYPEINFO(QSpiObjectReference, Q_MOVABLE_TYPE); // QDBusObjectPath is movable, even though it - // cannot be marked that way until Qt 6 - -QDBusArgument &operator<<(QDBusArgument &argument, const QSpiObjectReference &address); -const QDBusArgument &operator>>(const QDBusArgument &argument, QSpiObjectReference &address); - -typedef QVector QSpiObjectReferenceArray; - -struct QSpiAccessibleCacheItem -{ - QSpiObjectReference path; - QSpiObjectReference application; - QSpiObjectReference parent; - QSpiObjectReferenceArray children; - QStringList supportedInterfaces; - QString name; - uint role; - QString description; - QSpiUIntList state; -}; -Q_DECLARE_TYPEINFO(QSpiAccessibleCacheItem, Q_MOVABLE_TYPE); - -typedef QVector QSpiAccessibleCacheArray; - -QDBusArgument &operator<<(QDBusArgument &argument, const QSpiAccessibleCacheItem &item); -const QDBusArgument &operator>>(const QDBusArgument &argument, QSpiAccessibleCacheItem &item); - -struct QSpiAction -{ - QString name; - QString description; - QString keyBinding; -}; -Q_DECLARE_TYPEINFO(QSpiAction, Q_MOVABLE_TYPE); - -typedef QVector QSpiActionArray; - -QDBusArgument &operator<<(QDBusArgument &argument, const QSpiAction &action); -const QDBusArgument &operator>>(const QDBusArgument &argument, QSpiAction &action); - -struct QSpiEventListener -{ - QString listenerAddress; - QString eventName; -}; -Q_DECLARE_TYPEINFO(QSpiEventListener, Q_MOVABLE_TYPE); - -typedef QVector QSpiEventListenerArray; - -QDBusArgument &operator<<(QDBusArgument &argument, const QSpiEventListener &action); -const QDBusArgument &operator>>(const QDBusArgument &argument, QSpiEventListener &action); - -typedef QPair QSpiRelationArrayEntry; -typedef QVector QSpiRelationArray; - -//a(iisv) -struct QSpiTextRange { - int startOffset; - int endOffset; - QString contents; - QVariant v; -}; -Q_DECLARE_TYPEINFO(QSpiTextRange, Q_MOVABLE_TYPE); - -typedef QVector QSpiTextRangeList; -typedef QMap QSpiAttributeSet; - -enum QSpiAppUpdateType { - QSPI_APP_UPDATE_ADDED = 0, - QSPI_APP_UPDATE_REMOVED = 1 -}; -Q_DECLARE_TYPEINFO(QSpiAppUpdateType, Q_PRIMITIVE_TYPE); - -struct QSpiAppUpdate { - int type; /* Is an application added or removed */ - QString address; /* D-Bus address of application added or removed */ -}; -Q_DECLARE_TYPEINFO(QSpiAppUpdate, Q_MOVABLE_TYPE); - -QDBusArgument &operator<<(QDBusArgument &argument, const QSpiAppUpdate &update); -const QDBusArgument &operator>>(const QDBusArgument &argument, QSpiAppUpdate &update); - -struct QSpiDeviceEvent { - unsigned int type; - int id; - int hardwareCode; - int modifiers; - int timestamp; - QString text; - bool isText; -}; -Q_DECLARE_TYPEINFO(QSpiDeviceEvent, Q_MOVABLE_TYPE); - -QDBusArgument &operator<<(QDBusArgument &argument, const QSpiDeviceEvent &event); -const QDBusArgument &operator>>(const QDBusArgument &argument, QSpiDeviceEvent &event); - -void qSpiInitializeStructTypes(); - -QT_END_NAMESPACE - -Q_DECLARE_METATYPE(QSpiIntList) -Q_DECLARE_METATYPE(QSpiUIntList) -Q_DECLARE_METATYPE(QSpiObjectReference) -Q_DECLARE_METATYPE(QSpiObjectReferenceArray) -Q_DECLARE_METATYPE(QSpiAccessibleCacheItem) -Q_DECLARE_METATYPE(QSpiAccessibleCacheArray) -Q_DECLARE_METATYPE(QSpiAction) -Q_DECLARE_METATYPE(QSpiActionArray) -Q_DECLARE_METATYPE(QSpiEventListener) -Q_DECLARE_METATYPE(QSpiEventListenerArray) -Q_DECLARE_METATYPE(QSpiRelationArrayEntry) -Q_DECLARE_METATYPE(QSpiRelationArray) -Q_DECLARE_METATYPE(QSpiTextRange) -Q_DECLARE_METATYPE(QSpiTextRangeList) -Q_DECLARE_METATYPE(QSpiAttributeSet) -Q_DECLARE_METATYPE(QSpiAppUpdate) -Q_DECLARE_METATYPE(QSpiDeviceEvent) - -#endif /* Q_SPI_STRUCT_MARSHALLERS_H */ diff --git a/src/platformsupport/platformsupport.pro b/src/platformsupport/platformsupport.pro index 76ed9da61a..5a73a0edfb 100644 --- a/src/platformsupport/platformsupport.pro +++ b/src/platformsupport/platformsupport.pro @@ -17,12 +17,6 @@ qtConfig(xlib):qtConfig(opengl):!qtConfig(opengles2): \ qtConfig(kms): \ SUBDIRS += kmsconvenience -qtConfig(accessibility) { - qtConfig(accessibility-atspi-bridge) { - SUBDIRS += linuxaccessibility - } -} - !android:linux*:qtHaveModule(dbus) \ SUBDIRS += linuxofono diff --git a/src/plugins/platforms/xcb/.prev_CMakeLists.txt b/src/plugins/platforms/xcb/.prev_CMakeLists.txt index 7f0a0b01d0..0cf401037e 100644 --- a/src/plugins/platforms/xcb/.prev_CMakeLists.txt +++ b/src/plugins/platforms/xcb/.prev_CMakeLists.txt @@ -67,11 +67,6 @@ qt_extend_target(XcbQpa CONDITION QT_FEATURE_opengl Qt::OpenGLPrivate ) -qt_extend_target(XcbQpa CONDITION TARGET Qt::LinuxAccessibilitySupportPrivate - PUBLIC_LIBRARIES - Qt::LinuxAccessibilitySupportPrivate -) - qt_extend_target(XcbQpa CONDITION QT_FEATURE_glib LIBRARIES GLIB2::GLIB2 @@ -149,12 +144,12 @@ qt_internal_add_plugin(QXcbIntegrationPlugin Qt::XcbQpaPrivate ) -#### Keys ignored in scope 19:.:.:xcb-plugin.pro:: +#### Keys ignored in scope 18:.:.:xcb-plugin.pro:: # OTHER_FILES = "xcb.json" "README" ## Scopes: ##################################################################### -#### Keys ignored in scope 21:.:.:xcb-plugin.pro:NOT TARGET___equals____ss_QT_DEFAULT_QPA_PLUGIN: +#### Keys ignored in scope 20:.:.:xcb-plugin.pro:NOT TARGET___equals____ss_QT_DEFAULT_QPA_PLUGIN: # PLUGIN_EXTENDS = "-" add_subdirectory(gl_integrations) diff --git a/src/plugins/platforms/xcb/CMakeLists.txt b/src/plugins/platforms/xcb/CMakeLists.txt index a7c2d5610a..22a955fec6 100644 --- a/src/plugins/platforms/xcb/CMakeLists.txt +++ b/src/plugins/platforms/xcb/CMakeLists.txt @@ -70,11 +70,6 @@ qt_extend_target(XcbQpa CONDITION QT_FEATURE_opengl Qt::OpenGLPrivate ) -qt_extend_target(XcbQpa CONDITION TARGET Qt::LinuxAccessibilitySupportPrivate - PUBLIC_LIBRARIES - Qt::LinuxAccessibilitySupportPrivate -) - qt_extend_target(XcbQpa CONDITION QT_FEATURE_glib LIBRARIES GLIB2::GLIB2 @@ -176,13 +171,13 @@ qt_internal_add_plugin(QXcbIntegrationPlugin Qt::XcbQpaPrivate ) -#### Keys ignored in scope 19:.:.:xcb-plugin.pro:: +#### Keys ignored in scope 18:.:.:xcb-plugin.pro:: # OTHER_FILES = "xcb.json" "README" ## Scopes: ##################################################################### -#### Keys ignored in scope 21:.:.:xcb-plugin.pro:NOT TARGET___equals____ss_QT_DEFAULT_QPA_PLUGIN: +#### Keys ignored in scope 20:.:.:xcb-plugin.pro:NOT TARGET___equals____ss_QT_DEFAULT_QPA_PLUGIN: # PLUGIN_EXTENDS = "-" add_subdirectory(gl_integrations) if(OFF) diff --git a/src/plugins/platforms/xcb/qxcbintegration.cpp b/src/plugins/platforms/xcb/qxcbintegration.cpp index fd64185852..d93c7b3268 100644 --- a/src/plugins/platforms/xcb/qxcbintegration.cpp +++ b/src/plugins/platforms/xcb/qxcbintegration.cpp @@ -90,7 +90,7 @@ #ifndef QT_NO_ACCESSIBILITY #include #ifndef QT_NO_ACCESSIBILITY_ATSPI_BRIDGE -#include +#include #endif #endif diff --git a/src/plugins/platforms/xcb/xcb_qpa_lib.pro b/src/plugins/platforms/xcb/xcb_qpa_lib.pro index 809ae2b212..a146b62e17 100644 --- a/src/plugins/platforms/xcb/xcb_qpa_lib.pro +++ b/src/plugins/platforms/xcb/xcb_qpa_lib.pro @@ -7,9 +7,6 @@ QT += \ qtConfig(opengl): QT += opengl-private -qtHaveModule(linuxaccessibility_support-private): \ - QT += linuxaccessibility_support-private - qtConfig(glib) : QMAKE_USE_PRIVATE += glib SOURCES = \ diff --git a/src/src.pro b/src/src.pro index da827979d2..8179815a94 100644 --- a/src/src.pro +++ b/src/src.pro @@ -182,7 +182,7 @@ qtConfig(dbus) { qtConfig(accessibility-atspi-bridge): \ src_platformsupport.depends += src_dbus src_tools_qdbusxml2cpp src_plugins.depends += src_dbus src_tools_qdbusxml2cpp src_tools_qdbuscpp2xml - src_gui.depends += src_dbus + src_gui.depends += src_dbus src_tools_qdbusxml2cpp } android { -- cgit v1.2.3