1 /****************************************************************************
2 **
3 ** Copyright (C) 2017 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the plugins of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include <QtGui/qtguiglobal.h>
41 #if QT_CONFIG(accessibility)
42 
43 #include "qwindowsuiaaccessibility.h"
44 #include "qwindowsuiamainprovider.h"
45 #include "qwindowsuiautils.h"
46 
47 #include <QtGui/qaccessible.h>
48 #include <QtGui/qwindow.h>
49 #include <QtGui/qguiapplication.h>
50 #include <QtGui/private/qguiapplication_p.h>
51 #include <QtCore/qt_windows.h>
52 #include <qpa/qplatformintegration.h>
53 #include <QtWindowsUIAutomationSupport/private/qwindowsuiawrapper_p.h>
54 
55 #include <QtCore/private/qwinregistry_p.h>
56 
57 QT_BEGIN_NAMESPACE
58 
59 using namespace QWindowsUiAutomation;
60 
61 
QWindowsUiaAccessibility()62 QWindowsUiaAccessibility::QWindowsUiaAccessibility()
63 {
64 }
65 
~QWindowsUiaAccessibility()66 QWindowsUiaAccessibility::~QWindowsUiaAccessibility()
67 {
68 }
69 
70 // Handles UI Automation window messages.
handleWmGetObject(HWND hwnd,WPARAM wParam,LPARAM lParam,LRESULT * lResult)71 bool QWindowsUiaAccessibility::handleWmGetObject(HWND hwnd, WPARAM wParam, LPARAM lParam, LRESULT *lResult)
72 {
73     // Start handling accessibility internally
74     QGuiApplicationPrivate::platformIntegration()->accessibility()->setActive(true);
75 
76     // Ignoring all requests while starting up / shutting down
77     if (QCoreApplication::startingUp() || QCoreApplication::closingDown())
78         return false;
79 
80     if (QWindow *window = QWindowsContext::instance()->findWindow(hwnd)) {
81         if (QAccessibleInterface *accessible = window->accessibleRoot()) {
82             QWindowsUiaMainProvider *provider = QWindowsUiaMainProvider::providerForAccessible(accessible);
83             *lResult = QWindowsUiaWrapper::instance()->returnRawElementProvider(hwnd, wParam, lParam, provider);
84             return true;
85         }
86     }
87     return false;
88 }
89 
90 // Retrieve sound name by checking the icon property of a message box
91 // should it be the event object.
alertSound(const QObject * object)92 static QString alertSound(const QObject *object)
93 {
94     if (object->inherits("QMessageBox")) {
95         enum MessageBoxIcon { // Keep in sync with QMessageBox::Icon
96             Information = 1,
97             Warning = 2,
98             Critical = 3
99         };
100         switch (object->property("icon").toInt()) {
101         case Information:
102             return QStringLiteral("SystemAsterisk");
103         case Warning:
104             return QStringLiteral("SystemExclamation");
105         case Critical:
106             return QStringLiteral("SystemHand");
107         }
108         return QString();
109     }
110     return QStringLiteral("SystemAsterisk");
111 }
112 
soundFileName(const QString & soundName)113 static QString soundFileName(const QString &soundName)
114 {
115     const QString key = QStringLiteral("AppEvents\\Schemes\\Apps\\.Default\\")
116         + soundName + QStringLiteral("\\.Current");
117     return QWinRegistryKey(HKEY_CURRENT_USER, key).stringValue(L"");
118 }
119 
playSystemSound(const QString & soundName)120 static void playSystemSound(const QString &soundName)
121 {
122     if (!soundName.isEmpty() && !soundFileName(soundName).isEmpty()) {
123         PlaySound(reinterpret_cast<const wchar_t *>(soundName.utf16()), nullptr,
124                   SND_ALIAS | SND_ASYNC | SND_NODEFAULT | SND_NOWAIT);
125     }
126 }
127 
128 // Handles accessibility update notifications.
notifyAccessibilityUpdate(QAccessibleEvent * event)129 void QWindowsUiaAccessibility::notifyAccessibilityUpdate(QAccessibleEvent *event)
130 {
131     if (!event)
132         return;
133 
134     switch (event->type()) {
135         case QAccessible::PopupMenuStart:
136             playSystemSound(QStringLiteral("MenuPopup"));
137             break;
138         case QAccessible::MenuCommand:
139             playSystemSound(QStringLiteral("MenuCommand"));
140             break;
141         case QAccessible::Alert:
142             playSystemSound(alertSound(event->object()));
143             break;
144         default:
145             break;
146     }
147 
148     QAccessibleInterface *accessible = event->accessibleInterface();
149     if (!isActive() || !accessible || !accessible->isValid())
150         return;
151 
152     // Ensures QWindowsUiaWrapper is properly initialized.
153     if (!QWindowsUiaWrapper::instance()->ready())
154         return;
155 
156     // No need to do anything when nobody is listening.
157     if (!QWindowsUiaWrapper::instance()->clientsAreListening())
158         return;
159 
160     switch (event->type()) {
161     case QAccessible::Focus:
162         QWindowsUiaMainProvider::notifyFocusChange(event);
163         break;
164     case QAccessible::StateChanged:
165         QWindowsUiaMainProvider::notifyStateChange(static_cast<QAccessibleStateChangeEvent *>(event));
166         break;
167     case QAccessible::ValueChanged:
168         QWindowsUiaMainProvider::notifyValueChange(static_cast<QAccessibleValueChangeEvent *>(event));
169         break;
170     case QAccessible::NameChanged:
171         QWindowsUiaMainProvider::notifyNameChange(event);
172         break;
173     case QAccessible::SelectionAdd:
174         QWindowsUiaMainProvider::notifySelectionChange(event);
175         break;
176     case QAccessible::TextAttributeChanged:
177     case QAccessible::TextColumnChanged:
178     case QAccessible::TextInserted:
179     case QAccessible::TextRemoved:
180     case QAccessible::TextUpdated:
181     case QAccessible::TextSelectionChanged:
182     case QAccessible::TextCaretMoved:
183         QWindowsUiaMainProvider::notifyTextChange(event);
184         break;
185     default:
186         break;
187     }
188 }
189 
190 QT_END_NAMESPACE
191 
192 #endif // QT_CONFIG(accessibility)
193