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
23QT_BEGIN_NAMESPACE
24
25using namespace Qt::StringLiterals;
26
27Q_LOGGING_CATEGORY(lcQpaInputDevices, "qt.qpa.input.devices")
28
29Q_CONSTINIT QElapsedTimer QWindowSystemInterfacePrivate::eventTime;
30bool QWindowSystemInterfacePrivate::synchronousWindowSystemEvents = false;
31bool QWindowSystemInterfacePrivate::TabletEvent::platformSynthesizesMouse = true;
32QWaitCondition QWindowSystemInterfacePrivate::eventsFlushed;
33Q_CONSTINIT QMutex QWindowSystemInterfacePrivate::flushEventMutex;
34Q_CONSTINIT QAtomicInt QWindowSystemInterfacePrivate::eventAccepted;
35QWindowSystemEventHandler *QWindowSystemInterfacePrivate::eventHandler;
36QWindowSystemInterfacePrivate::WindowSystemEventList QWindowSystemInterfacePrivate::windowSystemEventQueue;
37
38extern 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
53template<typename Delivery>
54struct 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*/
75template<>
76template<typename EventType, typename ...Args>
77bool 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*/
94template<>
95template<typename EventType, typename ...Args>
96bool 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*/
125template<>
126template<typename EventType, typename ...Args>
127bool 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
135template <typename EventType, typename Delivery = QWindowSystemInterface::DefaultDelivery, typename ...Args>
136static bool handleWindowSystemEvent(Args ...args)
137{
138 return QWindowSystemHelper<Delivery>::template handleEvent<EventType>(args...);
139}
140
141qsizetype QWindowSystemInterfacePrivate::windowSystemEventsQueued()
142{
143 return windowSystemEventQueue.count();
144}
145
146bool QWindowSystemInterfacePrivate::nonUserInputEventsQueued()
147{
148 return windowSystemEventQueue.nonUserInputEventsQueued();
149}
150
151QWindowSystemInterfacePrivate::WindowSystemEvent * QWindowSystemInterfacePrivate::getWindowSystemEvent()
152{
153 return windowSystemEventQueue.takeFirstOrReturnNull();
154}
155
156QWindowSystemInterfacePrivate::WindowSystemEvent *QWindowSystemInterfacePrivate::getNonUserInputWindowSystemEvent()
157{
158 return windowSystemEventQueue.takeFirstNonUserInputOrReturnNull();
159}
160
161QWindowSystemInterfacePrivate::WindowSystemEvent *QWindowSystemInterfacePrivate::peekWindowSystemEvent(EventType t)
162{
163 return windowSystemEventQueue.peekAtFirstOfType(t);
164}
165
166void QWindowSystemInterfacePrivate::removeWindowSystemEvent(WindowSystemEvent *event)
167{
168 windowSystemEventQueue.remove(e: event);
169}
170
171void QWindowSystemInterfacePrivate::installWindowSystemEventHandler(QWindowSystemEventHandler *handler)
172{
173 if (!eventHandler)
174 eventHandler = handler;
175}
176
177void QWindowSystemInterfacePrivate::removeWindowSystemEventhandler(QWindowSystemEventHandler *handler)
178{
179 if (eventHandler == handler)
180 eventHandler = nullptr;
181}
182
183QWindowSystemEventHandler::~QWindowSystemEventHandler()
184{
185 QWindowSystemInterfacePrivate::removeWindowSystemEventhandler(handler: this);
186}
187
188bool 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
217QT_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
225QT_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*/
237void 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
243QT_DEFINE_QPA_EVENT_HANDLER(void, handleFocusWindowChanged, QWindow *window, Qt::FocusReason r)
244{
245 handleWindowSystemEvent<QWindowSystemInterfacePrivate::FocusWindowEvent, Delivery>(window, r);
246}
247
248QT_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
257QT_DEFINE_QPA_EVENT_HANDLER(void, handleWindowScreenChanged, QWindow *window, QScreen *screen)
258{
259 handleWindowSystemEvent<QWindowSystemInterfacePrivate::WindowScreenChangedEvent, Delivery>(window, screen);
260}
261
262QT_DEFINE_QPA_EVENT_HANDLER(void, handleWindowDevicePixelRatioChanged, QWindow *window)
263{
264 handleWindowSystemEvent<QWindowSystemInterfacePrivate::WindowDevicePixelRatioChangedEvent, Delivery>(window);
265}
266
267
268QT_DEFINE_QPA_EVENT_HANDLER(void, handleSafeAreaMarginsChanged, QWindow *window)
269{
270 handleWindowSystemEvent<QWindowSystemInterfacePrivate::SafeAreaMarginsChangedEvent, Delivery>(window);
271}
272
273QT_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
279QT_DEFINE_QPA_EVENT_HANDLER(bool, handleApplicationTermination)
280{
281 return handleWindowSystemEvent<QWindowSystemInterfacePrivate::WindowSystemEvent, Delivery>(
282 QWindowSystemInterfacePrivate::ApplicationTermination);
283}
284
285QWindowSystemInterfacePrivate::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
296QT_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
310QWindowSystemInterfacePrivate::ExposeEvent::ExposeEvent(QWindow *window, const QRegion &region)
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*/
335QT_DEFINE_QPA_EVENT_HANDLER(bool, handleExposeEvent, QWindow *window, const QRegion &region)
336{
337 return handleWindowSystemEvent<QWindowSystemInterfacePrivate::ExposeEvent, Delivery>(window,
338 QHighDpi::fromNativeLocalExposedRegion(pixelRegion: region, window));
339}
340
341QT_DEFINE_QPA_EVENT_HANDLER(bool, handlePaintEvent, QWindow *window, const QRegion &region)
342{
343 return handleWindowSystemEvent<QWindowSystemInterfacePrivate::PaintEvent, Delivery>(window,
344 QHighDpi::fromNativeLocalExposedRegion(pixelRegion: region, window));
345}
346
347
348QT_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
360QT_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
369QT_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
378QT_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
387QT_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
422bool 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
465QT_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
470QT_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
476bool 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
487bool 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
498bool 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
504bool 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
511bool 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*/
577void 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*/
610QList<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
650QWindowSystemInterface::TouchPoint
651QWindowSystemInterfacePrivate::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
665QT_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
672QT_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
689QT_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
696QT_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*/
714void 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*/
741void 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 */
791void 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
803void QWindowSystemInterface::handleScreenOrientationChange(QScreen *screen, Qt::ScreenOrientation orientation)
804{
805 handleWindowSystemEvent<QWindowSystemInterfacePrivate::ScreenOrientationEvent>(args: screen, args: orientation);
806}
807
808void 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
815void QWindowSystemInterface::handleScreenLogicalDotsPerInchChange(QScreen *screen, qreal dpiX, qreal dpiY)
816{
817 // Keep QHighDpiScaling::m_active in sync with platform screen state, in
818 // order to make scaling calls made during DPI change use the new state.
819 // FIXME: Remove when QHighDpiScaling::m_active has been removed.
820 QHighDpiScaling::updateHighDpiScaling();
821
822 const QDpi effectiveDpi = QPlatformScreen::overrideDpi(in: QDpi{dpiX, dpiY});
823 handleWindowSystemEvent<QWindowSystemInterfacePrivate::ScreenLogicalDotsPerInchEvent>(args: screen,
824 args: effectiveDpi.first, args: effectiveDpi.second);
825}
826
827void QWindowSystemInterface::handleScreenRefreshRateChange(QScreen *screen, qreal newRefreshRate)
828{
829 handleWindowSystemEvent<QWindowSystemInterfacePrivate::ScreenRefreshRateEvent>(args: screen, args: newRefreshRate);
830}
831
832QT_DEFINE_QPA_EVENT_HANDLER(void, handleThemeChange, QWindow *window)
833{
834 handleWindowSystemEvent<QWindowSystemInterfacePrivate::ThemeChangeEvent, Delivery>(window);
835}
836
837#if QT_CONFIG(draganddrop)
838/*!
839 Drag and drop events are sent immediately.
840
841 ### FIXME? Perhaps DnD API should add some convenience APIs that are more
842 intuitive for the possible DND operations. Here passing nullptr as drop data is used to
843 indicate that drop was canceled and QDragLeaveEvent should be sent as a result.
844*/
845QPlatformDragQtResponse QWindowSystemInterface::handleDrag(QWindow *window, const QMimeData *dropData,
846 const QPoint &p, Qt::DropActions supportedActions,
847 Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers)
848{
849 auto pos = QHighDpi::fromNativeLocalPosition(value: p, context: window);
850 return QGuiApplicationPrivate::processDrag(w: window, dropData, p: pos, supportedActions, buttons, modifiers);
851}
852
853QPlatformDropQtResponse QWindowSystemInterface::handleDrop(QWindow *window, const QMimeData *dropData,
854 const QPoint &p, Qt::DropActions supportedActions,
855 Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers)
856{
857 auto pos = QHighDpi::fromNativeLocalPosition(value: p, context: window);
858 return QGuiApplicationPrivate::processDrop(w: window, dropData, p: pos, supportedActions, buttons, modifiers);
859}
860#endif // QT_CONFIG(draganddrop)
861
862/*!
863 \fn static QWindowSystemInterface::handleNativeEvent(QWindow *window, const QByteArray &eventType, void *message, long *result)
864 \brief Passes a native event identified by \a eventType to the \a window.
865
866 \note This function can only be called from the GUI thread.
867*/
868
869bool QWindowSystemInterface::handleNativeEvent(QWindow *window, const QByteArray &eventType, void *message, qintptr *result)
870{
871 return QGuiApplicationPrivate::processNativeEvent(window, eventType, message, result);
872}
873
874void QWindowSystemInterface::handleFileOpenEvent(const QString& fileName)
875{
876 QWindowSystemInterfacePrivate::FileOpenEvent e(fileName);
877 QGuiApplicationPrivate::processWindowSystemEvent(e: &e);
878}
879
880void QWindowSystemInterface::handleFileOpenEvent(const QUrl &url)
881{
882 QWindowSystemInterfacePrivate::FileOpenEvent e(url);
883 QGuiApplicationPrivate::processWindowSystemEvent(e: &e);
884}
885
886void QWindowSystemInterfacePrivate::TabletEvent::setPlatformSynthesizesMouse(bool v)
887{
888 platformSynthesizesMouse = v;
889}
890
891bool QWindowSystemInterface::handleTabletEvent(QWindow *window, ulong timestamp, const QPointingDevice *device,
892 const QPointF &local, const QPointF &global,
893 Qt::MouseButtons buttons, qreal pressure, int xTilt, int yTilt,
894 qreal tangentialPressure, qreal rotation, int z,
895 Qt::KeyboardModifiers modifiers)
896{
897 return handleWindowSystemEvent<QWindowSystemInterfacePrivate::TabletEvent>(args: window,
898 args: timestamp,
899 args: QHighDpi::fromNativeLocalPosition(value: local, context: window),
900 args: QHighDpi::fromNativeGlobalPosition(value: global, context: window),
901 args: device, args: buttons, args: pressure,
902 args: xTilt, args: yTilt, args: tangentialPressure, args: rotation, args: z, args: modifiers);
903}
904
905bool QWindowSystemInterface::handleTabletEvent(QWindow *window, const QPointingDevice *device,
906 const QPointF &local, const QPointF &global,
907 Qt::MouseButtons buttons, qreal pressure, int xTilt, int yTilt,
908 qreal tangentialPressure, qreal rotation, int z,
909 Qt::KeyboardModifiers modifiers)
910{
911 const ulong time = QWindowSystemInterfacePrivate::eventTime.elapsed();
912 return handleTabletEvent(window, timestamp: time, device, local, global,
913 buttons, pressure, xTilt, yTilt, tangentialPressure,
914 rotation, z, modifiers);
915}
916
917bool QWindowSystemInterface::handleTabletEvent(QWindow *window, ulong timestamp, const QPointF &local, const QPointF &global,
918 int device, int pointerType, Qt::MouseButtons buttons, qreal pressure, int xTilt, int yTilt,
919 qreal tangentialPressure, qreal rotation, int z, qint64 uid,
920 Qt::KeyboardModifiers modifiers)
921{
922 const QPointingDevice *dev = QPointingDevicePrivate::tabletDevice(deviceType: QInputDevice::DeviceType(device),pointerType: QPointingDevice::PointerType(pointerType),
923 uniqueId: QPointingDeviceUniqueId::fromNumericId(id: uid));
924 return handleTabletEvent(window, timestamp, device: dev, local, global, buttons, pressure,
925 xTilt, yTilt, tangentialPressure, rotation, z, modifiers);
926}
927
928bool QWindowSystemInterface::handleTabletEvent(QWindow *window, const QPointF &local, const QPointF &global,
929 int device, int pointerType, Qt::MouseButtons buttons, qreal pressure, int xTilt, int yTilt,
930 qreal tangentialPressure, qreal rotation, int z, qint64 uid,
931 Qt::KeyboardModifiers modifiers)
932{
933 ulong time = QWindowSystemInterfacePrivate::eventTime.elapsed();
934 return handleTabletEvent(window, timestamp: time, local, global, device, pointerType, buttons, pressure,
935 xTilt, yTilt, tangentialPressure, rotation, z, uid, modifiers);
936}
937
938bool QWindowSystemInterface::handleTabletEnterLeaveProximityEvent(QWindow *window, ulong timestamp, const QPointingDevice *device,
939 bool inProximity, const QPointF &local, const QPointF &global,
940 Qt::MouseButtons buttons, int xTilt, int yTilt,
941 qreal tangentialPressure, qreal rotation, int z,
942 Qt::KeyboardModifiers modifiers)
943{
944 Q_UNUSED(window);
945 Q_UNUSED(local);
946 Q_UNUSED(global);
947 Q_UNUSED(buttons);
948 Q_UNUSED(xTilt);
949 Q_UNUSED(yTilt);
950 Q_UNUSED(tangentialPressure);
951 Q_UNUSED(rotation);
952 Q_UNUSED(z);
953 Q_UNUSED(modifiers);
954 return inProximity
955 ? handleWindowSystemEvent<QWindowSystemInterfacePrivate::TabletEnterProximityEvent>(args: timestamp, args: device)
956 : handleWindowSystemEvent<QWindowSystemInterfacePrivate::TabletLeaveProximityEvent>(args: timestamp, args: device);
957}
958
959bool QWindowSystemInterface::handleTabletEnterLeaveProximityEvent(QWindow *window, const QPointingDevice *device,
960 bool inProximity, const QPointF &local, const QPointF &global,
961 Qt::MouseButtons buttons, int xTilt, int yTilt,
962 qreal tangentialPressure, qreal rotation, int z,
963 Qt::KeyboardModifiers modifiers)
964{
965 const ulong time = QWindowSystemInterfacePrivate::eventTime.elapsed();
966 return handleTabletEnterLeaveProximityEvent(window, timestamp: time, device, inProximity,
967 local, global, buttons, xTilt, yTilt,
968 tangentialPressure, rotation, z, modifiers);
969}
970
971
972bool QWindowSystemInterface::handleTabletEnterProximityEvent(ulong timestamp, int deviceType, int pointerType, qint64 uid)
973{
974 const QPointingDevice *device = QPointingDevicePrivate::tabletDevice(deviceType: QInputDevice::DeviceType(deviceType),
975 pointerType: QPointingDevice::PointerType(pointerType),
976 uniqueId: QPointingDeviceUniqueId::fromNumericId(id: uid));
977 return handleWindowSystemEvent<QWindowSystemInterfacePrivate::TabletEnterProximityEvent>(args: timestamp, args: device);
978}
979
980void QWindowSystemInterface::handleTabletEnterProximityEvent(int deviceType, int pointerType, qint64 uid)
981{
982 ulong time = QWindowSystemInterfacePrivate::eventTime.elapsed();
983 handleTabletEnterProximityEvent(timestamp: time, deviceType, pointerType, uid);
984}
985
986bool QWindowSystemInterface::handleTabletLeaveProximityEvent(ulong timestamp, int deviceType, int pointerType, qint64 uid)
987{
988 const QPointingDevice *device = QPointingDevicePrivate::tabletDevice(deviceType: QInputDevice::DeviceType(deviceType),
989 pointerType: QPointingDevice::PointerType(pointerType),
990 uniqueId: QPointingDeviceUniqueId::fromNumericId(id: uid));
991 return handleWindowSystemEvent<QWindowSystemInterfacePrivate::TabletLeaveProximityEvent>(args: timestamp, args: device);
992}
993
994void QWindowSystemInterface::handleTabletLeaveProximityEvent(int deviceType, int pointerType, qint64 uid)
995{
996 ulong time = QWindowSystemInterfacePrivate::eventTime.elapsed();
997 handleTabletLeaveProximityEvent(timestamp: time, deviceType, pointerType, uid);
998}
999
1000#ifndef QT_NO_GESTURES
1001bool QWindowSystemInterface::handleGestureEvent(QWindow *window, ulong timestamp, const QPointingDevice *device,
1002 Qt::NativeGestureType type, const QPointF &local, const QPointF &global, int fingerCount)
1003{
1004 return handleGestureEventWithValueAndDelta(window, timestamp, device, type, value: {}, delta: {}, local, global, fingerCount);
1005}
1006
1007bool QWindowSystemInterface::handleGestureEventWithRealValue(QWindow *window, ulong timestamp, const QPointingDevice *device,
1008 Qt::NativeGestureType type, qreal value, const QPointF &local, const QPointF &global, int fingerCount)
1009{
1010 return handleGestureEventWithValueAndDelta(window, timestamp, device, type, value, delta: {}, local, global, fingerCount);
1011}
1012
1013bool QWindowSystemInterface::handleGestureEventWithValueAndDelta(QWindow *window, ulong timestamp, const QPointingDevice *device,
1014 Qt::NativeGestureType type, qreal value, const QPointF &delta,
1015 const QPointF &local, const QPointF &global, int fingerCount)
1016{
1017 auto localPos = QHighDpi::fromNativeLocalPosition(value: local, context: window);
1018 auto globalPos = QHighDpi::fromNativeGlobalPosition(value: global, context: window);
1019
1020 return handleWindowSystemEvent<QWindowSystemInterfacePrivate::GestureEvent>(args: window,
1021 args: timestamp, args: type, args: device, args: fingerCount, args: localPos, args: globalPos, args: value, args: delta);
1022}
1023#endif // QT_NO_GESTURES
1024
1025void QWindowSystemInterface::handlePlatformPanelEvent(QWindow *w)
1026{
1027 handleWindowSystemEvent<QWindowSystemInterfacePrivate::PlatformPanelEvent>(args: w);
1028}
1029
1030#ifndef QT_NO_CONTEXTMENU
1031QT_DEFINE_QPA_EVENT_HANDLER(bool, handleContextMenuEvent, QWindow *window, bool mouseTriggered,
1032 const QPoint &pos, const QPoint &globalPos,
1033 Qt::KeyboardModifiers modifiers)
1034{
1035 return handleWindowSystemEvent<QWindowSystemInterfacePrivate::ContextMenuEvent, Delivery>(
1036 window, mouseTriggered, pos, globalPos, modifiers);
1037}
1038#endif
1039
1040#if QT_CONFIG(whatsthis)
1041void QWindowSystemInterface::handleEnterWhatsThisEvent()
1042{
1043 handleWindowSystemEvent<QWindowSystemInterfacePrivate::WindowSystemEvent>(
1044 args: QWindowSystemInterfacePrivate::EnterWhatsThisMode);
1045}
1046#endif
1047
1048#ifndef QT_NO_DEBUG_STREAM
1049Q_GUI_EXPORT QDebug operator<<(QDebug dbg, const QWindowSystemInterface::TouchPoint &p)
1050{
1051 QDebugStateSaver saver(dbg);
1052 dbg.nospace() << "TouchPoint(" << p.id << " @" << p.area << " normalized " << p.normalPosition
1053 << " press " << p.pressure << " vel " << p.velocity << " state " << (int)p.state;
1054 return dbg;
1055}
1056#endif
1057
1058// ------------------ Event dispatcher functionality ------------------
1059
1060/*!
1061 Make Qt Gui process all events on the event queue immediately. Return the
1062 accepted state for the last event on the queue.
1063*/
1064bool QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ProcessEventsFlags flags)
1065{
1066 const qsizetype count = QWindowSystemInterfacePrivate::windowSystemEventQueue.count();
1067 if (!count)
1068 return false;
1069 if (!QGuiApplication::instance()) {
1070 qWarning().nospace()
1071 << "QWindowSystemInterface::flushWindowSystemEvents() invoked after "
1072 "QGuiApplication destruction, discarding " << count << " events.";
1073 QWindowSystemInterfacePrivate::windowSystemEventQueue.clear();
1074 return false;
1075 }
1076 if (QThread::currentThread() != QGuiApplication::instance()->thread()) {
1077 // Post a FlushEvents event which will trigger a call back to
1078 // deferredFlushWindowSystemEvents from the Gui thread.
1079 QMutexLocker locker(&QWindowSystemInterfacePrivate::flushEventMutex);
1080 handleWindowSystemEvent<QWindowSystemInterfacePrivate::FlushEventsEvent, AsynchronousDelivery>(args: flags);
1081 QWindowSystemInterfacePrivate::eventsFlushed.wait(lockedMutex: &QWindowSystemInterfacePrivate::flushEventMutex);
1082 } else {
1083 sendWindowSystemEvents(flags);
1084 }
1085 return QWindowSystemInterfacePrivate::eventAccepted.loadRelaxed() > 0;
1086}
1087
1088void QWindowSystemInterface::deferredFlushWindowSystemEvents(QEventLoop::ProcessEventsFlags flags)
1089{
1090 Q_ASSERT(QThread::currentThread() == QGuiApplication::instance()->thread());
1091
1092 QMutexLocker locker(&QWindowSystemInterfacePrivate::flushEventMutex);
1093 sendWindowSystemEvents(flags);
1094 QWindowSystemInterfacePrivate::eventsFlushed.wakeOne();
1095}
1096
1097bool QWindowSystemInterface::sendWindowSystemEvents(QEventLoop::ProcessEventsFlags flags)
1098{
1099 int nevents = 0;
1100
1101 while (QWindowSystemInterfacePrivate::windowSystemEventsQueued()) {
1102 QWindowSystemInterfacePrivate::WindowSystemEvent *event =
1103 flags & QEventLoop::ExcludeUserInputEvents ?
1104 QWindowSystemInterfacePrivate::getNonUserInputWindowSystemEvent() :
1105 QWindowSystemInterfacePrivate::getWindowSystemEvent();
1106 if (!event)
1107 break;
1108
1109 if (QWindowSystemInterfacePrivate::eventHandler) {
1110 if (QWindowSystemInterfacePrivate::eventHandler->sendEvent(e: event))
1111 nevents++;
1112 } else {
1113 nevents++;
1114 QGuiApplicationPrivate::processWindowSystemEvent(e: event);
1115 }
1116
1117 // Record the accepted state for the processed event
1118 // (excluding flush events). This state can then be
1119 // returned by flushWindowSystemEvents().
1120 if (event->type != QWindowSystemInterfacePrivate::FlushEvents)
1121 QWindowSystemInterfacePrivate::eventAccepted.storeRelaxed(newValue: event->eventAccepted);
1122
1123 delete event;
1124 }
1125
1126 return (nevents > 0);
1127}
1128
1129void QWindowSystemInterface::setSynchronousWindowSystemEvents(bool enable)
1130{
1131 QWindowSystemInterfacePrivate::synchronousWindowSystemEvents = enable;
1132}
1133
1134int QWindowSystemInterface::windowSystemEventsQueued()
1135{
1136 return QWindowSystemInterfacePrivate::windowSystemEventsQueued();
1137}
1138
1139bool QWindowSystemInterface::nonUserInputEventsQueued()
1140{
1141 return QWindowSystemInterfacePrivate::nonUserInputEventsQueued();
1142}
1143
1144// --------------------- QtTestLib support ---------------------
1145
1146// The following functions are used by testlib, and need to be synchronous to avoid
1147// race conditions with plugins delivering native events from secondary threads.
1148// FIXME: It seems unnecessary to export these wrapper functions, when qtestlib could access
1149// QWindowSystemInterface directly (by adding dependency to gui-private), see QTBUG-63146.
1150
1151Q_GUI_EXPORT void qt_handleMouseEvent(QWindow *window, const QPointF &local, const QPointF &global,
1152 Qt::MouseButtons state, Qt::MouseButton button,
1153 QEvent::Type type, Qt::KeyboardModifiers mods, int timestamp)
1154{
1155 QPointF nativeLocal = QHighDpi::toNativeLocalPosition(value: local, context: window);
1156 QPointF nativeGlobal = QHighDpi::toNativeGlobalPosition(value: global, context: window);
1157 QWindowSystemInterface::handleMouseEvent<QWindowSystemInterface::SynchronousDelivery>(window,
1158 timestamp, local: nativeLocal, global: nativeGlobal, state, button, type, mods);
1159}
1160
1161/*
1162 Used by QTest::simulateEvent() to synthesize key events during testing
1163*/
1164Q_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)
1165{
1166#if defined(Q_OS_MACOS)
1167 // FIXME: Move into QTest::simulateEvent() and align with QGuiApplicationPrivate::processKeyEvent()
1168 auto timestamp = QWindowSystemInterfacePrivate::eventTime.elapsed();
1169 if (t == QEvent::KeyPress && QWindowSystemInterface::handleShortcutEvent(window, timestamp, k, mods, 0, 0, 0, text, autorep, count))
1170 return;
1171#endif
1172
1173 QWindowSystemInterface::handleKeyEvent<QWindowSystemInterface::SynchronousDelivery>(window, t, k, mods, text, autorep, count);
1174}
1175
1176Q_GUI_EXPORT bool qt_sendShortcutOverrideEvent(QObject *o, ulong timestamp, int k, Qt::KeyboardModifiers mods, const QString &text = QString(), bool autorep = false, ushort count = 1)
1177{
1178#if QT_CONFIG(shortcut)
1179
1180 // FIXME: This method should not allow targeting a specific object, but should
1181 // instead forward the event to a window, which then takes care of normal event
1182 // propagation. We need to fix a lot of tests before we can refactor this (the
1183 // window needs to be exposed and active and have a focus object), so we leave
1184 // it as is for now. See QTBUG-48577.
1185
1186 QGuiApplicationPrivate::modifier_buttons = mods;
1187
1188 QKeyEvent qevent(QEvent::ShortcutOverride, k, mods, text, autorep, count);
1189 qevent.setTimestamp(timestamp);
1190
1191 QShortcutMap &shortcutMap = QGuiApplicationPrivate::instance()->shortcutMap;
1192 if (shortcutMap.state() == QKeySequence::NoMatch) {
1193 // Try sending as QKeyEvent::ShortcutOverride first
1194 QCoreApplication::sendEvent(receiver: o, event: &qevent);
1195 if (qevent.isAccepted())
1196 return false;
1197 }
1198
1199 // Then as QShortcutEvent
1200 return shortcutMap.tryShortcut(e: &qevent);
1201#else
1202 Q_UNUSED(o);
1203 Q_UNUSED(timestamp);
1204 Q_UNUSED(k);
1205 Q_UNUSED(mods);
1206 Q_UNUSED(text);
1207 Q_UNUSED(autorep);
1208 Q_UNUSED(count);
1209 return false;
1210#endif
1211}
1212
1213Q_GUI_EXPORT void qt_handleWheelEvent(QWindow *window, const QPointF &local, const QPointF &global,
1214 QPoint pixelDelta, QPoint angleDelta, Qt::KeyboardModifiers mods,
1215 Qt::ScrollPhase phase)
1216{
1217 QWindowSystemInterface::handleWheelEvent(window, local, global, pixelDelta, angleDelta, mods, phase);
1218}
1219
1220namespace QTest
1221{
1222 Q_GUI_EXPORT QPointingDevice * createTouchDevice(QInputDevice::DeviceType devType,
1223 QInputDevice::Capabilities caps)
1224 {
1225 static qint64 nextId = 0x100000000;
1226 QPointingDevice *ret = new QPointingDevice("test touch device"_L1, nextId++,
1227 devType, QPointingDevice::PointerType::Finger,
1228 caps, 8, 0);
1229 QWindowSystemInterface::registerInputDevice(device: ret);
1230 return ret;
1231 }
1232}
1233
1234Q_GUI_EXPORT bool qt_handleTouchEventv2(QWindow *window, const QPointingDevice *device,
1235 const QList<QEventPoint> &points,
1236 Qt::KeyboardModifiers mods)
1237{
1238 return QWindowSystemInterface::handleTouchEvent<QWindowSystemInterface::SynchronousDelivery>(window, device,
1239 points: QWindowSystemInterfacePrivate::toNativeTouchPoints(pointList: points, window), mods);
1240}
1241
1242Q_GUI_EXPORT void qt_handleTouchEvent(QWindow *window, const QPointingDevice *device,
1243 const QList<QEventPoint> &points,
1244 Qt::KeyboardModifiers mods)
1245{
1246 qt_handleTouchEventv2(window, device, points, mods);
1247}
1248
1249QT_END_NAMESPACE
1250

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

source code of qtbase/src/gui/kernel/qwindowsysteminterface.cpp