1 | // Copyright (C) 2020 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 | #include "qwindowsysteminterface.h" |
4 | #include <qpa/qplatformwindow.h> |
5 | #include "qwindowsysteminterface_p.h" |
6 | #include "private/qguiapplication_p.h" |
7 | #include "private/qevent_p.h" |
8 | #include "private/qeventpoint_p.h" |
9 | #include "private/qpointingdevice_p.h" |
10 | #include "private/qscreen_p.h" |
11 | #include <QAbstractEventDispatcher> |
12 | #include <qpa/qplatformintegration.h> |
13 | #include <qdebug.h> |
14 | #include "qhighdpiscaling_p.h" |
15 | |
16 | #include <QtCore/qscopedvaluerollback.h> |
17 | #include <QtCore/private/qlocking_p.h> |
18 | |
19 | #if QT_CONFIG(draganddrop) |
20 | #include <qpa/qplatformdrag.h> |
21 | #endif |
22 | |
23 | QT_BEGIN_NAMESPACE |
24 | |
25 | using namespace Qt::StringLiterals; |
26 | |
27 | Q_LOGGING_CATEGORY(lcQpaInputDevices, "qt.qpa.input.devices" ) |
28 | |
29 | Q_CONSTINIT QElapsedTimer QWindowSystemInterfacePrivate::eventTime; |
30 | bool QWindowSystemInterfacePrivate::synchronousWindowSystemEvents = false; |
31 | bool QWindowSystemInterfacePrivate::TabletEvent::platformSynthesizesMouse = true; |
32 | QWaitCondition QWindowSystemInterfacePrivate::eventsFlushed; |
33 | Q_CONSTINIT QMutex QWindowSystemInterfacePrivate::flushEventMutex; |
34 | Q_CONSTINIT QAtomicInt QWindowSystemInterfacePrivate::eventAccepted; |
35 | QWindowSystemEventHandler *QWindowSystemInterfacePrivate::eventHandler; |
36 | QWindowSystemInterfacePrivate::WindowSystemEventList QWindowSystemInterfacePrivate::windowSystemEventQueue; |
37 | |
38 | extern QPointer<QWindow> qt_last_mouse_receiver; |
39 | |
40 | |
41 | // ------------------- QWindowSystemInterfacePrivate ------------------- |
42 | |
43 | /*! |
44 | \internal |
45 | |
46 | The QWindowSystemHelper creates synchronously delivered events on the stack, unless |
47 | the calling thread is not the Gui thread. |
48 | |
49 | Asynchronously delivered events, and events created outside the Gui thread are |
50 | allocated on the heap. |
51 | */ |
52 | |
53 | template<typename Delivery> |
54 | struct QWindowSystemHelper |
55 | { |
56 | template<typename EventType, typename ...Args> |
57 | static bool handleEvent(Args ...); |
58 | }; |
59 | |
60 | /* |
61 | Handles a window system event. |
62 | |
63 | By default this function posts the event on the window system event queue and |
64 | wakes the Gui event dispatcher. Qt Gui will then handle the event asynchronously |
65 | at a later point. The return value is not used in asynchronous mode and will |
66 | always be true. |
67 | |
68 | In synchronous mode Qt Gui will process the event immediately. The return value |
69 | indicates if Qt accepted the event. If the event is delivered from another thread |
70 | than the Qt main thread the window system event queue is flushed, which may deliver |
71 | other events as well. |
72 | |
73 | \sa flushWindowSystemEvents(), setSynchronousWindowSystemEvents() |
74 | */ |
75 | template<> |
76 | template<typename EventType, typename ...Args> |
77 | bool QWindowSystemHelper<QWindowSystemInterface::DefaultDelivery>::handleEvent(Args ...args) |
78 | { |
79 | return QWindowSystemInterfacePrivate::synchronousWindowSystemEvents |
80 | ? QWindowSystemHelper<QWindowSystemInterface::SynchronousDelivery>::handleEvent<EventType>(args...) |
81 | : QWindowSystemHelper<QWindowSystemInterface::AsynchronousDelivery>::handleEvent<EventType>(args...); |
82 | } |
83 | |
84 | /* |
85 | Handles a window system event synchronously. |
86 | |
87 | Qt Gui will process the event immediately. The return value indicates if Qt |
88 | accepted the event. |
89 | |
90 | If the event is delivered from another thread than the Qt main thread the |
91 | window system event queue is flushed, which may deliver other events as |
92 | well. |
93 | */ |
94 | template<> |
95 | template<typename EventType, typename ...Args> |
96 | bool QWindowSystemHelper<QWindowSystemInterface::SynchronousDelivery>::handleEvent(Args ...args) |
97 | { |
98 | if (QThread::currentThread() == QGuiApplication::instance()->thread()) { |
99 | EventType event(args...); |
100 | // Process the event immediately on the Gui thread and return the accepted state |
101 | if (QWindowSystemInterfacePrivate::eventHandler) { |
102 | if (!QWindowSystemInterfacePrivate::eventHandler->sendEvent(event: &event)) |
103 | return false; |
104 | } else { |
105 | QGuiApplicationPrivate::processWindowSystemEvent(e: &event); |
106 | } |
107 | return event.eventAccepted; |
108 | } else { |
109 | // Post the event on the Qt main thread queue and flush the queue. |
110 | // This will wake up the Gui thread which will process the event. |
111 | // Return the accepted state for the last event on the queue, |
112 | // which is the event posted by this function. |
113 | QWindowSystemHelper<QWindowSystemInterface::AsynchronousDelivery>::handleEvent<EventType>(args...); |
114 | return QWindowSystemInterface::flushWindowSystemEvents(); |
115 | } |
116 | } |
117 | |
118 | /* |
119 | Handles a window system event asynchronously by posting the event to Qt Gui. |
120 | |
121 | This function posts the event on the window system event queue and wakes the |
122 | Gui event dispatcher. Qt Gui will then handle the event asynchronously at a |
123 | later point. |
124 | */ |
125 | template<> |
126 | template<typename EventType, typename ...Args> |
127 | bool QWindowSystemHelper<QWindowSystemInterface::AsynchronousDelivery>::handleEvent(Args ...args) |
128 | { |
129 | QWindowSystemInterfacePrivate::windowSystemEventQueue.append(e: new EventType(args...)); |
130 | if (QAbstractEventDispatcher *dispatcher = QGuiApplicationPrivate::qt_qpa_core_dispatcher()) |
131 | dispatcher->wakeUp(); |
132 | return true; |
133 | } |
134 | |
135 | template <typename EventType, typename Delivery = QWindowSystemInterface::DefaultDelivery, typename ...Args> |
136 | static bool handleWindowSystemEvent(Args ...args) |
137 | { |
138 | return QWindowSystemHelper<Delivery>::template handleEvent<EventType>(args...); |
139 | } |
140 | |
141 | qsizetype QWindowSystemInterfacePrivate::windowSystemEventsQueued() |
142 | { |
143 | return windowSystemEventQueue.count(); |
144 | } |
145 | |
146 | bool QWindowSystemInterfacePrivate::nonUserInputEventsQueued() |
147 | { |
148 | return windowSystemEventQueue.nonUserInputEventsQueued(); |
149 | } |
150 | |
151 | QWindowSystemInterfacePrivate::WindowSystemEvent * QWindowSystemInterfacePrivate::getWindowSystemEvent() |
152 | { |
153 | return windowSystemEventQueue.takeFirstOrReturnNull(); |
154 | } |
155 | |
156 | QWindowSystemInterfacePrivate::WindowSystemEvent *QWindowSystemInterfacePrivate::getNonUserInputWindowSystemEvent() |
157 | { |
158 | return windowSystemEventQueue.takeFirstNonUserInputOrReturnNull(); |
159 | } |
160 | |
161 | QWindowSystemInterfacePrivate::WindowSystemEvent *QWindowSystemInterfacePrivate::peekWindowSystemEvent(EventType t) |
162 | { |
163 | return windowSystemEventQueue.peekAtFirstOfType(t); |
164 | } |
165 | |
166 | void QWindowSystemInterfacePrivate::removeWindowSystemEvent(WindowSystemEvent *event) |
167 | { |
168 | windowSystemEventQueue.remove(e: event); |
169 | } |
170 | |
171 | void QWindowSystemInterfacePrivate::installWindowSystemEventHandler(QWindowSystemEventHandler *handler) |
172 | { |
173 | if (!eventHandler) |
174 | eventHandler = handler; |
175 | } |
176 | |
177 | void QWindowSystemInterfacePrivate::removeWindowSystemEventhandler(QWindowSystemEventHandler *handler) |
178 | { |
179 | if (eventHandler == handler) |
180 | eventHandler = nullptr; |
181 | } |
182 | |
183 | QWindowSystemEventHandler::~QWindowSystemEventHandler() |
184 | { |
185 | QWindowSystemInterfacePrivate::removeWindowSystemEventhandler(handler: this); |
186 | } |
187 | |
188 | bool QWindowSystemEventHandler::sendEvent(QWindowSystemInterfacePrivate::WindowSystemEvent *e) |
189 | { |
190 | QGuiApplicationPrivate::processWindowSystemEvent(e); |
191 | return true; |
192 | } |
193 | |
194 | //------------------------------------------------------------ |
195 | // |
196 | // Callback functions for plugins: |
197 | // |
198 | |
199 | #define QT_DEFINE_QPA_EVENT_HANDLER(ReturnType, HandlerName, ...) \ |
200 | template Q_GUI_EXPORT ReturnType QWindowSystemInterface::HandlerName<QWindowSystemInterface::DefaultDelivery>(__VA_ARGS__); \ |
201 | template Q_GUI_EXPORT ReturnType QWindowSystemInterface::HandlerName<QWindowSystemInterface::SynchronousDelivery>(__VA_ARGS__); \ |
202 | template Q_GUI_EXPORT ReturnType QWindowSystemInterface::HandlerName<QWindowSystemInterface::AsynchronousDelivery>(__VA_ARGS__); \ |
203 | template<typename Delivery> ReturnType QWindowSystemInterface::HandlerName(__VA_ARGS__) |
204 | |
205 | /*! |
206 | \class QWindowSystemInterface |
207 | \since 5.0 |
208 | \internal |
209 | \preliminary |
210 | \ingroup qpa |
211 | \brief The QWindowSystemInterface provides an event queue for the QPA platform. |
212 | |
213 | The platform plugins call the various functions to notify about events. The events are queued |
214 | until sendWindowSystemEvents() is called by the event dispatcher. |
215 | */ |
216 | |
217 | QT_DEFINE_QPA_EVENT_HANDLER(void, handleEnterEvent, QWindow *window, const QPointF &local, const QPointF &global) |
218 | { |
219 | if (window) { |
220 | handleWindowSystemEvent<QWindowSystemInterfacePrivate::EnterEvent, Delivery>(window, |
221 | QHighDpi::fromNativeLocalPosition(value: local, context: window), QHighDpi::fromNativeGlobalPosition(value: global, context: window)); |
222 | } |
223 | } |
224 | |
225 | QT_DEFINE_QPA_EVENT_HANDLER(void, handleLeaveEvent, QWindow *window) |
226 | { |
227 | handleWindowSystemEvent<QWindowSystemInterfacePrivate::LeaveEvent, Delivery>(window); |
228 | } |
229 | |
230 | /*! |
231 | This method can be used to ensure leave and enter events are both in queue when moving from |
232 | one QWindow to another. This allows QWindow subclasses to check for a queued enter event |
233 | when handling the leave event (\c QWindowSystemInterfacePrivate::peekWindowSystemEvent) to |
234 | determine where mouse went and act accordingly. E.g. QWidgetWindow needs to know if mouse |
235 | cursor moves between windows in same window hierarchy. |
236 | */ |
237 | void QWindowSystemInterface::handleEnterLeaveEvent(QWindow *enter, QWindow *leave, const QPointF &local, const QPointF& global) |
238 | { |
239 | handleLeaveEvent<AsynchronousDelivery>(window: leave); |
240 | handleEnterEvent(window: enter, local, global); |
241 | } |
242 | |
243 | QT_DEFINE_QPA_EVENT_HANDLER(void, handleWindowActivated, QWindow *window, Qt::FocusReason r) |
244 | { |
245 | handleWindowSystemEvent<QWindowSystemInterfacePrivate::ActivatedWindowEvent, Delivery>(window, r); |
246 | } |
247 | |
248 | QT_DEFINE_QPA_EVENT_HANDLER(void, handleWindowStateChanged, QWindow *window, Qt::WindowStates newState, int oldState) |
249 | { |
250 | Q_ASSERT(window); |
251 | if (oldState < Qt::WindowNoState) |
252 | oldState = window->windowStates(); |
253 | |
254 | handleWindowSystemEvent<QWindowSystemInterfacePrivate::WindowStateChangedEvent, Delivery>(window, newState, Qt::WindowStates(oldState)); |
255 | } |
256 | |
257 | QT_DEFINE_QPA_EVENT_HANDLER(void, handleWindowScreenChanged, QWindow *window, QScreen *screen) |
258 | { |
259 | handleWindowSystemEvent<QWindowSystemInterfacePrivate::WindowScreenChangedEvent, Delivery>(window, screen); |
260 | } |
261 | |
262 | QT_DEFINE_QPA_EVENT_HANDLER(void, handleWindowDevicePixelRatioChanged, QWindow *window) |
263 | { |
264 | handleWindowSystemEvent<QWindowSystemInterfacePrivate::WindowDevicePixelRatioChangedEvent, Delivery>(window); |
265 | } |
266 | |
267 | |
268 | QT_DEFINE_QPA_EVENT_HANDLER(void, handleSafeAreaMarginsChanged, QWindow *window) |
269 | { |
270 | handleWindowSystemEvent<QWindowSystemInterfacePrivate::SafeAreaMarginsChangedEvent, Delivery>(window); |
271 | } |
272 | |
273 | QT_DEFINE_QPA_EVENT_HANDLER(void, handleApplicationStateChanged, Qt::ApplicationState newState, bool forcePropagate) |
274 | { |
275 | Q_ASSERT(QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ApplicationState)); |
276 | handleWindowSystemEvent<QWindowSystemInterfacePrivate::ApplicationStateChangedEvent, Delivery>(newState, forcePropagate); |
277 | } |
278 | |
279 | QT_DEFINE_QPA_EVENT_HANDLER(bool, handleApplicationTermination) |
280 | { |
281 | return handleWindowSystemEvent<QWindowSystemInterfacePrivate::WindowSystemEvent, Delivery>( |
282 | QWindowSystemInterfacePrivate::ApplicationTermination); |
283 | } |
284 | |
285 | QWindowSystemInterfacePrivate::GeometryChangeEvent::GeometryChangeEvent(QWindow *window, const QRect &newGeometry) |
286 | : WindowSystemEvent(GeometryChange) |
287 | , window(window) |
288 | , newGeometry(newGeometry) |
289 | { |
290 | if (const QPlatformWindow *pw = window->handle()) { |
291 | const auto nativeGeometry = pw->QPlatformWindow::geometry(); |
292 | requestedGeometry = QHighDpi::fromNativeWindowGeometry(value: nativeGeometry, context: window); |
293 | } |
294 | } |
295 | |
296 | QT_DEFINE_QPA_EVENT_HANDLER(void, handleGeometryChange, QWindow *window, const QRect &newRect) |
297 | { |
298 | Q_ASSERT(window); |
299 | const auto newRectDi = QHighDpi::fromNativeWindowGeometry(value: newRect, context: window); |
300 | if (window->handle()) { |
301 | // Persist the new geometry so that QWindow::geometry() can be queried in the resize event |
302 | window->handle()->QPlatformWindow::setGeometry(newRect); |
303 | // FIXME: This does not work during platform window creation, where the QWindow does not |
304 | // have its handle set up yet. Platforms that deliver events during window creation need |
305 | // to handle the persistence manually, e.g. by overriding geometry(). |
306 | } |
307 | handleWindowSystemEvent<QWindowSystemInterfacePrivate::GeometryChangeEvent, Delivery>(window, newRectDi); |
308 | } |
309 | |
310 | QWindowSystemInterfacePrivate::ExposeEvent::ExposeEvent(QWindow *window, const QRegion ®ion) |
311 | : WindowSystemEvent(Expose) |
312 | , window(window) |
313 | , isExposed(window && window->handle() ? window->handle()->isExposed() : false) |
314 | , region(region) |
315 | { |
316 | } |
317 | |
318 | /*! \internal |
319 | Handles an expose event. |
320 | |
321 | The platform plugin sends expose events when an area of the window |
322 | is invalidated or window exposure changes. \a region is in window |
323 | local coordinates. An empty region indicates that the window is |
324 | obscured, but note that the exposed property of the QWindow will be set |
325 | based on what QPlatformWindow::isExposed() returns at the time of this call, |
326 | not based on what the region is. // FIXME: this should probably be fixed. |
327 | |
328 | The platform plugin may omit sending expose events (or send obscure |
329 | events) for windows that are on screen but where the client area is |
330 | completely covered by other windows or otherwise not visible. Expose |
331 | event consumers can then use this to disable updates for such windows. |
332 | This is required behavior on platforms where OpenGL swapbuffers stops |
333 | blocking for obscured windows (like macOS). |
334 | */ |
335 | QT_DEFINE_QPA_EVENT_HANDLER(bool, handleExposeEvent, QWindow *window, const QRegion ®ion) |
336 | { |
337 | return handleWindowSystemEvent<QWindowSystemInterfacePrivate::ExposeEvent, Delivery>(window, |
338 | QHighDpi::fromNativeLocalExposedRegion(pixelRegion: region, window)); |
339 | } |
340 | |
341 | QT_DEFINE_QPA_EVENT_HANDLER(bool, handlePaintEvent, QWindow *window, const QRegion ®ion) |
342 | { |
343 | return handleWindowSystemEvent<QWindowSystemInterfacePrivate::PaintEvent, Delivery>(window, |
344 | QHighDpi::fromNativeLocalExposedRegion(pixelRegion: region, window)); |
345 | } |
346 | |
347 | |
348 | QT_DEFINE_QPA_EVENT_HANDLER(bool, handleCloseEvent, QWindow *window) |
349 | { |
350 | Q_ASSERT(window); |
351 | return handleWindowSystemEvent<QWindowSystemInterfacePrivate::CloseEvent, Delivery>(window); |
352 | } |
353 | |
354 | /*! |
355 | |
356 | \a w == 0 means that the event is in global coords only, \a local will be ignored in this case |
357 | |
358 | */ |
359 | |
360 | QT_DEFINE_QPA_EVENT_HANDLER(bool, handleMouseEvent, QWindow *window, |
361 | const QPointF &local, const QPointF &global, Qt::MouseButtons state, |
362 | Qt::MouseButton button, QEvent::Type type, Qt::KeyboardModifiers mods, |
363 | Qt::MouseEventSource source) |
364 | { |
365 | unsigned long time = QWindowSystemInterfacePrivate::eventTime.elapsed(); |
366 | return handleMouseEvent<Delivery>(window, time, local, global, state, button, type, mods, source); |
367 | } |
368 | |
369 | QT_DEFINE_QPA_EVENT_HANDLER(bool, handleMouseEvent, QWindow *window, const QPointingDevice *device, |
370 | const QPointF &local, const QPointF &global, Qt::MouseButtons state, |
371 | Qt::MouseButton button, QEvent::Type type, Qt::KeyboardModifiers mods, |
372 | Qt::MouseEventSource source) |
373 | { |
374 | unsigned long time = QWindowSystemInterfacePrivate::eventTime.elapsed(); |
375 | return handleMouseEvent<Delivery>(window, time, device, local, global, state, button, type, mods, source); |
376 | } |
377 | |
378 | QT_DEFINE_QPA_EVENT_HANDLER(bool, handleMouseEvent, QWindow *window, ulong timestamp, |
379 | const QPointF &local, const QPointF &global, Qt::MouseButtons state, |
380 | Qt::MouseButton button, QEvent::Type type, Qt::KeyboardModifiers mods, |
381 | Qt::MouseEventSource source) |
382 | { |
383 | return handleMouseEvent<Delivery>(window, timestamp, QPointingDevice::primaryPointingDevice(), |
384 | local, global, state, button, type, mods, source); |
385 | } |
386 | |
387 | QT_DEFINE_QPA_EVENT_HANDLER(bool, handleMouseEvent, QWindow *window, ulong timestamp, const QPointingDevice *device, |
388 | const QPointF &local, const QPointF &global, Qt::MouseButtons state, |
389 | Qt::MouseButton button, QEvent::Type type, Qt::KeyboardModifiers mods, |
390 | Qt::MouseEventSource source) |
391 | { |
392 | |
393 | bool isNonClientArea = {}; |
394 | |
395 | switch (type) { |
396 | case QEvent::MouseButtonDblClick: |
397 | case QEvent::NonClientAreaMouseButtonDblClick: |
398 | Q_ASSERT_X(false, "QWindowSystemInterface::handleMouseEvent" , |
399 | "QTBUG-71263: Native double clicks are not implemented." ); |
400 | return false; |
401 | case QEvent::MouseMove: |
402 | case QEvent::MouseButtonPress: |
403 | case QEvent::MouseButtonRelease: |
404 | isNonClientArea = false; |
405 | break; |
406 | case QEvent::NonClientAreaMouseMove: |
407 | case QEvent::NonClientAreaMouseButtonPress: |
408 | case QEvent::NonClientAreaMouseButtonRelease: |
409 | isNonClientArea = true; |
410 | break; |
411 | default: |
412 | Q_UNREACHABLE(); |
413 | } |
414 | |
415 | auto localPos = QHighDpi::fromNativeLocalPosition(value: local, context: window); |
416 | auto globalPos = QHighDpi::fromNativeGlobalPosition(value: global, context: window); |
417 | |
418 | return handleWindowSystemEvent<QWindowSystemInterfacePrivate::MouseEvent, Delivery>(window, |
419 | timestamp, localPos, globalPos, state, mods, button, type, source, isNonClientArea, device); |
420 | } |
421 | |
422 | bool QWindowSystemInterface::handleShortcutEvent(QWindow *window, ulong timestamp, int keyCode, Qt::KeyboardModifiers modifiers, quint32 nativeScanCode, |
423 | quint32 nativeVirtualKey, quint32 nativeModifiers, const QString &text, bool autorepeat, ushort count) |
424 | { |
425 | #if QT_CONFIG(shortcut) |
426 | if (!window) |
427 | window = QGuiApplication::focusWindow(); |
428 | |
429 | QShortcutMap &shortcutMap = QGuiApplicationPrivate::instance()->shortcutMap; |
430 | if (shortcutMap.state() == QKeySequence::NoMatch) { |
431 | // Check if the shortcut is overridden by some object in the event delivery path (typically the focus object). |
432 | // If so, we should not look up the shortcut in the shortcut map, but instead deliver the event as a regular |
433 | // key event, so that the target that accepted the shortcut override event can handle it. Note that we only |
434 | // do this if the shortcut map hasn't found a partial shortcut match yet. If it has, the shortcut can not be |
435 | // overridden. |
436 | bool overridden = handleWindowSystemEvent<QWindowSystemInterfacePrivate::KeyEvent, SynchronousDelivery> |
437 | (args: window,args: timestamp, args: QEvent::ShortcutOverride, args: keyCode, args: modifiers, args: nativeScanCode, |
438 | args: nativeVirtualKey, args: nativeModifiers, args: text, args: autorepeat, args: count); |
439 | if (overridden) |
440 | return false; |
441 | } |
442 | |
443 | // The shortcut event is dispatched as a QShortcutEvent, not a QKeyEvent, but we use |
444 | // the QKeyEvent as a container for the various properties that the shortcut map needs |
445 | // to inspect to determine if a shortcut matched the keys that were pressed. |
446 | QKeyEvent keyEvent(QEvent::ShortcutOverride, keyCode, modifiers, nativeScanCode, |
447 | nativeVirtualKey, nativeModifiers, text, autorepeat, count); |
448 | |
449 | return shortcutMap.tryShortcut(e: &keyEvent); |
450 | #else |
451 | Q_UNUSED(window); |
452 | Q_UNUSED(timestamp); |
453 | Q_UNUSED(keyCode); |
454 | Q_UNUSED(modifiers); |
455 | Q_UNUSED(nativeScanCode); |
456 | Q_UNUSED(nativeVirtualKey); |
457 | Q_UNUSED(nativeModifiers); |
458 | Q_UNUSED(text); |
459 | Q_UNUSED(autorepeat); |
460 | Q_UNUSED(count); |
461 | return false; |
462 | #endif |
463 | } |
464 | |
465 | QT_DEFINE_QPA_EVENT_HANDLER(bool, handleKeyEvent, QWindow *window, QEvent::Type t, int k, Qt::KeyboardModifiers mods, const QString & text, bool autorep, ushort count) { |
466 | unsigned long time = QWindowSystemInterfacePrivate::eventTime.elapsed(); |
467 | return handleKeyEvent<Delivery>(window, time, t, k, mods, text, autorep, count); |
468 | } |
469 | |
470 | QT_DEFINE_QPA_EVENT_HANDLER(bool, handleKeyEvent, QWindow *window, ulong timestamp, QEvent::Type t, int k, Qt::KeyboardModifiers mods, const QString & text, bool autorep, ushort count) |
471 | { |
472 | return handleWindowSystemEvent<QWindowSystemInterfacePrivate::KeyEvent, Delivery>(window, |
473 | timestamp, t, k, mods, text, autorep, count); |
474 | } |
475 | |
476 | bool QWindowSystemInterface::handleExtendedKeyEvent(QWindow *window, QEvent::Type type, int key, Qt::KeyboardModifiers modifiers, |
477 | quint32 nativeScanCode, quint32 nativeVirtualKey, |
478 | quint32 nativeModifiers, |
479 | const QString& text, bool autorep, |
480 | ushort count) |
481 | { |
482 | unsigned long time = QWindowSystemInterfacePrivate::eventTime.elapsed(); |
483 | return handleExtendedKeyEvent(window, timestamp: time, type, key, modifiers, nativeScanCode, nativeVirtualKey, nativeModifiers, |
484 | text, autorep, count); |
485 | } |
486 | |
487 | bool QWindowSystemInterface::handleExtendedKeyEvent(QWindow *window, ulong timestamp, QEvent::Type type, int key, |
488 | Qt::KeyboardModifiers modifiers, |
489 | quint32 nativeScanCode, quint32 nativeVirtualKey, |
490 | quint32 nativeModifiers, |
491 | const QString& text, bool autorep, |
492 | ushort count) |
493 | { |
494 | return handleWindowSystemEvent<QWindowSystemInterfacePrivate::KeyEvent>(args: window, |
495 | args: timestamp, args: type, args: key, args: modifiers, args: nativeScanCode, args: nativeVirtualKey, args: nativeModifiers, args: text, args: autorep, args: count); |
496 | } |
497 | |
498 | bool QWindowSystemInterface::handleWheelEvent(QWindow *window, const QPointF &local, const QPointF &global, QPoint pixelDelta, QPoint angleDelta, Qt::KeyboardModifiers mods, Qt::ScrollPhase phase, Qt::MouseEventSource source) |
499 | { |
500 | unsigned long time = QWindowSystemInterfacePrivate::eventTime.elapsed(); |
501 | return handleWheelEvent(window, timestamp: time, local, global, pixelDelta, angleDelta, mods, phase, source); |
502 | } |
503 | |
504 | bool QWindowSystemInterface::handleWheelEvent(QWindow *window, ulong timestamp, const QPointF &local, const QPointF &global, QPoint pixelDelta, QPoint angleDelta, Qt::KeyboardModifiers mods, Qt::ScrollPhase phase, |
505 | Qt::MouseEventSource source, bool invertedScrolling) |
506 | { |
507 | return handleWheelEvent(window, timestamp, device: QPointingDevice::primaryPointingDevice(), local, global, |
508 | pixelDelta, angleDelta, mods, phase, source, inverted: invertedScrolling); |
509 | } |
510 | |
511 | bool QWindowSystemInterface::handleWheelEvent(QWindow *window, ulong timestamp, const QPointingDevice *device, |
512 | const QPointF &local, const QPointF &global, QPoint pixelDelta, QPoint angleDelta, |
513 | Qt::KeyboardModifiers mods, Qt::ScrollPhase phase, |
514 | Qt::MouseEventSource source, bool invertedScrolling) |
515 | { |
516 | // Qt 4 sends two separate wheel events for horizontal and vertical |
517 | // deltas. For Qt 5 we want to send the deltas in one event, but at the |
518 | // same time preserve source and behavior compatibility with Qt 4. |
519 | // |
520 | // In addition high-resolution pixel-based deltas are also supported. |
521 | // Platforms that does not support these may pass a null point here. |
522 | // Angle deltas must always be sent in addition to pixel deltas. |
523 | |
524 | // Pass Qt::ScrollBegin and Qt::ScrollEnd through |
525 | // even if the wheel delta is null. |
526 | if (angleDelta.isNull() && phase == Qt::ScrollUpdate) |
527 | return false; |
528 | |
529 | // Simple case: vertical deltas only: |
530 | if (angleDelta.y() != 0 && angleDelta.x() == 0) { |
531 | return handleWindowSystemEvent<QWindowSystemInterfacePrivate::WheelEvent>(args: window, |
532 | args: timestamp, args: QHighDpi::fromNativeLocalPosition(value: local, context: window), args: QHighDpi::fromNativeGlobalPosition(value: global, context: window), |
533 | args: pixelDelta, args: angleDelta, args: angleDelta.y(), args: Qt::Vertical, args: mods, args: phase, args: source, args: invertedScrolling, args: device); |
534 | } |
535 | |
536 | // Simple case: horizontal deltas only: |
537 | if (angleDelta.y() == 0 && angleDelta.x() != 0) { |
538 | return handleWindowSystemEvent<QWindowSystemInterfacePrivate::WheelEvent>(args: window, |
539 | args: timestamp, args: QHighDpi::fromNativeLocalPosition(value: local, context: window), args: QHighDpi::fromNativeGlobalPosition(value: global, context: window), |
540 | args: pixelDelta, args: angleDelta, args: angleDelta.x(), args: Qt::Horizontal, args: mods, args: phase, args: source, args: invertedScrolling, args: device); |
541 | } |
542 | |
543 | bool acceptVert; |
544 | bool acceptHorz; |
545 | // Both horizontal and vertical deltas: Send two wheel events. |
546 | // The first event contains the Qt 5 pixel and angle delta as points, |
547 | // and in addition the Qt 4 compatibility vertical angle delta. |
548 | acceptVert = handleWindowSystemEvent<QWindowSystemInterfacePrivate::WheelEvent>(args: window, |
549 | args: timestamp, args: QHighDpi::fromNativeLocalPosition(value: local, context: window), args: QHighDpi::fromNativeGlobalPosition(value: global, context: window), |
550 | args: pixelDelta, args: angleDelta, args: angleDelta.y(), args: Qt::Vertical, args: mods, args: phase, args: source, args: invertedScrolling, args: device); |
551 | |
552 | // The second event contains null pixel and angle points and the |
553 | // Qt 4 compatibility horizontal angle delta. |
554 | acceptHorz = handleWindowSystemEvent<QWindowSystemInterfacePrivate::WheelEvent>(args: window, |
555 | args: timestamp, args: QHighDpi::fromNativeLocalPosition(value: local, context: window), args: QHighDpi::fromNativeGlobalPosition(value: global, context: window), |
556 | args: QPoint(), args: QPoint(), args: angleDelta.x(), args: Qt::Horizontal, args: mods, args: phase, args: source, args: invertedScrolling, args: device); |
557 | |
558 | return acceptVert || acceptHorz; |
559 | } |
560 | |
561 | /*! |
562 | \internal |
563 | Register a new input \a device. |
564 | |
565 | It is expected that every platform plugin will discover available input |
566 | devices at startup, and whenever a new device is plugged in, if possible. |
567 | If that's not possible, then it at least must call this function before |
568 | sending an event whose QInputEvent::source() is this device. |
569 | |
570 | When a device is unplugged, the platform plugin should destroy the |
571 | corresponding QInputDevice instance. There is no unregisterInputDevice() |
572 | function, because it's enough for the destructor to call |
573 | QInputDevicePrivate::unregisterDevice(); while other parts of Qt can |
574 | connect to the QObject::destroyed() signal to be notified when a device is |
575 | unplugged or otherwise destroyed. |
576 | */ |
577 | void QWindowSystemInterface::registerInputDevice(const QInputDevice *device) |
578 | { |
579 | qCDebug(lcQpaInputDevices) << "register" << device; |
580 | QInputDevicePrivate::registerDevice(dev: device); |
581 | } |
582 | |
583 | /*! |
584 | \internal |
585 | Convert a list of \l QWindowSystemInterface::TouchPoint \a points to a list |
586 | of \e temporary QEventPoint instances, scaled (but not localized) |
587 | for delivery to the given \a window. |
588 | |
589 | This is called from QWindowSystemInterface::handleTouchEvent(): |
590 | that is too early to update the QEventPoint instances in QPointingDevice, |
591 | because we want those to hold "current" state from the applcation's |
592 | point of view. The QWindowSystemInterfacePrivate::TouchEvent, to which |
593 | the returned touchpoints will "belong", might go through the queue before |
594 | being processed; the application doesn't see the equivalent QTouchEvent |
595 | until later on. Therefore the responsibility to update the QEventPoint |
596 | instances in QPointingDevice is in QGuiApplication, not here. |
597 | |
598 | QGuiApplicationPrivate::processMouseEvent() also calls this function |
599 | when it synthesizes a touch event from a mouse event. But that's outside |
600 | the normal use case. |
601 | |
602 | It might be better if we change all the platform plugins to create |
603 | temporary instances of QEventPoint directly, and remove |
604 | QWindowSystemInterface::TouchPoint completely. Then we will no longer need |
605 | this function either. But that's only possible as long as QEventPoint |
606 | remains a Q_GADGET, not a QObject, so that it continues to be small and |
607 | suitable for temporary stack allocation. QEventPoint is a little bigger |
608 | than QWindowSystemInterface::TouchPoint, though. |
609 | */ |
610 | QList<QEventPoint> |
611 | QWindowSystemInterfacePrivate::fromNativeTouchPoints(const QList<QWindowSystemInterface::TouchPoint> &points, |
612 | const QWindow *window, QEvent::Type *type) |
613 | { |
614 | QList<QEventPoint> touchPoints; |
615 | QEventPoint::States states; |
616 | |
617 | touchPoints.reserve(asize: points.size()); |
618 | QList<QWindowSystemInterface::TouchPoint>::const_iterator point = points.constBegin(); |
619 | QList<QWindowSystemInterface::TouchPoint>::const_iterator end = points.constEnd(); |
620 | while (point != end) { |
621 | QPointF globalPos = QHighDpi::fromNativePixels(value: point->area.center(), context: window); |
622 | QEventPoint p(point->id, point->state, globalPos, globalPos); |
623 | states |= point->state; |
624 | if (point->uniqueId >= 0) |
625 | QMutableEventPoint::setUniqueId(p, arg: QPointingDeviceUniqueId::fromNumericId(id: point->uniqueId)); |
626 | QMutableEventPoint::setPressure(p, arg: point->pressure); |
627 | QMutableEventPoint::setRotation(p, arg: point->rotation); |
628 | QMutableEventPoint::setEllipseDiameters(p, arg: QHighDpi::fromNativePixels(value: point->area.size(), context: window)); |
629 | QMutableEventPoint::setVelocity(p, arg: QHighDpi::fromNativePixels(value: point->velocity, context: window)); |
630 | |
631 | // The local pos is not set: it will be calculated |
632 | // when the event gets processed by QGuiApplication. |
633 | |
634 | touchPoints.append(t: p); |
635 | ++point; |
636 | } |
637 | |
638 | // Determine the event type based on the combined point states. |
639 | if (type) { |
640 | *type = QEvent::TouchUpdate; |
641 | if (states == QEventPoint::State::Pressed) |
642 | *type = QEvent::TouchBegin; |
643 | else if (states == QEventPoint::State::Released) |
644 | *type = QEvent::TouchEnd; |
645 | } |
646 | |
647 | return touchPoints; |
648 | } |
649 | |
650 | QWindowSystemInterface::TouchPoint |
651 | QWindowSystemInterfacePrivate::toNativeTouchPoint(const QEventPoint &pt, const QWindow *window) |
652 | { |
653 | QWindowSystemInterface::TouchPoint p; |
654 | p.id = pt.id(); |
655 | QRectF area(QPointF(), pt.ellipseDiameters()); |
656 | area.moveCenter(p: pt.globalPosition()); |
657 | // TODO store ellipseDiameters in QWindowSystemInterface::TouchPoint or just use QEventPoint |
658 | p.area = QHighDpi::toNativePixels(value: area, context: window); |
659 | p.pressure = pt.pressure(); |
660 | p.state = pt.state(); |
661 | p.velocity = QHighDpi::toNativePixels(value: pt.velocity(), context: window); |
662 | return p; |
663 | } |
664 | |
665 | QT_DEFINE_QPA_EVENT_HANDLER(bool, handleTouchEvent, QWindow *window, const QPointingDevice *device, |
666 | const QList<TouchPoint> &points, Qt::KeyboardModifiers mods) |
667 | { |
668 | unsigned long time = QWindowSystemInterfacePrivate::eventTime.elapsed(); |
669 | return handleTouchEvent<Delivery>(window, time, device, points, mods); |
670 | } |
671 | |
672 | QT_DEFINE_QPA_EVENT_HANDLER(bool, handleTouchEvent, QWindow *window, ulong timestamp, const QPointingDevice *device, |
673 | const QList<TouchPoint> &points, Qt::KeyboardModifiers mods) |
674 | { |
675 | if (!points.size()) // Touch events must have at least one point |
676 | return false; |
677 | |
678 | if (!QPointingDevicePrivate::isRegistered(dev: device)) // Disallow passing bogus, non-registered devices. |
679 | return false; |
680 | |
681 | QEvent::Type type; |
682 | QList<QEventPoint> touchPoints = |
683 | QWindowSystemInterfacePrivate::fromNativeTouchPoints(points, window, type: &type); |
684 | |
685 | return handleWindowSystemEvent<QWindowSystemInterfacePrivate::TouchEvent, Delivery>(window, |
686 | timestamp, type, device, touchPoints, mods); |
687 | } |
688 | |
689 | QT_DEFINE_QPA_EVENT_HANDLER(bool, handleTouchCancelEvent, QWindow *window, const QPointingDevice *device, |
690 | Qt::KeyboardModifiers mods) |
691 | { |
692 | unsigned long time = QWindowSystemInterfacePrivate::eventTime.elapsed(); |
693 | return handleTouchCancelEvent<Delivery>(window, time, device, mods); |
694 | } |
695 | |
696 | QT_DEFINE_QPA_EVENT_HANDLER(bool, handleTouchCancelEvent, QWindow *window, ulong timestamp, const QPointingDevice *device, |
697 | Qt::KeyboardModifiers mods) |
698 | { |
699 | return handleWindowSystemEvent<QWindowSystemInterfacePrivate::TouchEvent, Delivery>(window, |
700 | timestamp, QEvent::TouchCancel, device, QList<QEventPoint>(), mods); |
701 | } |
702 | |
703 | /*! |
704 | Should be called by the implementation whenever a new screen is added. |
705 | |
706 | The first screen added will be the primary screen, used for default-created |
707 | windows, GL contexts, and other resources unless otherwise specified. |
708 | |
709 | This adds the screen to QGuiApplication::screens(), and emits the |
710 | QGuiApplication::screenAdded() signal. |
711 | |
712 | The screen should be deleted by calling QWindowSystemInterface::handleScreenRemoved(). |
713 | */ |
714 | void QWindowSystemInterface::handleScreenAdded(QPlatformScreen *platformScreen, bool isPrimary) |
715 | { |
716 | QScreen *screen = new QScreen(platformScreen); |
717 | |
718 | if (isPrimary) |
719 | QGuiApplicationPrivate::screen_list.prepend(t: screen); |
720 | else |
721 | QGuiApplicationPrivate::screen_list.append(t: screen); |
722 | |
723 | QGuiApplicationPrivate::resetCachedDevicePixelRatio(); |
724 | QHighDpiScaling::updateHighDpiScaling(); |
725 | screen->d_func()->updateGeometry(); |
726 | |
727 | emit qGuiApp->screenAdded(screen); |
728 | |
729 | if (isPrimary) |
730 | emit qGuiApp->primaryScreenChanged(screen); |
731 | } |
732 | |
733 | /*! |
734 | Should be called by the implementation whenever a screen is removed. |
735 | |
736 | This removes the screen from QGuiApplication::screens(), and deletes it. |
737 | |
738 | Failing to call this and manually deleting the QPlatformScreen instead may |
739 | lead to a crash due to a pure virtual call. |
740 | */ |
741 | void QWindowSystemInterface::handleScreenRemoved(QPlatformScreen *platformScreen) |
742 | { |
743 | QScreen *screen = platformScreen->screen(); |
744 | |
745 | // Remove screen |
746 | const bool wasPrimary = QGuiApplication::primaryScreen() == screen; |
747 | QGuiApplicationPrivate::screen_list.removeOne(t: screen); |
748 | QGuiApplicationPrivate::resetCachedDevicePixelRatio(); |
749 | |
750 | if (qGuiApp) { |
751 | QScreen *newPrimaryScreen = QGuiApplication::primaryScreen(); |
752 | if (wasPrimary && newPrimaryScreen) |
753 | emit qGuiApp->primaryScreenChanged(screen: newPrimaryScreen); |
754 | |
755 | // Allow clients to manage windows that are affected by the screen going |
756 | // away, before we fall back to moving them to the primary screen. |
757 | emit qApp->screenRemoved(screen); |
758 | |
759 | if (!QGuiApplication::closingDown()) { |
760 | bool movingFromVirtualSibling = newPrimaryScreen |
761 | && newPrimaryScreen->handle()->virtualSiblings().contains(t: platformScreen); |
762 | |
763 | // Move any leftover windows to the primary screen |
764 | const auto allWindows = QGuiApplication::allWindows(); |
765 | for (QWindow *window : allWindows) { |
766 | if (!window->isTopLevel() || window->screen() != screen) |
767 | continue; |
768 | |
769 | const bool wasVisible = window->isVisible(); |
770 | window->setScreen(newPrimaryScreen); |
771 | |
772 | // Re-show window if moved from a virtual sibling screen. Otherwise |
773 | // leave it up to the application developer to show the window. |
774 | if (movingFromVirtualSibling) |
775 | window->setVisible(wasVisible); |
776 | } |
777 | } |
778 | } |
779 | |
780 | // Important to keep this order since the QSceen doesn't own the platform screen |
781 | delete screen; |
782 | delete platformScreen; |
783 | } |
784 | |
785 | /*! |
786 | Should be called whenever the primary screen changes. |
787 | |
788 | When the screen specified as primary changes, this method will notify |
789 | QGuiApplication and emit the QGuiApplication::primaryScreenChanged signal. |
790 | */ |
791 | void QWindowSystemInterface::handlePrimaryScreenChanged(QPlatformScreen *newPrimary) |
792 | { |
793 | QScreen *newPrimaryScreen = newPrimary->screen(); |
794 | qsizetype indexOfScreen = QGuiApplicationPrivate::screen_list.indexOf(t: newPrimaryScreen); |
795 | Q_ASSERT(indexOfScreen >= 0); |
796 | if (indexOfScreen == 0) |
797 | return; |
798 | |
799 | QGuiApplicationPrivate::screen_list.swapItemsAt(i: 0, j: indexOfScreen); |
800 | emit qGuiApp->primaryScreenChanged(screen: newPrimaryScreen); |
801 | } |
802 | |
803 | void QWindowSystemInterface::handleScreenOrientationChange(QScreen *screen, Qt::ScreenOrientation orientation) |
804 | { |
805 | handleWindowSystemEvent<QWindowSystemInterfacePrivate::ScreenOrientationEvent>(args: screen, args: orientation); |
806 | } |
807 | |
808 | void QWindowSystemInterface::handleScreenGeometryChange(QScreen *screen, const QRect &geometry, const QRect &availableGeometry) |
809 | { |
810 | handleWindowSystemEvent<QWindowSystemInterfacePrivate::ScreenGeometryEvent>(args: screen, |
811 | args: QHighDpi::fromNativeScreenGeometry(nativeScreenGeometry: geometry, screen), args: QHighDpi::fromNative(rect: availableGeometry, |
812 | screen, screenOrigin: geometry.topLeft())); |
813 | } |
814 | |
815 | void QWindowSystemInterface::handleScreenLogicalDotsPerInchChange(QScreen *screen, qreal dpiX, qreal dpiY) |
816 | { |
817 | const QDpi effectiveDpi = QPlatformScreen::overrideDpi(in: QDpi{dpiX, dpiY}); |
818 | handleWindowSystemEvent<QWindowSystemInterfacePrivate::ScreenLogicalDotsPerInchEvent>(args: screen, |
819 | args: effectiveDpi.first, args: effectiveDpi.second); |
820 | } |
821 | |
822 | void QWindowSystemInterface::handleScreenRefreshRateChange(QScreen *screen, qreal newRefreshRate) |
823 | { |
824 | handleWindowSystemEvent<QWindowSystemInterfacePrivate::ScreenRefreshRateEvent>(args: screen, args: newRefreshRate); |
825 | } |
826 | |
827 | QT_DEFINE_QPA_EVENT_HANDLER(void, handleThemeChange, QWindow *window) |
828 | { |
829 | handleWindowSystemEvent<QWindowSystemInterfacePrivate::ThemeChangeEvent, Delivery>(window); |
830 | } |
831 | |
832 | #if QT_CONFIG(draganddrop) |
833 | /*! |
834 | Drag and drop events are sent immediately. |
835 | |
836 | ### FIXME? Perhaps DnD API should add some convenience APIs that are more |
837 | intuitive for the possible DND operations. Here passing nullptr as drop data is used to |
838 | indicate that drop was canceled and QDragLeaveEvent should be sent as a result. |
839 | */ |
840 | QPlatformDragQtResponse QWindowSystemInterface::handleDrag(QWindow *window, const QMimeData *dropData, |
841 | const QPoint &p, Qt::DropActions supportedActions, |
842 | Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers) |
843 | { |
844 | auto pos = QHighDpi::fromNativeLocalPosition(value: p, context: window); |
845 | return QGuiApplicationPrivate::processDrag(w: window, dropData, p: pos, supportedActions, buttons, modifiers); |
846 | } |
847 | |
848 | QPlatformDropQtResponse QWindowSystemInterface::handleDrop(QWindow *window, const QMimeData *dropData, |
849 | const QPoint &p, Qt::DropActions supportedActions, |
850 | Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers) |
851 | { |
852 | auto pos = QHighDpi::fromNativeLocalPosition(value: p, context: window); |
853 | return QGuiApplicationPrivate::processDrop(w: window, dropData, p: pos, supportedActions, buttons, modifiers); |
854 | } |
855 | #endif // QT_CONFIG(draganddrop) |
856 | |
857 | /*! |
858 | \fn static QWindowSystemInterface::handleNativeEvent(QWindow *window, const QByteArray &eventType, void *message, long *result) |
859 | \brief Passes a native event identified by \a eventType to the \a window. |
860 | |
861 | \note This function can only be called from the GUI thread. |
862 | */ |
863 | |
864 | bool QWindowSystemInterface::handleNativeEvent(QWindow *window, const QByteArray &eventType, void *message, qintptr *result) |
865 | { |
866 | return QGuiApplicationPrivate::processNativeEvent(window, eventType, message, result); |
867 | } |
868 | |
869 | void QWindowSystemInterface::handleFileOpenEvent(const QString& fileName) |
870 | { |
871 | QWindowSystemInterfacePrivate::FileOpenEvent e(fileName); |
872 | QGuiApplicationPrivate::processWindowSystemEvent(e: &e); |
873 | } |
874 | |
875 | void QWindowSystemInterface::handleFileOpenEvent(const QUrl &url) |
876 | { |
877 | QWindowSystemInterfacePrivate::FileOpenEvent e(url); |
878 | QGuiApplicationPrivate::processWindowSystemEvent(e: &e); |
879 | } |
880 | |
881 | void QWindowSystemInterfacePrivate::TabletEvent::setPlatformSynthesizesMouse(bool v) |
882 | { |
883 | platformSynthesizesMouse = v; |
884 | } |
885 | |
886 | bool QWindowSystemInterface::handleTabletEvent(QWindow *window, ulong timestamp, const QPointingDevice *device, |
887 | const QPointF &local, const QPointF &global, |
888 | Qt::MouseButtons buttons, qreal pressure, int xTilt, int yTilt, |
889 | qreal tangentialPressure, qreal rotation, int z, |
890 | Qt::KeyboardModifiers modifiers) |
891 | { |
892 | return handleWindowSystemEvent<QWindowSystemInterfacePrivate::TabletEvent>(args: window, |
893 | args: timestamp, |
894 | args: QHighDpi::fromNativeLocalPosition(value: local, context: window), |
895 | args: QHighDpi::fromNativeGlobalPosition(value: global, context: window), |
896 | args: device, args: buttons, args: pressure, |
897 | args: xTilt, args: yTilt, args: tangentialPressure, args: rotation, args: z, args: modifiers); |
898 | } |
899 | |
900 | bool QWindowSystemInterface::handleTabletEvent(QWindow *window, const QPointingDevice *device, |
901 | const QPointF &local, const QPointF &global, |
902 | Qt::MouseButtons buttons, qreal pressure, int xTilt, int yTilt, |
903 | qreal tangentialPressure, qreal rotation, int z, |
904 | Qt::KeyboardModifiers modifiers) |
905 | { |
906 | const ulong time = QWindowSystemInterfacePrivate::eventTime.elapsed(); |
907 | return handleTabletEvent(window, timestamp: time, device, local, global, |
908 | buttons, pressure, xTilt, yTilt, tangentialPressure, |
909 | rotation, z, modifiers); |
910 | } |
911 | |
912 | bool QWindowSystemInterface::handleTabletEvent(QWindow *window, ulong timestamp, const QPointF &local, const QPointF &global, |
913 | int device, int pointerType, Qt::MouseButtons buttons, qreal pressure, int xTilt, int yTilt, |
914 | qreal tangentialPressure, qreal rotation, int z, qint64 uid, |
915 | Qt::KeyboardModifiers modifiers) |
916 | { |
917 | const QPointingDevice *dev = QPointingDevicePrivate::tabletDevice(deviceType: QInputDevice::DeviceType(device),pointerType: QPointingDevice::PointerType(pointerType), |
918 | uniqueId: QPointingDeviceUniqueId::fromNumericId(id: uid)); |
919 | return handleTabletEvent(window, timestamp, device: dev, local, global, buttons, pressure, |
920 | xTilt, yTilt, tangentialPressure, rotation, z, modifiers); |
921 | } |
922 | |
923 | bool QWindowSystemInterface::handleTabletEvent(QWindow *window, const QPointF &local, const QPointF &global, |
924 | int device, int pointerType, Qt::MouseButtons buttons, qreal pressure, int xTilt, int yTilt, |
925 | qreal tangentialPressure, qreal rotation, int z, qint64 uid, |
926 | Qt::KeyboardModifiers modifiers) |
927 | { |
928 | ulong time = QWindowSystemInterfacePrivate::eventTime.elapsed(); |
929 | return handleTabletEvent(window, timestamp: time, local, global, device, pointerType, buttons, pressure, |
930 | xTilt, yTilt, tangentialPressure, rotation, z, uid, modifiers); |
931 | } |
932 | |
933 | bool QWindowSystemInterface::handleTabletEnterLeaveProximityEvent(QWindow *window, ulong timestamp, const QPointingDevice *device, |
934 | bool inProximity, const QPointF &local, const QPointF &global, |
935 | Qt::MouseButtons buttons, int xTilt, int yTilt, |
936 | qreal tangentialPressure, qreal rotation, int z, |
937 | Qt::KeyboardModifiers modifiers) |
938 | { |
939 | Q_UNUSED(window); |
940 | Q_UNUSED(local); |
941 | Q_UNUSED(global); |
942 | Q_UNUSED(buttons); |
943 | Q_UNUSED(xTilt); |
944 | Q_UNUSED(yTilt); |
945 | Q_UNUSED(tangentialPressure); |
946 | Q_UNUSED(rotation); |
947 | Q_UNUSED(z); |
948 | Q_UNUSED(modifiers); |
949 | return inProximity |
950 | ? handleWindowSystemEvent<QWindowSystemInterfacePrivate::TabletEnterProximityEvent>(args: timestamp, args: device) |
951 | : handleWindowSystemEvent<QWindowSystemInterfacePrivate::TabletLeaveProximityEvent>(args: timestamp, args: device); |
952 | } |
953 | |
954 | bool QWindowSystemInterface::handleTabletEnterLeaveProximityEvent(QWindow *window, const QPointingDevice *device, |
955 | bool inProximity, const QPointF &local, const QPointF &global, |
956 | Qt::MouseButtons buttons, int xTilt, int yTilt, |
957 | qreal tangentialPressure, qreal rotation, int z, |
958 | Qt::KeyboardModifiers modifiers) |
959 | { |
960 | const ulong time = QWindowSystemInterfacePrivate::eventTime.elapsed(); |
961 | return handleTabletEnterLeaveProximityEvent(window, timestamp: time, device, inProximity, |
962 | local, global, buttons, xTilt, yTilt, |
963 | tangentialPressure, rotation, z, modifiers); |
964 | } |
965 | |
966 | |
967 | bool QWindowSystemInterface::handleTabletEnterProximityEvent(ulong timestamp, int deviceType, int pointerType, qint64 uid) |
968 | { |
969 | const QPointingDevice *device = QPointingDevicePrivate::tabletDevice(deviceType: QInputDevice::DeviceType(deviceType), |
970 | pointerType: QPointingDevice::PointerType(pointerType), |
971 | uniqueId: QPointingDeviceUniqueId::fromNumericId(id: uid)); |
972 | return handleWindowSystemEvent<QWindowSystemInterfacePrivate::TabletEnterProximityEvent>(args: timestamp, args: device); |
973 | } |
974 | |
975 | void QWindowSystemInterface::handleTabletEnterProximityEvent(int deviceType, int pointerType, qint64 uid) |
976 | { |
977 | ulong time = QWindowSystemInterfacePrivate::eventTime.elapsed(); |
978 | handleTabletEnterProximityEvent(timestamp: time, deviceType, pointerType, uid); |
979 | } |
980 | |
981 | bool QWindowSystemInterface::handleTabletLeaveProximityEvent(ulong timestamp, int deviceType, int pointerType, qint64 uid) |
982 | { |
983 | const QPointingDevice *device = QPointingDevicePrivate::tabletDevice(deviceType: QInputDevice::DeviceType(deviceType), |
984 | pointerType: QPointingDevice::PointerType(pointerType), |
985 | uniqueId: QPointingDeviceUniqueId::fromNumericId(id: uid)); |
986 | return handleWindowSystemEvent<QWindowSystemInterfacePrivate::TabletLeaveProximityEvent>(args: timestamp, args: device); |
987 | } |
988 | |
989 | void QWindowSystemInterface::handleTabletLeaveProximityEvent(int deviceType, int pointerType, qint64 uid) |
990 | { |
991 | ulong time = QWindowSystemInterfacePrivate::eventTime.elapsed(); |
992 | handleTabletLeaveProximityEvent(timestamp: time, deviceType, pointerType, uid); |
993 | } |
994 | |
995 | #ifndef QT_NO_GESTURES |
996 | bool QWindowSystemInterface::handleGestureEvent(QWindow *window, ulong timestamp, const QPointingDevice *device, |
997 | Qt::NativeGestureType type, const QPointF &local, const QPointF &global, int fingerCount) |
998 | { |
999 | return handleGestureEventWithValueAndDelta(window, timestamp, device, type, value: {}, delta: {}, local, global, fingerCount); |
1000 | } |
1001 | |
1002 | bool QWindowSystemInterface::handleGestureEventWithRealValue(QWindow *window, ulong timestamp, const QPointingDevice *device, |
1003 | Qt::NativeGestureType type, qreal value, const QPointF &local, const QPointF &global, int fingerCount) |
1004 | { |
1005 | return handleGestureEventWithValueAndDelta(window, timestamp, device, type, value, delta: {}, local, global, fingerCount); |
1006 | } |
1007 | |
1008 | bool QWindowSystemInterface::handleGestureEventWithValueAndDelta(QWindow *window, ulong timestamp, const QPointingDevice *device, |
1009 | Qt::NativeGestureType type, qreal value, const QPointF &delta, |
1010 | const QPointF &local, const QPointF &global, int fingerCount) |
1011 | { |
1012 | auto localPos = QHighDpi::fromNativeLocalPosition(value: local, context: window); |
1013 | auto globalPos = QHighDpi::fromNativeGlobalPosition(value: global, context: window); |
1014 | |
1015 | return handleWindowSystemEvent<QWindowSystemInterfacePrivate::GestureEvent>(args: window, |
1016 | args: timestamp, args: type, args: device, args: fingerCount, args: localPos, args: globalPos, args: value, args: delta); |
1017 | } |
1018 | #endif // QT_NO_GESTURES |
1019 | |
1020 | void QWindowSystemInterface::handlePlatformPanelEvent(QWindow *w) |
1021 | { |
1022 | handleWindowSystemEvent<QWindowSystemInterfacePrivate::PlatformPanelEvent>(args: w); |
1023 | } |
1024 | |
1025 | #ifndef QT_NO_CONTEXTMENU |
1026 | void QWindowSystemInterface::handleContextMenuEvent(QWindow *window, bool mouseTriggered, |
1027 | const QPoint &pos, const QPoint &globalPos, |
1028 | Qt::KeyboardModifiers modifiers) |
1029 | { |
1030 | handleWindowSystemEvent<QWindowSystemInterfacePrivate::ContextMenuEvent>(args: window, |
1031 | args: mouseTriggered, args: pos, args: globalPos, args: modifiers); |
1032 | } |
1033 | #endif |
1034 | |
1035 | #if QT_CONFIG(whatsthis) |
1036 | void QWindowSystemInterface::handleEnterWhatsThisEvent() |
1037 | { |
1038 | handleWindowSystemEvent<QWindowSystemInterfacePrivate::WindowSystemEvent>( |
1039 | args: QWindowSystemInterfacePrivate::EnterWhatsThisMode); |
1040 | } |
1041 | #endif |
1042 | |
1043 | #ifndef QT_NO_DEBUG_STREAM |
1044 | Q_GUI_EXPORT QDebug operator<<(QDebug dbg, const QWindowSystemInterface::TouchPoint &p) |
1045 | { |
1046 | QDebugStateSaver saver(dbg); |
1047 | dbg.nospace() << "TouchPoint(" << p.id << " @" << p.area << " normalized " << p.normalPosition |
1048 | << " press " << p.pressure << " vel " << p.velocity << " state " << (int)p.state; |
1049 | return dbg; |
1050 | } |
1051 | #endif |
1052 | |
1053 | // ------------------ Event dispatcher functionality ------------------ |
1054 | |
1055 | /*! |
1056 | Make Qt Gui process all events on the event queue immediately. Return the |
1057 | accepted state for the last event on the queue. |
1058 | */ |
1059 | bool QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ProcessEventsFlags flags) |
1060 | { |
1061 | const qsizetype count = QWindowSystemInterfacePrivate::windowSystemEventQueue.count(); |
1062 | if (!count) |
1063 | return false; |
1064 | if (!QGuiApplication::instance()) { |
1065 | qWarning().nospace() |
1066 | << "QWindowSystemInterface::flushWindowSystemEvents() invoked after " |
1067 | "QGuiApplication destruction, discarding " << count << " events." ; |
1068 | QWindowSystemInterfacePrivate::windowSystemEventQueue.clear(); |
1069 | return false; |
1070 | } |
1071 | if (QThread::currentThread() != QGuiApplication::instance()->thread()) { |
1072 | // Post a FlushEvents event which will trigger a call back to |
1073 | // deferredFlushWindowSystemEvents from the Gui thread. |
1074 | QMutexLocker locker(&QWindowSystemInterfacePrivate::flushEventMutex); |
1075 | handleWindowSystemEvent<QWindowSystemInterfacePrivate::FlushEventsEvent, AsynchronousDelivery>(args: flags); |
1076 | QWindowSystemInterfacePrivate::eventsFlushed.wait(lockedMutex: &QWindowSystemInterfacePrivate::flushEventMutex); |
1077 | } else { |
1078 | sendWindowSystemEvents(flags); |
1079 | } |
1080 | return QWindowSystemInterfacePrivate::eventAccepted.loadRelaxed() > 0; |
1081 | } |
1082 | |
1083 | void QWindowSystemInterface::deferredFlushWindowSystemEvents(QEventLoop::ProcessEventsFlags flags) |
1084 | { |
1085 | Q_ASSERT(QThread::currentThread() == QGuiApplication::instance()->thread()); |
1086 | |
1087 | QMutexLocker locker(&QWindowSystemInterfacePrivate::flushEventMutex); |
1088 | sendWindowSystemEvents(flags); |
1089 | QWindowSystemInterfacePrivate::eventsFlushed.wakeOne(); |
1090 | } |
1091 | |
1092 | bool QWindowSystemInterface::sendWindowSystemEvents(QEventLoop::ProcessEventsFlags flags) |
1093 | { |
1094 | int nevents = 0; |
1095 | |
1096 | while (QWindowSystemInterfacePrivate::windowSystemEventsQueued()) { |
1097 | QWindowSystemInterfacePrivate::WindowSystemEvent *event = |
1098 | flags & QEventLoop::ExcludeUserInputEvents ? |
1099 | QWindowSystemInterfacePrivate::getNonUserInputWindowSystemEvent() : |
1100 | QWindowSystemInterfacePrivate::getWindowSystemEvent(); |
1101 | if (!event) |
1102 | break; |
1103 | |
1104 | if (QWindowSystemInterfacePrivate::eventHandler) { |
1105 | if (QWindowSystemInterfacePrivate::eventHandler->sendEvent(e: event)) |
1106 | nevents++; |
1107 | } else { |
1108 | nevents++; |
1109 | QGuiApplicationPrivate::processWindowSystemEvent(e: event); |
1110 | } |
1111 | |
1112 | // Record the accepted state for the processed event |
1113 | // (excluding flush events). This state can then be |
1114 | // returned by flushWindowSystemEvents(). |
1115 | if (event->type != QWindowSystemInterfacePrivate::FlushEvents) |
1116 | QWindowSystemInterfacePrivate::eventAccepted.storeRelaxed(newValue: event->eventAccepted); |
1117 | |
1118 | delete event; |
1119 | } |
1120 | |
1121 | return (nevents > 0); |
1122 | } |
1123 | |
1124 | void QWindowSystemInterface::setSynchronousWindowSystemEvents(bool enable) |
1125 | { |
1126 | QWindowSystemInterfacePrivate::synchronousWindowSystemEvents = enable; |
1127 | } |
1128 | |
1129 | int QWindowSystemInterface::windowSystemEventsQueued() |
1130 | { |
1131 | return QWindowSystemInterfacePrivate::windowSystemEventsQueued(); |
1132 | } |
1133 | |
1134 | bool QWindowSystemInterface::nonUserInputEventsQueued() |
1135 | { |
1136 | return QWindowSystemInterfacePrivate::nonUserInputEventsQueued(); |
1137 | } |
1138 | |
1139 | // --------------------- QtTestLib support --------------------- |
1140 | |
1141 | // The following functions are used by testlib, and need to be synchronous to avoid |
1142 | // race conditions with plugins delivering native events from secondary threads. |
1143 | // FIXME: It seems unnecessary to export these wrapper functions, when qtestlib could access |
1144 | // QWindowSystemInterface directly (by adding dependency to gui-private), see QTBUG-63146. |
1145 | |
1146 | Q_GUI_EXPORT void qt_handleMouseEvent(QWindow *window, const QPointF &local, const QPointF &global, |
1147 | Qt::MouseButtons state, Qt::MouseButton button, |
1148 | QEvent::Type type, Qt::KeyboardModifiers mods, int timestamp) |
1149 | { |
1150 | QPointF nativeLocal = QHighDpi::toNativeLocalPosition(value: local, context: window); |
1151 | QPointF nativeGlobal = QHighDpi::toNativeGlobalPosition(value: global, context: window); |
1152 | QWindowSystemInterface::handleMouseEvent<QWindowSystemInterface::SynchronousDelivery>(window, |
1153 | timestamp, local: nativeLocal, global: nativeGlobal, state, button, type, mods); |
1154 | } |
1155 | |
1156 | /* |
1157 | Used by QTest::simulateEvent() to synthesize key events during testing |
1158 | */ |
1159 | Q_GUI_EXPORT void qt_handleKeyEvent(QWindow *window, QEvent::Type t, int k, Qt::KeyboardModifiers mods, const QString & text = QString(), bool autorep = false, ushort count = 1) |
1160 | { |
1161 | #if defined(Q_OS_MACOS) |
1162 | // FIXME: Move into QTest::simulateEvent() and align with QGuiApplicationPrivate::processKeyEvent() |
1163 | auto timestamp = QWindowSystemInterfacePrivate::eventTime.elapsed(); |
1164 | if (t == QEvent::KeyPress && QWindowSystemInterface::handleShortcutEvent(window, timestamp, k, mods, 0, 0, 0, text, autorep, count)) |
1165 | return; |
1166 | #endif |
1167 | |
1168 | QWindowSystemInterface::handleKeyEvent<QWindowSystemInterface::SynchronousDelivery>(window, t, k, mods, text, autorep, count); |
1169 | } |
1170 | |
1171 | Q_GUI_EXPORT bool qt_sendShortcutOverrideEvent(QObject *o, ulong timestamp, int k, Qt::KeyboardModifiers mods, const QString &text = QString(), bool autorep = false, ushort count = 1) |
1172 | { |
1173 | #if QT_CONFIG(shortcut) |
1174 | |
1175 | // FIXME: This method should not allow targeting a specific object, but should |
1176 | // instead forward the event to a window, which then takes care of normal event |
1177 | // propagation. We need to fix a lot of tests before we can refactor this (the |
1178 | // window needs to be exposed and active and have a focus object), so we leave |
1179 | // it as is for now. See QTBUG-48577. |
1180 | |
1181 | QGuiApplicationPrivate::modifier_buttons = mods; |
1182 | |
1183 | QKeyEvent qevent(QEvent::ShortcutOverride, k, mods, text, autorep, count); |
1184 | qevent.setTimestamp(timestamp); |
1185 | |
1186 | QShortcutMap &shortcutMap = QGuiApplicationPrivate::instance()->shortcutMap; |
1187 | if (shortcutMap.state() == QKeySequence::NoMatch) { |
1188 | // Try sending as QKeyEvent::ShortcutOverride first |
1189 | QCoreApplication::sendEvent(receiver: o, event: &qevent); |
1190 | if (qevent.isAccepted()) |
1191 | return false; |
1192 | } |
1193 | |
1194 | // Then as QShortcutEvent |
1195 | return shortcutMap.tryShortcut(e: &qevent); |
1196 | #else |
1197 | Q_UNUSED(o); |
1198 | Q_UNUSED(timestamp); |
1199 | Q_UNUSED(k); |
1200 | Q_UNUSED(mods); |
1201 | Q_UNUSED(text); |
1202 | Q_UNUSED(autorep); |
1203 | Q_UNUSED(count); |
1204 | return false; |
1205 | #endif |
1206 | } |
1207 | |
1208 | namespace QTest |
1209 | { |
1210 | Q_GUI_EXPORT QPointingDevice * createTouchDevice(QInputDevice::DeviceType devType, |
1211 | QInputDevice::Capabilities caps) |
1212 | { |
1213 | static qint64 nextId = 0x100000000; |
1214 | QPointingDevice *ret = new QPointingDevice("test touch device"_L1 , nextId++, |
1215 | devType, QPointingDevice::PointerType::Finger, |
1216 | caps, 8, 0); |
1217 | QWindowSystemInterface::registerInputDevice(device: ret); |
1218 | return ret; |
1219 | } |
1220 | } |
1221 | |
1222 | Q_GUI_EXPORT bool qt_handleTouchEventv2(QWindow *window, const QPointingDevice *device, |
1223 | const QList<QEventPoint> &points, |
1224 | Qt::KeyboardModifiers mods) |
1225 | { |
1226 | return QWindowSystemInterface::handleTouchEvent<QWindowSystemInterface::SynchronousDelivery>(window, device, |
1227 | points: QWindowSystemInterfacePrivate::toNativeTouchPoints(pointList: points, window), mods); |
1228 | } |
1229 | |
1230 | Q_GUI_EXPORT void qt_handleTouchEvent(QWindow *window, const QPointingDevice *device, |
1231 | const QList<QEventPoint> &points, |
1232 | Qt::KeyboardModifiers mods) |
1233 | { |
1234 | qt_handleTouchEventv2(window, device, points, mods); |
1235 | } |
1236 | |
1237 | QT_END_NAMESPACE |
1238 | |