int QEventDispatcherBlackberry::select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, timespec *timeout) { Q_UNUSED(nfds); Q_D(QEventDispatcherBlackberry); const BBScopedLoopLevelCounter bbLoopCounter(d); BpsChannelScopeSwitcher channelSwitcher(d->bps_channel); // prepare file sets for bps callback d->ioData->count = 0; d->ioData->readfds = readfds; d->ioData->writefds = writefds; d->ioData->exceptfds = exceptfds; // reset all file sets if (readfds) FD_ZERO(readfds); if (writefds) FD_ZERO(writefds); if (exceptfds) FD_ZERO(exceptfds); bps_event_t *event = 0; unsigned int eventCount = 0; // If an event handler called through filterEvent() starts a nested event loop by creating a // new QEventLoop, we will recursively enter this function again. However, each time // bps_get_event() is called, it destroys the last event it handed out before returning the // next event. We don't want it to destroy the event that triggered the nested event loop, // since there may still be more handlers that need to get that event, once the nested event // loop is done and control returns to the outer event loop. // // So we move an event to a holding channel, which takes ownership of the event. Putting // the event on our own channel allows us to manage when it is destroyed, keeping it alive // until we know we are done with it. Each recursive call of this function needs to have // it's own holding channel, since a channel is a queue, not a stack. // // However, a recursive call into this function happens very rarely compared to the many // times this function is called. We don't want to create a holding channel for each time // this function is called, only when it is called recursively. Thus we have the instance // variable d->holding_channel to use in the common case. We keep track of recursive calls // with d->loop_level. If we are in a recursive call, then we create a new holding channel // for this run. int holding_channel = d->holding_channel; if ((d->loop_level > 1) && Q_UNLIKELY(bps_channel_create(&holding_channel, 0) != BPS_SUCCESS)) { qWarning("QEventDispatcherBlackberry: bps_channel_create failed"); holding_channel = -1; } // Convert timeout to milliseconds int timeoutTotal = -1; if (timeout) timeoutTotal = timespecToMillisecs(*timeout); int timeoutLeft = timeoutTotal; timespec startTime = qt_gettime(); // This loop exists such that we can drain the bps event queue of all native events // more efficiently than if we were to return control to Qt after each event. This // is important for handling touch events which can come in rapidly. forever { // Only emit the awake() and aboutToBlock() signals in the second iteration. For the // first iteration, the UNIX event dispatcher will have taken care of that already. // Also native events are actually processed one loop iteration after they were // retrieved with bps_get_event(). // Filtering the native event should happen between the awake() and aboutToBlock() // signal emissions. The calls awake() - filterNativeEvent() - aboutToBlock() - // bps_get_event() need not to be interrupted by a break or return statement. if (eventCount > 0) { if (event) { emit awake(); filterNativeEvent(QByteArrayLiteral("bps_event_t"), static_cast<void*>(event), 0); emit aboutToBlock(); if (Q_LIKELY(holding_channel != -1)) { // We are now done with this BPS event. Destroy it. destroyHeldBpsEvent(holding_channel); } } // Update the timeout // Clock source is monotonic, so we can recalculate how much timeout is left if (timeoutTotal != -1) { timespec t2 = qt_gettime(); timeoutLeft = timeoutTotal - (timespecToMillisecs(t2) - timespecToMillisecs(startTime)); if (timeoutLeft < 0) timeoutLeft = 0; } timespec tnext; if (d->timerList.timerWait(tnext)) { int timeoutNext = timespecToMillisecs(tnext); if (timeoutNext < timeoutLeft || timeoutTotal == -1) { timeoutTotal = timeoutLeft = timeoutNext; startTime = qt_gettime(); } } } event = 0; { // We need to increase loop level in this scope, // because bps_get_event can also invoke callbacks QScopedLoopLevelCounter loopLevelCounter(d->threadData); // Wait for event or file to be ready const int result = bps_get_event(&event, timeoutLeft); if (Q_UNLIKELY(result != BPS_SUCCESS)) qWarning("QEventDispatcherBlackberry: bps_get_event failed"); } if (!event) // In case of !event, we break out of the loop to let Qt process the timers break; // (since timeout has expired) and socket notifiers that are now ready. if (bps_event_get_domain(event) == bpsUnblockDomain) { timeoutTotal = 0; // in order to immediately drain the event queue of native events event = 0; // (especially touch move events) we don't break out here } else { // Move the event to our holding channel so we can manage when it is destroyed. if (Q_LIKELY(holding_channel != 1) && Q_UNLIKELY(bps_channel_push_event(holding_channel, event) != BPS_SUCCESS)) { qWarning("QEventDispatcherBlackberry: bps_channel_push_event failed"); } } ++eventCount; // Make sure we are not trapped in this loop due to continuous native events // also we cannot recalculate the timeout without a monotonic clock as the time may have changed const unsigned int maximumEventCount = 12; if (Q_UNLIKELY((eventCount > maximumEventCount && timeoutLeft == 0) || !QElapsedTimer::isMonotonic())) { if (event) { filterNativeEvent(QByteArrayLiteral("bps_event_t"), static_cast<void*>(event), 0); if (Q_LIKELY(holding_channel != -1)) { // We are now done with this BPS event. Destroy it. destroyHeldBpsEvent(holding_channel); } } break; } } // If this was a recursive call into this function, a new holding channel was created for // this run, so destroy it now. if ((holding_channel != d->holding_channel) && Q_LIKELY(holding_channel != -1) && Q_UNLIKELY(bps_channel_destroy(holding_channel) != BPS_SUCCESS)) { qWarning("QEventDispatcherBlackberry: bps_channel_destroy failed"); } // the number of bits set in the file sets return d->ioData->count; }
bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags) { Q_D(QEventDispatcherWin32); if (!d->internalHwnd) createInternalHwnd(); d->interrupt = false; emit awake(); bool canWait; bool retVal = false; bool seenWM_QT_SENDPOSTEDEVENTS = false; bool needWM_QT_SENDPOSTEDEVENTS = false; do { DWORD waitRet = 0; HANDLE pHandles[MAXIMUM_WAIT_OBJECTS - 1]; QVarLengthArray<MSG> processedTimers; while (!d->interrupt) { DWORD nCount = d->winEventNotifierList.count(); Q_ASSERT(nCount < MAXIMUM_WAIT_OBJECTS - 1); MSG msg; bool haveMessage; if (!(flags & QEventLoop::ExcludeUserInputEvents) && !d->queuedUserInputEvents.isEmpty()) { // process queued user input events haveMessage = true; msg = d->queuedUserInputEvents.takeFirst(); } else if(!(flags & QEventLoop::ExcludeSocketNotifiers) && !d->queuedSocketEvents.isEmpty()) { // process queued socket events haveMessage = true; msg = d->queuedSocketEvents.takeFirst(); } else { haveMessage = PeekMessage(&msg, 0, 0, 0, PM_REMOVE); if (haveMessage && (flags & QEventLoop::ExcludeUserInputEvents) && ((msg.message >= WM_KEYFIRST && msg.message <= WM_KEYLAST) || (msg.message >= WM_MOUSEFIRST && msg.message <= WM_MOUSELAST) || msg.message == WM_MOUSEWHEEL || msg.message == WM_MOUSEHWHEEL || msg.message == WM_TOUCH #ifndef QT_NO_GESTURES || msg.message == WM_GESTURE || msg.message == WM_GESTURENOTIFY #endif || msg.message == WM_CLOSE)) { // queue user input events for later processing haveMessage = false; d->queuedUserInputEvents.append(msg); } if (haveMessage && (flags & QEventLoop::ExcludeSocketNotifiers) && (msg.message == WM_QT_SOCKETNOTIFIER && msg.hwnd == d->internalHwnd)) { // queue socket events for later processing haveMessage = false; d->queuedSocketEvents.append(msg); } } if (!haveMessage) { // no message - check for signalled objects for (int i=0; i<(int)nCount; i++) pHandles[i] = d->winEventNotifierList.at(i)->handle(); waitRet = MsgWaitForMultipleObjectsEx(nCount, pHandles, 0, QS_ALLINPUT, MWMO_ALERTABLE); if ((haveMessage = (waitRet == WAIT_OBJECT_0 + nCount))) { // a new message has arrived, process it continue; } } if (haveMessage) { #ifdef Q_OS_WINCE // WinCE doesn't support hooks at all, so we have to call this by hand :( (void) qt_GetMessageHook(0, PM_REMOVE, (LPARAM) &msg); #endif if (d->internalHwnd == msg.hwnd && msg.message == WM_QT_SENDPOSTEDEVENTS) { if (seenWM_QT_SENDPOSTEDEVENTS) { // when calling processEvents() "manually", we only want to send posted // events once needWM_QT_SENDPOSTEDEVENTS = true; continue; } seenWM_QT_SENDPOSTEDEVENTS = true; } else if (msg.message == WM_TIMER) { // avoid live-lock by keeping track of the timers we've already sent bool found = false; for (int i = 0; !found && i < processedTimers.count(); ++i) { const MSG processed = processedTimers.constData()[i]; found = (processed.wParam == msg.wParam && processed.hwnd == msg.hwnd && processed.lParam == msg.lParam); } if (found) continue; processedTimers.append(msg); } else if (msg.message == WM_QUIT) { if (QCoreApplication::instance()) QCoreApplication::instance()->quit(); return false; } if (!filterNativeEvent(QByteArrayLiteral("windows_generic_MSG"), &msg, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } } else if (waitRet - WAIT_OBJECT_0 < nCount) { d->activateEventNotifier(d->winEventNotifierList.at(waitRet - WAIT_OBJECT_0)); } else { // nothing todo so break break; } retVal = true; } // still nothing - wait for message or signalled objects canWait = (!retVal && !d->interrupt && (flags & QEventLoop::WaitForMoreEvents)); if (canWait) { DWORD nCount = d->winEventNotifierList.count(); Q_ASSERT(nCount < MAXIMUM_WAIT_OBJECTS - 1); for (int i=0; i<(int)nCount; i++) pHandles[i] = d->winEventNotifierList.at(i)->handle(); emit aboutToBlock(); waitRet = MsgWaitForMultipleObjectsEx(nCount, pHandles, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE | MWMO_INPUTAVAILABLE); emit awake(); if (waitRet - WAIT_OBJECT_0 < nCount) { d->activateEventNotifier(d->winEventNotifierList.at(waitRet - WAIT_OBJECT_0)); retVal = true; } } } while (canWait); if (!seenWM_QT_SENDPOSTEDEVENTS && (flags & QEventLoop::EventLoopExec) == 0) { // when called "manually", always send posted events sendPostedEvents(); } if (needWM_QT_SENDPOSTEDEVENTS) PostMessage(d->internalHwnd, WM_QT_SENDPOSTEDEVENTS, 0, 0); return retVal; }