Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qeventdispatcher_win.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// Copyright (C) 2016 Intel Corporation.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
6
8#include <private/qsystemlibrary_p.h>
10#include "qset.h"
13
14#include "qelapsedtimer.h"
16#include <private/qthread_p.h>
17
18#include "q26numeric.h"
19
21
22#ifndef TIME_KILL_SYNCHRONOUS
23# define TIME_KILL_SYNCHRONOUS 0x0100
24#endif
25
26#ifndef QS_RAWINPUT
27# define QS_RAWINPUT 0x0400
28#endif
29
30#ifndef WM_TOUCH
31# define WM_TOUCH 0x0240
32#endif
33#ifndef QT_NO_GESTURES
34#ifndef WM_GESTURE
35# define WM_GESTURE 0x0119
36#endif
37#ifndef WM_GESTURENOTIFY
38# define WM_GESTURENOTIFY 0x011A
39#endif
40#endif // QT_NO_GESTURES
41
42enum {
46};
47
48enum {
50};
51
52class QEventDispatcherWin32Private;
53
54#if !defined(DWORD_PTR) && !defined(Q_OS_WIN64)
55#define DWORD_PTR DWORD
56#endif
57
58LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp);
59
61{
62 using namespace std::chrono;
63 auto t = duration_cast<milliseconds>(steady_clock::now().time_since_epoch());
64 return t.count();
65}
66
67QEventDispatcherWin32Private::QEventDispatcherWin32Private()
68 : interrupt(false), internalHwnd(0),
69 sendPostedEventsTimerId(0), wakeUps(0),
70 activateNotifiersPosted(false)
71{
72}
73
74QEventDispatcherWin32Private::~QEventDispatcherWin32Private()
75{
76 if (internalHwnd)
77 DestroyWindow(internalHwnd);
78}
79
80// This function is called by a workerthread
81void WINAPI QT_WIN_CALLBACK qt_fast_timer_proc(uint timerId, uint /*reserved*/, DWORD_PTR user, DWORD_PTR /*reserved*/, DWORD_PTR /*reserved*/)
82{
83 if (!timerId) // sanity check
84 return;
85 auto t = reinterpret_cast<WinTimerInfo*>(user);
86 Q_ASSERT(t);
87 QCoreApplication::postEvent(t->dispatcher, new QTimerEvent(t->timerId));
88}
89
90LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp)
91{
92 if (message == WM_NCCREATE)
93 return true;
94
95 MSG msg;
96 msg.hwnd = hwnd;
97 msg.message = message;
98 msg.wParam = wp;
99 msg.lParam = lp;
100 QAbstractEventDispatcher* dispatcher = QAbstractEventDispatcher::instance();
101 qintptr result;
102 if (!dispatcher) {
103 if (message == WM_TIMER)
104 KillTimer(hwnd, wp);
105 return 0;
106 }
107 if (dispatcher->filterNativeEvent(QByteArrayLiteral("windows_dispatcher_MSG"), &msg, &result))
108 return result;
109
110 auto q = reinterpret_cast<QEventDispatcherWin32 *>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
111 QEventDispatcherWin32Private *d = nullptr;
112 if (q != nullptr)
113 d = q->d_func();
114
115 switch (message) {
116 case WM_QT_SOCKETNOTIFIER: {
117 // socket notifier message
118 int type = -1;
119 switch (WSAGETSELECTEVENT(lp)) {
120 case FD_READ:
121 case FD_ACCEPT:
122 type = 0;
123 break;
124 case FD_WRITE:
125 case FD_CONNECT:
126 type = 1;
127 break;
128 case FD_OOB:
129 type = 2;
130 break;
131 case FD_CLOSE:
132 type = 3;
133 break;
134 }
135 if (type >= 0) {
136 Q_ASSERT(d != nullptr);
137 QSNDict *sn_vec[4] = { &d->sn_read, &d->sn_write, &d->sn_except, &d->sn_read };
138 QSNDict *dict = sn_vec[type];
139
140 QSockNot *sn = dict ? dict->value(qintptr(wp)) : 0;
141 if (sn == nullptr) {
142 d->postActivateSocketNotifiers();
143 } else {
144 Q_ASSERT(d->active_fd.contains(sn->fd));
145 QSockFd &sd = d->active_fd[sn->fd];
146 if (sd.selected) {
147 Q_ASSERT(sd.mask == 0);
148 d->doWsaAsyncSelect(sn->fd, 0);
149 sd.selected = false;
150 }
151 d->postActivateSocketNotifiers();
152
153 // Ignore the message if a notification with the same type was
154 // received previously. Suppressed message is definitely spurious.
155 const long eventCode = WSAGETSELECTEVENT(lp);
156 if ((sd.mask & eventCode) != eventCode) {
157 sd.mask |= eventCode;
158 QEvent event(type < 3 ? QEvent::SockAct : QEvent::SockClose);
159 QCoreApplication::sendEvent(sn->obj, &event);
160 }
161 }
162 }
163 return 0;
164 }
165 case WM_QT_ACTIVATENOTIFIERS: {
166 Q_ASSERT(d != nullptr);
167
168 // Postpone activation if we have unhandled socket notifier messages
169 // in the queue. WM_QT_ACTIVATENOTIFIERS will be posted again as a result of
170 // event processing.
171 MSG msg;
172 if (!PeekMessage(&msg, d->internalHwnd,
173 WM_QT_SOCKETNOTIFIER, WM_QT_SOCKETNOTIFIER, PM_NOREMOVE)
174 && d->queuedSocketEvents.isEmpty()) {
175 // register all socket notifiers
176 for (QSFDict::iterator it = d->active_fd.begin(), end = d->active_fd.end();
177 it != end; ++it) {
178 QSockFd &sd = it.value();
179 if (!sd.selected) {
180 d->doWsaAsyncSelect(it.key(), sd.event);
181 // allow any event to be accepted
182 sd.mask = 0;
183 sd.selected = true;
184 }
185 }
186 }
187 d->activateNotifiersPosted = false;
188 return 0;
189 }
190 case WM_TIMER:
191 Q_ASSERT(d != nullptr);
192
193 if (wp == d->sendPostedEventsTimerId)
194 q->sendPostedEvents();
195 else
196 d->sendTimerEvent(wp);
197 return 0;
198 case WM_QT_SENDPOSTEDEVENTS:
199 Q_ASSERT(d != nullptr);
200
201 // We send posted events manually, if the window procedure was invoked
202 // by the foreign event loop (e.g. from the native modal dialog).
203 // Skip sending, if the message queue is not empty.
204 // sendPostedEventsTimer will deliver posted events later.
205 static const UINT mask = QS_ALLEVENTS;
206 if (HIWORD(GetQueueStatus(mask)) == 0)
207 q->sendPostedEvents();
208 else
209 d->startPostedEventsTimer();
210 return 0;
211 } // switch (message)
212
213 return DefWindowProc(hwnd, message, wp, lp);
214}
215
216void QEventDispatcherWin32Private::startPostedEventsTimer()
217{
218 // we received WM_QT_SENDPOSTEDEVENTS, so allow posting it again
219 wakeUps.storeRelaxed(0);
220 if (sendPostedEventsTimerId == 0) {
221 // Start a timer to deliver posted events when the message queue is emptied.
222 sendPostedEventsTimerId = SetTimer(internalHwnd, SendPostedEventsTimerId,
223 USER_TIMER_MINIMUM, NULL);
224 }
225}
226
227// Provide class name and atom for the message window used by
228// QEventDispatcherWin32Private via Q_GLOBAL_STATIC shared between threads.
237
239 : atom(0), className(0)
240{
241 // make sure that multiple Qt's can coexist in the same process
242 const QString qClassName = QStringLiteral("QEventDispatcherWin32_Internal_Widget")
243 + QString::number(quintptr(qt_internal_proc));
244 className = new wchar_t[qClassName.size() + 1];
245 qClassName.toWCharArray(className);
246 className[qClassName.size()] = 0;
247
248 WNDCLASS wc;
249 wc.style = 0;
250 wc.lpfnWndProc = qt_internal_proc;
251 wc.cbClsExtra = 0;
252 wc.cbWndExtra = 0;
253 wc.hInstance = GetModuleHandle(0);
254 wc.hIcon = 0;
255 wc.hCursor = 0;
256 wc.hbrBackground = 0;
257 wc.lpszMenuName = NULL;
258 wc.lpszClassName = className;
259 atom = RegisterClass(&wc);
260 if (!atom) {
261 qErrnoWarning("%ls RegisterClass() failed", qUtf16Printable(qClassName));
262 delete [] className;
263 className = 0;
264 }
265}
266
268{
269 if (className) {
270 UnregisterClass(className, GetModuleHandle(0));
271 delete [] className;
272 }
273}
274
275Q_GLOBAL_STATIC(QWindowsMessageWindowClassContext, qWindowsMessageWindowClassContext)
276
277static HWND qt_create_internal_window(const QEventDispatcherWin32 *eventDispatcher)
278{
279 QWindowsMessageWindowClassContext *ctx = qWindowsMessageWindowClassContext();
280 if (!ctx->atom)
281 return 0;
282 HWND wnd = CreateWindow(ctx->className, // classname
283 ctx->className, // window name
284 0, // style
285 0, 0, 0, 0, // geometry
286 HWND_MESSAGE, // parent
287 0, // menu handle
288 GetModuleHandle(0), // application
289 0); // windows creation data.
290
291 if (!wnd) {
292 qErrnoWarning("CreateWindow() for QEventDispatcherWin32 internal window failed");
293 return 0;
294 }
295
296 SetWindowLongPtr(wnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(eventDispatcher));
297
298 return wnd;
299}
300
301static ULONG calculateNextTimeout(WinTimerInfo *t, quint64 currentTime)
302{
303 qint64 interval = t->interval;
304 ULONG tolerance = TIMERV_DEFAULT_COALESCING;
305 switch (t->timerType) {
306 case Qt::PreciseTimer:
307 // high precision timer is based on millisecond precision
308 // so no adjustment is necessary
309 break;
310
311 case Qt::CoarseTimer:
312 // this timer has up to 5% coarseness
313 // so our boundaries are 20 ms and 20 s
314 // below 20 ms, 5% inaccuracy is below 1 ms, so we convert to high precision
315 // above 20 s, 5% inaccuracy is above 1 s, so we convert to VeryCoarseTimer
316 if (interval >= 20000) {
317 t->timerType = Qt::VeryCoarseTimer;
318 } else if (interval <= 20) {
319 // no adjustment necessary
320 t->timerType = Qt::PreciseTimer;
321 break;
322 } else {
323 tolerance = interval / 20;
324 break;
325 }
326 Q_FALLTHROUGH();
327 case Qt::VeryCoarseTimer:
328 // the very coarse timer is based on full second precision,
329 // so we round to closest second (but never to zero)
330 tolerance = 1000;
331 if (interval < 1000)
332 interval = 1000;
333 else
334 interval = (interval + 500) / 1000 * 1000;
335 currentTime = currentTime / 1000 * 1000;
336 break;
337 }
338
339 t->interval = interval;
340 t->timeout = currentTime + interval;
341 return tolerance;
342}
343
344void QEventDispatcherWin32Private::registerTimer(WinTimerInfo *t)
345{
346 Q_ASSERT(internalHwnd);
347
348 Q_Q(QEventDispatcherWin32);
349
350 bool ok = false;
351 ULONG tolerance = calculateNextTimeout(t, qt_msectime());
352 uint interval = q26::saturate_cast<uint>(t->interval);
353 if (interval != t->interval) {
354 // Now we'll need to post the timer event at each second timeout.
355 interval = q26::saturate_cast<uint>(t->interval / 2 + 1);
356 t->usesExtendedInterval = true;
357 t->isSecondTimeout = false;
358 }
359 if (interval == 0u) {
360 // optimization for single-shot-zero-timer
361 QCoreApplication::postEvent(q, new QZeroTimerEvent(t->timerId));
362 ok = true;
363 } else if (tolerance == TIMERV_DEFAULT_COALESCING) {
364 // 3/2016: Although MSDN states timeSetEvent() is deprecated, the function
365 // is still deemed to be the most reliable precision timer.
366 t->fastTimerId = timeSetEvent(interval, 1, qt_fast_timer_proc, DWORD_PTR(t),
367 TIME_CALLBACK_FUNCTION | TIME_PERIODIC | TIME_KILL_SYNCHRONOUS);
368 ok = t->fastTimerId;
369 }
370
371 if (!ok) {
372 // user normal timers for (Very)CoarseTimers, or if no more multimedia timers available
373 ok = SetCoalescableTimer(internalHwnd, t->timerId, interval, nullptr, tolerance);
374 }
375 if (!ok)
376 ok = SetTimer(internalHwnd, t->timerId, interval, nullptr);
377
378 if (!ok)
379 qErrnoWarning("QEventDispatcherWin32::registerTimer: Failed to create a timer");
380}
381
382void QEventDispatcherWin32Private::unregisterTimer(WinTimerInfo *t)
383{
384 if (t->interval == 0) {
385 QCoreApplicationPrivate::removePostedTimerEvent(t->dispatcher, t->timerId);
386 } else if (t->fastTimerId != 0) {
387 timeKillEvent(t->fastTimerId);
388 QCoreApplicationPrivate::removePostedTimerEvent(t->dispatcher, t->timerId);
389 } else {
390 KillTimer(internalHwnd, t->timerId);
391 }
392 t->timerId = -1;
393 if (!t->inTimerEvent)
394 delete t;
395}
396
397void QEventDispatcherWin32Private::sendTimerEvent(int timerId)
398{
399 WinTimerInfo *t = timerDict.value(timerId);
400 if (t && !t->inTimerEvent) {
401 if (t->usesExtendedInterval && !t->isSecondTimeout) {
402 // That's the case when the interval is larger than uint
403 t->isSecondTimeout = true;
404 return;
405 }
406
407 // send event, but don't allow it to recurse
408 t->inTimerEvent = true;
409
410 // recalculate next emission
411 calculateNextTimeout(t, qt_msectime());
412
413 QTimerEvent e(t->timerId);
414 QCoreApplication::sendEvent(t->obj, &e);
415
416 // timer could have been removed
417 if (t->timerId == -1) {
418 delete t;
419 } else {
420 t->isSecondTimeout = false;
421 t->inTimerEvent = false;
422 }
423 }
424}
425
426void QEventDispatcherWin32Private::doWsaAsyncSelect(qintptr socket, long event)
427{
428 Q_ASSERT(internalHwnd);
429 // BoundsChecker may emit a warning for WSAAsyncSelect when event == 0
430 // This is a BoundsChecker bug and not a Qt bug
431 WSAAsyncSelect(socket, internalHwnd, event ? int(WM_QT_SOCKETNOTIFIER) : 0, event);
432}
433
434void QEventDispatcherWin32Private::postActivateSocketNotifiers()
435{
436 if (!activateNotifiersPosted)
437 activateNotifiersPosted = PostMessage(internalHwnd, WM_QT_ACTIVATENOTIFIERS, 0, 0);
438}
439
440QEventDispatcherWin32::QEventDispatcherWin32(QObject *parent)
441 : QEventDispatcherWin32(*new QEventDispatcherWin32Private, parent)
442{
443}
444
445QEventDispatcherWin32::QEventDispatcherWin32(QEventDispatcherWin32Private &dd, QObject *parent)
446 : QAbstractEventDispatcherV2(dd, parent)
447{
448 Q_D(QEventDispatcherWin32);
449
450 d->internalHwnd = qt_create_internal_window(this);
451}
452
453QEventDispatcherWin32::~QEventDispatcherWin32()
454{
455}
456
457static bool isUserInputMessage(UINT message)
458{
459 return (message >= WM_KEYFIRST && message <= WM_KEYLAST)
460 || (message >= WM_MOUSEFIRST && message <= WM_MOUSELAST)
461 || message == WM_MOUSEWHEEL
462 || message == WM_MOUSEHWHEEL
463 || message == WM_TOUCH
464#ifndef QT_NO_GESTURES
465 || message == WM_GESTURE
466 || message == WM_GESTURENOTIFY
467#endif
468// Pointer input: Exclude WM_NCPOINTERUPDATE .. WM_POINTERROUTEDRELEASED
469 || (message >= 0x0241 && message <= 0x0253)
470 || message == WM_CLOSE;
471}
472
473bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)
474{
475 Q_D(QEventDispatcherWin32);
476
477 // We don't know _when_ the interrupt occurred so we have to honor it.
478 const bool wasInterrupted = d->interrupt.fetchAndStoreRelaxed(false);
479 emit awake();
480
481 // To prevent livelocks, send posted events once per iteration.
482 // QCoreApplication::sendPostedEvents() takes care about recursions.
483 sendPostedEvents();
484
485 if (wasInterrupted)
486 return false;
487
488 auto threadData = d->threadData.loadRelaxed();
489 bool canWait;
490 bool retVal = false;
491 do {
492 QVarLengthArray<MSG> processedTimers;
493 while (!d->interrupt.loadRelaxed()) {
494 MSG msg;
495
496 if (!(flags & QEventLoop::ExcludeUserInputEvents) && !d->queuedUserInputEvents.isEmpty()) {
497 // process queued user input events
498 msg = d->queuedUserInputEvents.takeFirst();
499 } else if (!(flags & QEventLoop::ExcludeSocketNotifiers) && !d->queuedSocketEvents.isEmpty()) {
500 // process queued socket events
501 msg = d->queuedSocketEvents.takeFirst();
502 } else if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
503 if (flags.testFlag(QEventLoop::ExcludeUserInputEvents)
504 && isUserInputMessage(msg.message)) {
505 // queue user input events for later processing
506 d->queuedUserInputEvents.append(msg);
507 continue;
508 }
509 if ((flags & QEventLoop::ExcludeSocketNotifiers)
510 && (msg.message == WM_QT_SOCKETNOTIFIER && msg.hwnd == d->internalHwnd)) {
511 // queue socket events for later processing
512 d->queuedSocketEvents.append(msg);
513 continue;
514 }
515 } else if (MsgWaitForMultipleObjectsEx(0, NULL, 0, QS_ALLINPUT, MWMO_ALERTABLE)
516 == WAIT_OBJECT_0) {
517 // a new message has arrived, process it
518 continue;
519 } else {
520 // nothing to do, so break
521 break;
522 }
523
524 if (d->internalHwnd == msg.hwnd && msg.message == WM_QT_SENDPOSTEDEVENTS) {
525 d->startPostedEventsTimer();
526 // Set result to 'true' because the message was sent by wakeUp().
527 retVal = true;
528 continue;
529 }
530 if (msg.message == WM_TIMER) {
531 // Skip timer event intended for use inside foreign loop.
532 if (d->internalHwnd == msg.hwnd && msg.wParam == d->sendPostedEventsTimerId)
533 continue;
534
535 // avoid live-lock by keeping track of the timers we've already sent
536 bool found = false;
537 for (int i = 0; !found && i < processedTimers.count(); ++i) {
538 const MSG processed = processedTimers.constData()[i];
539 found = (processed.wParam == msg.wParam && processed.hwnd == msg.hwnd && processed.lParam == msg.lParam);
540 }
541 if (found)
542 continue;
543 processedTimers.append(msg);
544 } else if (msg.message == WM_QUIT) {
545 if (QCoreApplication::instance())
546 QCoreApplication::instance()->quit();
547 return false;
548 }
549
550 if (!filterNativeEvent(QByteArrayLiteral("windows_generic_MSG"), &msg, 0)) {
551 TranslateMessage(&msg);
552 DispatchMessage(&msg);
553 }
554 retVal = true;
555 }
556
557 // wait for message
558 canWait = (!retVal
559 && !d->interrupt.loadRelaxed()
560 && flags.testFlag(QEventLoop::WaitForMoreEvents)
561 && threadData->canWaitLocked());
562 if (canWait) {
563 emit aboutToBlock();
564 MsgWaitForMultipleObjectsEx(0, NULL, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE | MWMO_INPUTAVAILABLE);
565 emit awake();
566 }
567 } while (canWait);
568
569 return retVal;
570}
571
572void QEventDispatcherWin32::registerSocketNotifier(QSocketNotifier *notifier)
573{
574 Q_ASSERT(notifier);
575 qintptr sockfd = notifier->socket();
576 int type = notifier->type();
577#ifndef QT_NO_DEBUG
578 if (sockfd < 0) {
579 qWarning("QEventDispatcherWin32::registerSocketNotifier: invalid socket identifier");
580 return;
581 }
582 if (notifier->thread() != thread() || thread() != QThread::currentThread()) {
583 qWarning("QEventDispatcherWin32: socket notifiers cannot be enabled from another thread");
584 return;
585 }
586#endif
587
588 Q_D(QEventDispatcherWin32);
589 QSNDict *sn_vec[3] = { &d->sn_read, &d->sn_write, &d->sn_except };
590 QSNDict *dict = sn_vec[type];
591
592 if (QCoreApplication::closingDown()) // ### d->exitloop?
593 return; // after sn_cleanup, don't reinitialize.
594
595 if (dict->contains(sockfd)) {
596 const char *t[] = { "Read", "Write", "Exception" };
597 /* Variable "socket" below is a function pointer. */
598 qWarning("QSocketNotifier: Multiple socket notifiers for "
599 "same socket %" PRIdQINTPTR " and type %s", sockfd, t[type]);
600 }
601
602 QSockNot *sn = new QSockNot;
603 sn->obj = notifier;
604 sn->fd = sockfd;
605 dict->insert(sn->fd, sn);
606
607 long event = 0;
608 if (d->sn_read.contains(sockfd))
609 event |= FD_READ | FD_CLOSE | FD_ACCEPT;
610 if (d->sn_write.contains(sockfd))
611 event |= FD_WRITE | FD_CONNECT;
612 if (d->sn_except.contains(sockfd))
613 event |= FD_OOB;
614
615 QSFDict::iterator it = d->active_fd.find(sockfd);
616 if (it != d->active_fd.end()) {
617 QSockFd &sd = it.value();
618 if (sd.selected) {
619 d->doWsaAsyncSelect(sockfd, 0);
620 sd.selected = false;
621 }
622 sd.event |= event;
623 } else {
624 // Although WSAAsyncSelect(..., 0), which is called from
625 // unregisterSocketNotifier(), immediately disables event message
626 // posting for the socket, it is possible that messages could be
627 // waiting in the application message queue even if the socket was
628 // closed. Also, some events could be implicitly re-enabled due
629 // to system calls. Ignore these superfluous events until all
630 // pending notifications have been suppressed. Next activation of
631 // socket notifiers will reset the mask.
632 d->active_fd.insert(sockfd, QSockFd(event, FD_READ | FD_CLOSE | FD_ACCEPT | FD_WRITE
633 | FD_CONNECT | FD_OOB));
634 }
635
636 d->postActivateSocketNotifiers();
637}
638
639void QEventDispatcherWin32::unregisterSocketNotifier(QSocketNotifier *notifier)
640{
641 Q_ASSERT(notifier);
642#ifndef QT_NO_DEBUG
643 qintptr sockfd = notifier->socket();
644 if (sockfd < 0) {
645 qWarning("QEventDispatcherWin32::unregisterSocketNotifier: invalid socket identifier");
646 return;
647 }
648 if (notifier->thread() != thread() || thread() != QThread::currentThread()) {
649 qWarning("QEventDispatcherWin32: socket notifiers cannot be disabled from another thread");
650 return;
651 }
652#endif
653 doUnregisterSocketNotifier(notifier);
654}
655
656void QEventDispatcherWin32::doUnregisterSocketNotifier(QSocketNotifier *notifier)
657{
658 Q_D(QEventDispatcherWin32);
659 int type = notifier->type();
660 qintptr sockfd = notifier->socket();
661 Q_ASSERT(sockfd >= 0);
662
663 QSFDict::iterator it = d->active_fd.find(sockfd);
664 if (it != d->active_fd.end()) {
665 QSockFd &sd = it.value();
666 if (sd.selected)
667 d->doWsaAsyncSelect(sockfd, 0);
668 const long event[3] = { FD_READ | FD_CLOSE | FD_ACCEPT, FD_WRITE | FD_CONNECT, FD_OOB };
669 sd.event ^= event[type];
670 if (sd.event == 0) {
671 d->active_fd.erase(it);
672 } else if (sd.selected) {
673 sd.selected = false;
674 d->postActivateSocketNotifiers();
675 }
676 }
677
678 QSNDict *sn_vec[3] = { &d->sn_read, &d->sn_write, &d->sn_except };
679 QSNDict *dict = sn_vec[type];
680 QSockNot *sn = dict->value(sockfd);
681 if (!sn)
682 return;
683
684 dict->remove(sockfd);
685 delete sn;
686}
687
688void QEventDispatcherWin32::registerTimer(Qt::TimerId timerId, Duration interval,
689 Qt::TimerType timerType, QObject *object)
690{
691#ifndef QT_NO_DEBUG
692 if (qToUnderlying(timerId) < 1 || interval.count() < 0 || !object) {
693 qWarning("QEventDispatcherWin32::registerTimer: invalid arguments");
694 return;
695 }
696 if (object->thread() != thread() || thread() != QThread::currentThread()) {
697 qWarning("QEventDispatcherWin32::registerTimer: timers cannot be started from another thread");
698 return;
699 }
700#endif
701
702 Q_D(QEventDispatcherWin32);
703
704 // exiting ... do not register new timers
705 // (QCoreApplication::closingDown() is set too late to be used here)
706 if (d->closingDown)
707 return;
708
709 using namespace std::chrono;
710
711 WinTimerInfo *t = new WinTimerInfo;
712 t->dispatcher = this;
713 t->timerId = qToUnderlying(timerId);
714 // Windows does not have a ns-resolution timer
715 t->interval = ceil<milliseconds>(interval).count();
716 t->timerType = timerType;
717 t->obj = object;
718 t->inTimerEvent = false;
719 t->fastTimerId = 0;
720 t->usesExtendedInterval = false;
721 t->isSecondTimeout = false;
722
723 d->registerTimer(t);
724
725 d->timerDict.insert(t->timerId, t); // store timers in dict
726}
727
728bool QEventDispatcherWin32::unregisterTimer(Qt::TimerId timerId)
729{
730#ifndef QT_NO_DEBUG
731 if (qToUnderlying(timerId) < 1) {
732 qWarning("QEventDispatcherWin32::unregisterTimer: invalid argument");
733 return false;
734 }
735 if (thread() != QThread::currentThread()) {
736 qWarning("QEventDispatcherWin32::unregisterTimer: timers cannot be stopped from another thread");
737 return false;
738 }
739#endif
740
741 Q_D(QEventDispatcherWin32);
742
743 WinTimerInfo *t = d->timerDict.take(qToUnderlying(timerId));
744 if (!t)
745 return false;
746
747 d->unregisterTimer(t);
748 return true;
749}
750
751bool QEventDispatcherWin32::unregisterTimers(QObject *object)
752{
753#ifndef QT_NO_DEBUG
754 if (!object) {
755 qWarning("QEventDispatcherWin32::unregisterTimers: invalid argument");
756 return false;
757 }
758 if (object->thread() != thread() || thread() != QThread::currentThread()) {
759 qWarning("QEventDispatcherWin32::unregisterTimers: timers cannot be stopped from another thread");
760 return false;
761 }
762#endif
763
764 Q_D(QEventDispatcherWin32);
765 if (d->timerDict.isEmpty())
766 return false;
767
768 auto it = d->timerDict.begin();
769 while (it != d->timerDict.end()) {
770 WinTimerInfo *t = it.value();
771 Q_ASSERT(t);
772 if (t->obj == object) {
773 it = d->timerDict.erase(it);
774 d->unregisterTimer(t);
775 } else {
776 ++it;
777 }
778 }
779 return true;
780}
781
782QList<QEventDispatcherWin32::TimerInfoV2>
783QEventDispatcherWin32::timersForObject(QObject *object) const
784{
785#ifndef QT_NO_DEBUG
786 if (!object) {
787 qWarning("QEventDispatcherWin32:registeredTimers: invalid argument");
788 return QList<TimerInfoV2>();
789 }
790#endif
791
792 Q_D(const QEventDispatcherWin32);
793 QList<TimerInfoV2> list;
794 for (WinTimerInfo *t : std::as_const(d->timerDict)) {
795 Q_ASSERT(t);
796 if (t->obj == object) {
797 list << TimerInfoV2{std::chrono::milliseconds{t->interval},
798 Qt::TimerId{t->timerId},
799 t->timerType};
800 }
801 }
802 return list;
803}
804
805QEventDispatcherWin32::Duration QEventDispatcherWin32::remainingTime(Qt::TimerId timerId) const
806{
807#ifndef QT_NO_DEBUG
808 if (qToUnderlying(timerId) < 1) {
809 qWarning("QEventDispatcherWin32::remainingTime: invalid argument");
810 return Duration::min();
811 }
812#endif
813
814 Q_D(const QEventDispatcherWin32);
815
816 WinTimerInfo *t = d->timerDict.value(qToUnderlying(timerId));
817 if (t) {
818 // timer found, return time to wait
819 using namespace std::chrono;
820 using namespace std::chrono_literals;
821 const auto currentTimeMs = duration_cast<milliseconds>(steady_clock::now().time_since_epoch());
822 const auto timeoutMs = t->timeout * 1ms;
823 return timeoutMs > currentTimeMs ? milliseconds{timeoutMs - currentTimeMs} : 0ms;
824 }
825
826#ifndef QT_NO_DEBUG
827 qWarning("QEventDispatcherWin32::remainingTime: timer id %d not found", qToUnderlying(timerId));
828#endif
829
830 return Duration::min();
831}
832
833void QEventDispatcherWin32::wakeUp()
834{
835 Q_D(QEventDispatcherWin32);
836 if (d->wakeUps.testAndSetRelaxed(0, 1)) {
837 // post a WM_QT_SENDPOSTEDEVENTS to this thread if there isn't one already pending
838 if (!PostMessage(d->internalHwnd, WM_QT_SENDPOSTEDEVENTS, 0, 0))
839 qErrnoWarning("QEventDispatcherWin32::wakeUp: Failed to post a message");
840 }
841}
842
843void QEventDispatcherWin32::interrupt()
844{
845 Q_D(QEventDispatcherWin32);
846 d->interrupt.storeRelaxed(true);
847 wakeUp();
848}
849
850void QEventDispatcherWin32::startingUp()
851{ }
852
853void QEventDispatcherWin32::closingDown()
854{
855 Q_D(QEventDispatcherWin32);
856
857 // clean up any socketnotifiers
858 while (!d->sn_read.isEmpty())
859 doUnregisterSocketNotifier((*(d->sn_read.begin()))->obj);
860 while (!d->sn_write.isEmpty())
861 doUnregisterSocketNotifier((*(d->sn_write.begin()))->obj);
862 while (!d->sn_except.isEmpty())
863 doUnregisterSocketNotifier((*(d->sn_except.begin()))->obj);
864 Q_ASSERT(d->active_fd.isEmpty());
865
866 // clean up any timers
867 for (WinTimerInfo *t : std::as_const(d->timerDict))
868 d->unregisterTimer(t);
869 d->timerDict.clear();
870
871 d->closingDown = true;
872
873 if (d->sendPostedEventsTimerId != 0)
874 KillTimer(d->internalHwnd, d->sendPostedEventsTimerId);
875 d->sendPostedEventsTimerId = 0;
876}
877
878bool QEventDispatcherWin32::event(QEvent *e)
879{
880 Q_D(QEventDispatcherWin32);
881 switch (e->type()) {
882 case QEvent::ZeroTimerEvent: {
883 QZeroTimerEvent *zte = static_cast<QZeroTimerEvent*>(e);
884 WinTimerInfo *t = d->timerDict.value(zte->timerId());
885 if (t) {
886 t->inTimerEvent = true;
887
888 QTimerEvent te(zte->timerId());
889 QCoreApplication::sendEvent(t->obj, &te);
890
891 // timer could have been removed
892 if (t->timerId == -1) {
893 delete t;
894 } else {
895 if (t->interval == 0 && t->inTimerEvent) {
896 // post the next zero timer event as long as the timer was not restarted
897 QCoreApplication::postEvent(this, new QZeroTimerEvent(zte->timerId()));
898 }
899
900 t->inTimerEvent = false;
901 }
902 }
903 return true;
904 }
905 case QEvent::Timer:
906 d->sendTimerEvent(static_cast<const QTimerEvent*>(e)->timerId());
907 break;
908 default:
909 break;
910 }
911 return QAbstractEventDispatcher::event(e);
912}
913
914void QEventDispatcherWin32::sendPostedEvents()
915{
916 Q_D(QEventDispatcherWin32);
917
918 if (d->sendPostedEventsTimerId != 0)
919 KillTimer(d->internalHwnd, d->sendPostedEventsTimerId);
920 d->sendPostedEventsTimerId = 0;
921
922 // Allow posting WM_QT_SENDPOSTEDEVENTS message.
923 d->wakeUps.storeRelaxed(0);
924
925 QCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData.loadRelaxed());
926}
927
928HWND QEventDispatcherWin32::internalHwnd()
929{
930 return d_func()->internalHwnd;
931}
932
933QT_END_NAMESPACE
934
935#include "moc_qeventdispatcher_win_p.cpp"
static HWND qt_create_internal_window(const QEventDispatcherWin32 *eventDispatcher)
static quint64 qt_msectime()
#define TIME_KILL_SYNCHRONOUS
#define WM_GESTURE
@ SendPostedEventsTimerId
#define WM_GESTURENOTIFY
#define DWORD_PTR
@ WM_QT_ACTIVATENOTIFIERS
@ WM_QT_SOCKETNOTIFIER
@ WM_QT_SENDPOSTEDEVENTS
#define WM_TOUCH
static bool isUserInputMessage(UINT message)
static ULONG calculateNextTimeout(WinTimerInfo *t, quint64 currentTime)