1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4
5#include "qspiapplicationadaptor_p.h"
6
7#include <QtCore/qcoreapplication.h>
8#include <QtDBus/qdbuspendingreply.h>
9#include <qdebug.h>
10
11#if QT_CONFIG(accessibility)
12#include "deviceeventcontroller_adaptor.h"
13#include "atspi/atspi-constants.h"
14
15#include <xcb/xproto.h>
16
17//#define KEYBOARD_DEBUG
18
19QT_BEGIN_NAMESPACE
20
21/*!
22 \class QSpiApplicationAdaptor
23 \internal
24
25 \brief QSpiApplicationAdaptor
26
27 QSpiApplicationAdaptor
28*/
29
30QSpiApplicationAdaptor::QSpiApplicationAdaptor(const QDBusConnection &connection, QObject *parent)
31 : QObject(parent), dbusConnection(connection)
32{
33}
34
35enum QSpiKeyEventType {
36 QSPI_KEY_EVENT_PRESS,
37 QSPI_KEY_EVENT_RELEASE,
38 QSPI_KEY_EVENT_LAST_DEFINED
39};
40
41void QSpiApplicationAdaptor::sendEvents(bool active)
42{
43 if (active) {
44 qApp->installEventFilter(filterObj: this);
45 } else {
46 qApp->removeEventFilter(obj: this);
47 }
48}
49
50
51bool QSpiApplicationAdaptor::eventFilter(QObject *target, QEvent *event)
52{
53 if (!event->spontaneous())
54 return false;
55
56 switch (event->type()) {
57 case QEvent::WindowActivate:
58 emit windowActivated(window: target, active: true);
59 break;
60 case QEvent::WindowDeactivate:
61 emit windowActivated(window: target, active: false);
62 break;
63 case QEvent::KeyPress:
64 case QEvent::KeyRelease: {
65 QKeyEvent *keyEvent = static_cast <QKeyEvent *>(event);
66 QSpiDeviceEvent de;
67
68 if (event->type() == QEvent::KeyPress)
69 de.type = QSPI_KEY_EVENT_PRESS;
70 else
71 de.type = QSPI_KEY_EVENT_RELEASE;
72
73 de.id = keyEvent->nativeVirtualKey();
74 de.hardwareCode = keyEvent->nativeScanCode();
75
76 de.timestamp = QDateTime::currentMSecsSinceEpoch();
77
78 if (keyEvent->key() == Qt::Key_Tab)
79 de.text = QStringLiteral("Tab");
80 else if (keyEvent->key() == Qt::Key_Backtab)
81 de.text = QStringLiteral("Backtab");
82 else if (keyEvent->key() == Qt::Key_Control)
83 de.text = QStringLiteral("Control_L");
84 else if (keyEvent->key() == Qt::Key_Left)
85 de.text = (keyEvent->modifiers() & Qt::KeypadModifier) ? QStringLiteral("KP_Left") : QStringLiteral("Left");
86 else if (keyEvent->key() == Qt::Key_Right)
87 de.text = (keyEvent->modifiers() & Qt::KeypadModifier) ? QStringLiteral("KP_Right") : QStringLiteral("Right");
88 else if (keyEvent->key() == Qt::Key_Up)
89 de.text = (keyEvent->modifiers() & Qt::KeypadModifier) ? QStringLiteral("KP_Up") : QStringLiteral("Up");
90 else if (keyEvent->key() == Qt::Key_Down)
91 de.text = (keyEvent->modifiers() & Qt::KeypadModifier) ? QStringLiteral("KP_Down") : QStringLiteral("Down");
92 else if (keyEvent->key() == Qt::Key_Enter || keyEvent->key() == Qt::Key_Return)
93 de.text = QStringLiteral("Return");
94 else if (keyEvent->key() == Qt::Key_Backspace)
95 de.text = QStringLiteral("BackSpace");
96 else if (keyEvent->key() == Qt::Key_Delete)
97 de.text = QStringLiteral("Delete");
98 else if (keyEvent->key() == Qt::Key_PageUp)
99 de.text = (keyEvent->modifiers() & Qt::KeypadModifier) ? QStringLiteral("KP_Page_Up") : QStringLiteral("Page_Up");
100 else if (keyEvent->key() == Qt::Key_PageDown)
101 de.text = (keyEvent->modifiers() & Qt::KeypadModifier) ? QStringLiteral("KP_Page_Up") : QStringLiteral("Page_Down");
102 else if (keyEvent->key() == Qt::Key_Home)
103 de.text = (keyEvent->modifiers() & Qt::KeypadModifier) ? QStringLiteral("KP_Home") : QStringLiteral("Home");
104 else if (keyEvent->key() == Qt::Key_End)
105 de.text = (keyEvent->modifiers() & Qt::KeypadModifier) ? QStringLiteral("KP_End") : QStringLiteral("End");
106 else if (keyEvent->key() == Qt::Key_Clear && (keyEvent->modifiers() & Qt::KeypadModifier))
107 de.text = QStringLiteral("KP_Begin"); // Key pad 5
108 else if (keyEvent->key() == Qt::Key_Escape)
109 de.text = QStringLiteral("Escape");
110 else if (keyEvent->key() == Qt::Key_Space)
111 de.text = QStringLiteral("space");
112 else if (keyEvent->key() == Qt::Key_CapsLock)
113 de.text = QStringLiteral("Caps_Lock");
114 else if (keyEvent->key() == Qt::Key_NumLock)
115 de.text = QStringLiteral("Num_Lock");
116 else if (keyEvent->key() == Qt::Key_Insert)
117 de.text = QStringLiteral("Insert");
118 else
119 de.text = keyEvent->text();
120
121 // This is a bit dubious, Gnome uses some gtk function here.
122 // Long term the spec will hopefully change to just use keycodes.
123 de.isText = !de.text.isEmpty();
124
125 de.modifiers = 0;
126 if ((keyEvent->modifiers() & Qt::ShiftModifier) && (keyEvent->key() != Qt::Key_Shift))
127 de.modifiers |= 1 << ATSPI_MODIFIER_SHIFT;
128 // TODO rather introduce Qt::CapslockModifier into KeyboardModifier
129 if (keyEvent->nativeModifiers() & XCB_MOD_MASK_LOCK )
130 de.modifiers |= 1 << ATSPI_MODIFIER_SHIFTLOCK;
131 if ((keyEvent->modifiers() & Qt::ControlModifier) && (keyEvent->key() != Qt::Key_Control))
132 de.modifiers |= 1 << ATSPI_MODIFIER_CONTROL;
133 if ((keyEvent->modifiers() & Qt::AltModifier) && (keyEvent->key() != Qt::Key_Alt))
134 de.modifiers |= 1 << ATSPI_MODIFIER_ALT;
135 if ((keyEvent->modifiers() & Qt::MetaModifier) && (keyEvent->key() != Qt::Key_Meta))
136 de.modifiers |= 1 << ATSPI_MODIFIER_META;
137
138#ifdef KEYBOARD_DEBUG
139 qDebug() << "Key event text:" << event->type() << de.text
140 << "native virtual key:" << de.id
141 << "hardware code/scancode:" << de.hardwareCode
142 << "modifiers:" << de.modifiers
143 << "text:" << de.text;
144#endif
145
146 QDBusMessage m = QDBusMessage::createMethodCall(QStringLiteral("org.a11y.atspi.Registry"),
147 QStringLiteral("/org/a11y/atspi/registry/deviceeventcontroller"),
148 QStringLiteral("org.a11y.atspi.DeviceEventController"), QStringLiteral("NotifyListenersSync"));
149 m.setArguments(QVariantList() << QVariant::fromValue(de));
150
151 // FIXME: this is critical, the timeout should probably be pretty low to allow normal processing
152 int timeout = 100;
153 bool sent = dbusConnection.callWithCallback(message: m, receiver: this, SLOT(notifyKeyboardListenerCallback(QDBusMessage)),
154 SLOT(notifyKeyboardListenerError(QDBusError,QDBusMessage)), timeout);
155 if (sent) {
156 //queue the event and send it after callback
157 keyEvents.enqueue(t: QPair<QPointer<QObject>, QKeyEvent*> (QPointer<QObject>(target), copyKeyEvent(keyEvent)));
158 return true;
159 }
160 }
161 default:
162 break;
163 }
164 return false;
165}
166
167QKeyEvent* QSpiApplicationAdaptor::copyKeyEvent(QKeyEvent* old)
168{
169 return new QKeyEvent(old->type(), old->key(), old->modifiers(),
170 old->nativeScanCode(), old->nativeVirtualKey(), old->nativeModifiers(),
171 old->text(), old->isAutoRepeat(), old->count());
172}
173
174void QSpiApplicationAdaptor::notifyKeyboardListenerCallback(const QDBusMessage& message)
175{
176 if (!keyEvents.size()) {
177 qWarning(msg: "QSpiApplication::notifyKeyboardListenerCallback with no queued key called");
178 return;
179 }
180 Q_ASSERT(message.arguments().size() == 1);
181 if (message.arguments().at(i: 0).toBool() == true) {
182 QPair<QPointer<QObject>, QKeyEvent*> event = keyEvents.dequeue();
183 delete event.second;
184 } else {
185 QPair<QPointer<QObject>, QKeyEvent*> event = keyEvents.dequeue();
186 if (event.first)
187 QCoreApplication::postEvent(receiver: event.first.data(), event: event.second);
188 }
189}
190
191void QSpiApplicationAdaptor::notifyKeyboardListenerError(const QDBusError& error, const QDBusMessage& /*message*/)
192{
193 qWarning() << "QSpiApplication::keyEventError " << error.name() << error.message();
194 while (!keyEvents.isEmpty()) {
195 QPair<QPointer<QObject>, QKeyEvent*> event = keyEvents.dequeue();
196 if (event.first)
197 QCoreApplication::postEvent(receiver: event.first.data(), event: event.second);
198 }
199}
200
201QT_END_NAMESPACE
202
203#include "moc_qspiapplicationadaptor_p.cpp"
204
205#endif // QT_CONFIG(accessibility)
206

source code of qtbase/src/gui/accessible/linux/qspiapplicationadaptor.cpp