QT 之wayland 事件处理分析基于qt5wayland5.14.2
1. Qt wayland 初始化 接收鼠标/案件,触摸屏等事件事件
QWaylandNativeInterface : public QPlatformNativeInterface
在QWaylandNativeInterface 继承qpa 接口类QPlatformNativeInterface;
1.1 初始化鼠标:
void *QWaylandNativeInterface::nativeResourceForIntegration(const QByteArray &resourceString)
{QByteArray lowerCaseResource = resourceString.toLower();if (lowerCaseResource == "display" || lowerCaseResource == "wl_display" || lowerCaseResource == "nativedisplay")return m_integration->display()->wl_display();if (lowerCaseResource == "compositor")return const_cast<wl_compositor *>(m_integration->display()->wl_compositor());if (lowerCaseResource == "server_buffer_integration")return m_integration->serverBufferIntegration();if (lowerCaseResource == "egldisplay" && m_integration->clientBufferIntegration())return m_integration->clientBufferIntegration()->nativeResource(QWaylandClientBufferIntegration::EglDisplay);if (lowerCaseResource == "wl_seat")return m_integration->display()->defaultInputDevice()->wl_seat();if (lowerCaseResource == "wl_keyboard") {auto *keyboard = m_integration->display()->defaultInputDevice()->keyboard();if (keyboard)return keyboard->wl_keyboard();return nullptr;}if (lowerCaseResource == "wl_pointer") {auto *pointer = m_integration->display()->defaultInputDevice()->pointer();if (pointer)return pointer->wl_pointer();return nullptr;}if (lowerCaseResource == "wl_touch") {auto *touch = m_integration->display()->defaultInputDevice()->touch();if (touch)return touch->wl_touch();return nullptr;}return nullptr;
}
QWaylandInputDevice::Point 主要实现 QtWayland::wl_pointer 类函数
class Q_WAYLAND_CLIENT_WAYLAND_EXPORT wl_pointer{protected:virtual void pointer_enter(uint32_t serial, struct ::wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y);virtual void pointer_leave(uint32_t serial, struct ::wl_surface *surface);virtual void pointer_motion(uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y);virtual void pointer_button(uint32_t serial, uint32_t time, uint32_t button, uint32_t state);virtual void pointer_axis(uint32_t time, uint32_t axis, wl_fixed_t value);virtual void pointer_frame();virtual void pointer_axis_source(uint32_t axis_source);virtual void pointer_axis_stop(uint32_t time, uint32_t axis);virtual void pointer_axis_discrete(uint32_t axis, int32_t discrete);private:void init_listener();static const struct wl_pointer_listener m_wl_pointer_listener;static void handle_enter(void *data,struct ::wl_pointer *object,uint32_t serial,struct ::wl_surface *surface,wl_fixed_t surface_x,wl_fixed_t surface_y);static void handle_leave(void *data,struct ::wl_pointer *object,uint32_t serial,struct ::wl_surface *surface);static void handle_motion(void *data,struct ::wl_pointer *object,uint32_t time,wl_fixed_t surface_x,wl_fixed_t surface_y);static void handle_button(void *data,struct ::wl_pointer *object,uint32_t serial,uint32_t time,uint32_t button,uint32_t state);static void handle_axis(void *data,struct ::wl_pointer *object,uint32_t time,uint32_t axis,wl_fixed_t value);static void handle_frame(void *data,struct ::wl_pointer *object);static void handle_axis_source(void *data,struct ::wl_pointer *object,uint32_t axis_source);static void handle_axis_stop(void *data,struct ::wl_pointer *object,uint32_t time,uint32_t axis);static void handle_axis_discrete(void *data,struct ::wl_pointer *object,uint32_t axis,int32_t discrete);struct ::wl_pointer *m_wl_pointer;};
2. wl_pointer 类向weston 注册监听鼠标事件
const struct wl_pointer_listener wl_pointer::m_wl_pointer_listener = {wl_pointer::handle_enter,wl_pointer::handle_leave,wl_pointer::handle_motion,wl_pointer::handle_button,wl_pointer::handle_axis,wl_pointer::handle_frame,wl_pointer::handle_axis_source,wl_pointer::handle_axis_stop,wl_pointer::handle_axis_discrete,};void wl_pointer::init_listener(){wl_pointer_add_listener(m_wl_pointer, &m_wl_pointer_listener, this);}
weston 服务接收鼠标事件就调用m_wl_pointer_listener 函数集。
3. 事件产生流程:分析hand_enter 鼠标进入事件为例:
void wl_pointer::handle_enter(void *data,struct ::wl_pointer *object,uint32_t serial,struct ::wl_surface *surface,wl_fixed_t surface_x,wl_fixed_t surface_y){Q_UNUSED(object);static_cast<wl_pointer *>(data)->pointer_enter(serial,surface,surface_x,surface_y);}
hand_enter 处理调用虚函数pointer_enter。pointer_enter函数在QWaylandInputDevice::Pointe实现。
void QWaylandInputDevice::Pointer::pointer_enter(uint32_t serial, struct wl_surface *surface,wl_fixed_t sx, wl_fixed_t sy)
{if (!surface)return;QWaylandWindow *window = QWaylandWindow::fromWlSurface(surface);if (!window)return; // Ignore foreign surfacesif (mFocus) {qCWarning(lcQpaWayland) << "The compositor sent a wl_pointer.enter event before sending a"<< "leave event first, this is not allowed by the wayland protocol"<< "attempting to work around it by invalidating the current focus";invalidateFocus();}mFocus = window->waylandSurface();connect(mFocus.data(), &QObject::destroyed, this, &Pointer::handleFocusDestroyed);mSurfacePos = QPointF(wl_fixed_to_double(sx), wl_fixed_to_double(sy));mGlobalPos = window->window()->mapToGlobal(mSurfacePos.toPoint());mParent->mSerial = serial;mEnterSerial = serial;#if QT_CONFIG(cursor)// Depends on mEnterSerial being updatedupdateCursor();
#endifprintf("%s %d \n",__FUNCTION__,__LINE__);QWaylandWindow *grab = QWaylandWindow::mouseGrab();if (!grab)setFrameEvent(new EnterEvent(window, mSurfacePos, mGlobalPos));
}
setFrameEvent 发送EnterEvent 事件。
void QWaylandInputDevice::Pointer::setFrameEvent(QWaylandPointerEvent *event)
{qCDebug(lcQpaWaylandInput) << "Setting frame event " << event->type;if (mFrameData.event && mFrameData.event->type != event->type) {qCDebug(lcQpaWaylandInput) << "Flushing; previous was " << mFrameData.event->type;flushFrameEvent();}mFrameData.event = event;if (mParent->mVersion < WL_POINTER_FRAME_SINCE_VERSION) {qCDebug(lcQpaWaylandInput) << "Flushing new event; no frame event in this version";flushFrameEvent();}
}void QWaylandInputDevice::Pointer::flushFrameEvent()
{if (auto *event = mFrameData.event) {if (auto window = event->surface) {window->handleMouse(mParent, *event);} else if (mFrameData.event->type == QWaylandPointerEvent::Type::Release) {// If the window has been destroyed, we still need to report an up event, but it can't// be handled by the destroyed window (obviously), so send the event here instead.QWindowSystemInterface::handleMouseEvent(nullptr, event->timestamp, event->local,event->global, event->buttons, event->modifiers);}delete mFrameData.event;mFrameData.event = nullptr;}//TODO: do modifiers get passed correctly here?flushScrollEvent();
}void QWaylandInputDevice::Pointer::flushFrameEvent()
{if (auto *event = mFrameData.event) {if (auto window = event->surface) {window->handleMouse(mParent, *event);} else if (mFrameData.event->type == QWaylandPointerEvent::Type::Release) {// If the window has been destroyed, we still need to report an up event, but it can't// be handled by the destroyed window (obviously), so send the event here instead.QWindowSystemInterface::handleMouseEvent(nullptr, event->timestamp, event->local,event->global, event->buttons, event->modifiers);}delete mFrameData.event;mFrameData.event = nullptr;}//TODO: do modifiers get passed correctly here?flushScrollEvent();
}
window->handleMouse
void QWaylandWindow::handleMouse(QWaylandInputDevice *inputDevice, const QWaylandPointerEvent &e)
{if (e.type == QWaylandPointerEvent::Leave) {if (mWindowDecoration) {if (mMouseEventsInContentArea)QWindowSystemInterface::handleLeaveEvent(window());} else {QWindowSystemInterface::handleLeaveEvent(window());}
#if QT_CONFIG(cursor)restoreMouseCursor(inputDevice);
#endifreturn;}if (mWindowDecoration) {handleMouseEventWithDecoration(inputDevice, e);} else {switch (e.type) {case QWaylandPointerEvent::Enter:QWindowSystemInterface::handleEnterEvent(window(), e.local, e.global);break;case QWaylandPointerEvent::Press:case QWaylandPointerEvent::Release:case QWaylandPointerEvent::Motion:QWindowSystemInterface::handleMouseEvent(window(), e.timestamp, e.local, e.global, e.buttons, e.modifiers);break;case QWaylandPointerEvent::Wheel:QWindowSystemInterface::handleWheelEvent(window(), e.timestamp, e.local, e.global,e.pixelDelta, e.angleDelta, e.modifiers,e.phase, e.source, false);break;}}#if QT_CONFIG(cursor)if (e.type == QWaylandPointerEvent::Enter) {QRect contentGeometry = windowContentGeometry().marginsRemoved(frameMargins());if (contentGeometry.contains(e.local.toPoint()))restoreMouseCursor(inputDevice);}
#endif
}
调用 QWindowSystemInterface::handleMouseEvent(window()。 handleMouseEvent 函数是qtbase 代码中实现的。
QT_DEFINE_QPA_EVENT_HANDLER(void, handleMouseEvent, QWindow *window,const QPointF &local, const QPointF &global, Qt::MouseButtons state,Qt::MouseButton button, QEvent::Type type, Qt::KeyboardModifiers mods,Qt::MouseEventSource source)
{unsigned long time = QWindowSystemInterfacePrivate::eventTime.elapsed();handleMouseEvent<Delivery>(window, time, local, global, state, button, type, mods, source);
}QT_DEFINE_QPA_EVENT_HANDLER(void, handleMouseEvent, QWindow *window, ulong timestamp,const QPointF &local, const QPointF &global, Qt::MouseButtons state,Qt::MouseButton button, QEvent::Type type, Qt::KeyboardModifiers mods,Qt::MouseEventSource source)
{Q_ASSERT_X(type != QEvent::MouseButtonDblClick && type != QEvent::NonClientAreaMouseButtonDblClick,"QWindowSystemInterface::handleMouseEvent","QTBUG-71263: Native double clicks are not implemented.");auto localPos = QHighDpi::fromNativeLocalPosition(local, window);auto globalPos = QHighDpi::fromNativePixels(global, window);QWindowSystemInterfacePrivate::MouseEvent *e =new QWindowSystemInterfacePrivate::MouseEvent(window, timestamp, localPos, globalPos,state, mods, button, type, source);QWindowSystemInterfacePrivate::handleWindowSystemEvent<Delivery>(e);
}
handleMouseEvent 调用handleWindowSystemEvent 函数。
template<>
bool QWindowSystemInterfacePrivate::handleWindowSystemEvent<QWindowSystemInterface::DefaultDelivery>(QWindowSystemInterfacePrivate::WindowSystemEvent *ev)
{if (synchronousWindowSystemEvents)return handleWindowSystemEvent<QWindowSystemInterface::SynchronousDelivery>(ev);elsereturn handleWindowSystemEvent<QWindowSystemInterface::AsynchronousDelivery>(ev);
}
handleWindowSystemEvent 同步处理及异步处理
template<>
bool QWindowSystemInterfacePrivate::handleWindowSystemEvent<QWindowSystemInterface::AsynchronousDelivery>(WindowSystemEvent *ev)
{windowSystemEventQueue.append(ev);if (QAbstractEventDispatcher *dispatcher = QGuiApplicationPrivate::qt_qpa_core_dispatcher())dispatcher->wakeUp();return true;
}
异步处理实现:将事件加入到windowSystemEventQueue 队列处理。
同步处理:
template<>
bool QWindowSystemInterfacePrivate::handleWindowSystemEvent<QWindowSystemInterface::SynchronousDelivery>(WindowSystemEvent *ev)
{bool accepted = true;if (QThread::currentThread() == QGuiApplication::instance()->thread()) {// Process the event immediately on the current thread and return the accepted state.QGuiApplicationPrivate::processWindowSystemEvent(ev);accepted = ev->eventAccepted;delete ev;} else {// Post the event on the Qt main thread queue and flush the queue.// This will wake up the Gui thread which will process the event.// Return the accepted state for the last event on the queue,// which is the event posted by this function.handleWindowSystemEvent<QWindowSystemInterface::AsynchronousDelivery>(ev);accepted = QWindowSystemInterface::flushWindowSystemEvents();}return accepted;
}/*!Make Qt Gui process all events on the event queue immediately. Return theaccepted state for the last event on the queue.
*/
bool QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ProcessEventsFlags flags)
{const int count = QWindowSystemInterfacePrivate::windowSystemEventQueue.count();if (!count)return false;if (!QGuiApplication::instance()) {qWarning().nospace()<< "QWindowSystemInterface::flushWindowSystemEvents() invoked after ""QGuiApplication destruction, discarding " << count << " events.";QWindowSystemInterfacePrivate::windowSystemEventQueue.clear();return false;}if (QThread::currentThread() != QGuiApplication::instance()->thread()) {// Post a FlushEvents event which will trigger a call back to// deferredFlushWindowSystemEvents from the Gui thread.QMutexLocker locker(&QWindowSystemInterfacePrivate::flushEventMutex);QWindowSystemInterfacePrivate::FlushEventsEvent *e = new QWindowSystemInterfacePrivate::FlushEventsEvent(flags);QWindowSystemInterfacePrivate::handleWindowSystemEvent<AsynchronousDelivery>(e);QWindowSystemInterfacePrivate::eventsFlushed.wait(&QWindowSystemInterfacePrivate::flushEventMutex);} else {sendWindowSystemEvents(flags);}return QWindowSystemInterfacePrivate::eventAccepted.loadRelaxed() > 0;
}
ool QWindowSystemInterface::sendWindowSystemEvents(QEventLoop::ProcessEventsFlags flags)
{int nevents = 0;while (QWindowSystemInterfacePrivate::windowSystemEventsQueued()) {QWindowSystemInterfacePrivate::WindowSystemEvent *event = nullptr;if (QWindowSystemInterfacePrivate::platformFiltersEvents) {event = QWindowSystemInterfacePrivate::getWindowSystemEvent();} else {event = flags & QEventLoop::ExcludeUserInputEvents ?QWindowSystemInterfacePrivate::getNonUserInputWindowSystemEvent() :QWindowSystemInterfacePrivate::getWindowSystemEvent();}if (!event)break;if (QWindowSystemInterfacePrivate::eventHandler) {if (QWindowSystemInterfacePrivate::eventHandler->sendEvent(event))nevents++;} else {nevents++;QGuiApplicationPrivate::processWindowSystemEvent(event);}// Record the accepted state for the processed event// (excluding flush events). This state can then be// returned by flushWindowSystemEvents().if (event->type != QWindowSystemInterfacePrivate::FlushEvents)QWindowSystemInterfacePrivate::eventAccepted.storeRelaxed(event->eventAccepted);delete event;}return (nevents > 0);
}void QGuiApplicationPrivate::processWindowSystemEvent(QWindowSystemInterfacePrivate::WindowSystemEvent *e)
{Q_TRACE_SCOPE(QGuiApplicationPrivate_processWindowSystemEvent, e->type);switch(e->type) {case QWindowSystemInterfacePrivate::Mouse:QGuiApplicationPrivate::processMouseEvent(static_cast<QWindowSystemInterfacePrivate::MouseEvent *>(e));break;case QWindowSystemInterfacePrivate::Wheel:QGuiApplicationPrivate::processWheelEvent(static_cast<QWindowSystemInterfacePrivate::WheelEvent *>(e));break;case QWindowSystemInterfacePrivate::Key:QGuiApplicationPrivate::processKeyEvent(static_cast<QWindowSystemInterfacePrivate::KeyEvent *>(e));break;case QWindowSystemInterfacePrivate::Touch:QGuiApplicationPrivate::processTouchEvent(static_cast<QWindowSystemInterfacePrivate::TouchEvent *>(e));break;case QWindowSystemInterfacePrivate::GeometryChange:QGuiApplicationPrivate::processGeometryChangeEvent(static_cast<QWindowSystemInterfacePrivate::GeometryChangeEvent*>(e));break;case QWindowSystemInterfacePrivate::Enter:QGuiApplicationPrivate::processEnterEvent(static_cast<QWindowSystemInterfacePrivate::EnterEvent *>(e));break;case QWindowSystemInterfacePrivate::Leave:QGuiApplicationPrivate::processLeaveEvent(static_cast<QWindowSystemInterfacePrivate::LeaveEvent *>(e));break;case QWindowSystemInterfacePrivate::ActivatedWindow:QGuiApplicationPrivate::processActivatedEvent(static_cast<QWindowSystemInterfacePrivate::ActivatedWindowEvent *>(e));break;case QWindowSystemInterfacePrivate::WindowStateChanged:QGuiApplicationPrivate::processWindowStateChangedEvent(static_cast<QWindowSystemInterfacePrivate::WindowStateChangedEvent *>(e));break;case QWindowSystemInterfacePrivate::WindowScreenChanged:QGuiApplicationPrivate::processWindowScreenChangedEvent(static_cast<QWindowSystemInterfacePrivate::WindowScreenChangedEvent *>(e));break;case QWindowSystemInterfacePrivate::SafeAreaMarginsChanged:QGuiApplicationPrivate::processSafeAreaMarginsChangedEvent(static_cast<QWindowSystemInterfacePrivate::SafeAreaMarginsChangedEvent *>(e));break;case QWindowSystemInterfacePrivate::ApplicationStateChanged: {QWindowSystemInterfacePrivate::ApplicationStateChangedEvent * changeEvent = static_cast<QWindowSystemInterfacePrivate::ApplicationStateChangedEvent *>(e);QGuiApplicationPrivate::setApplicationState(changeEvent->newState, changeEvent->forcePropagate); }break;case QWindowSystemInterfacePrivate::ApplicationTermination:QGuiApplicationPrivate::processApplicationTermination(e);break;case QWindowSystemInterfacePrivate::FlushEvents: {QWindowSystemInterfacePrivate::FlushEventsEvent *flushEventsEvent = static_cast<QWindowSystemInterfacePrivate::FlushEventsEvent *>(e);QWindowSystemInterface::deferredFlushWindowSystemEvents(flushEventsEvent->flags); }break;case QWindowSystemInterfacePrivate::Close:QGuiApplicationPrivate::processCloseEvent(static_cast<QWindowSystemInterfacePrivate::CloseEvent *>(e));break;case QWindowSystemInterfacePrivate::ScreenOrientation:QGuiApplicationPrivate::processScreenOrientationChange(static_cast<QWindowSystemInterfacePrivate::ScreenOrientationEvent *>(e));break;case QWindowSystemInterfacePrivate::ScreenGeometry:QGuiApplicationPrivate::processScreenGeometryChange(static_cast<QWindowSystemInterfacePrivate::ScreenGeometryEvent *>(e));break;case QWindowSystemInterfacePrivate::ScreenLogicalDotsPerInch:QGuiApplicationPrivate::processScreenLogicalDotsPerInchChange(static_cast<QWindowSystemInterfacePrivate::ScreenLogicalDotsPerInchEvent *>(e));break;case QWindowSystemInterfacePrivate::ScreenRefreshRate:QGuiApplicationPrivate::processScreenRefreshRateChange(static_cast<QWindowSystemInterfacePrivate::ScreenRefreshRateEvent *>(e));break;case QWindowSystemInterfacePrivate::ThemeChange:QGuiApplicationPrivate::processThemeChanged(static_cast<QWindowSystemInterfacePrivate::ThemeChangeEvent *>(e));break;case QWindowSystemInterfacePrivate::Expose:QGuiApplicationPrivate::processExposeEvent(static_cast<QWindowSystemInterfacePrivate::ExposeEvent *>(e));break;case QWindowSystemInterfacePrivate::Tablet:QGuiApplicationPrivate::processTabletEvent(static_cast<QWindowSystemInterfacePrivate::TabletEvent *>(e));break;case QWindowSystemInterfacePrivate::TabletEnterProximity:QGuiApplicationPrivate::processTabletEnterProximityEvent(static_cast<QWindowSystemInterfacePrivate::TabletEnterProximityEvent *>(e));break;case QWindowSystemInterfacePrivate::TabletLeaveProximity:QGuiApplicationPrivate::processTabletLeaveProximityEvent(static_cast<QWindowSystemInterfacePrivate::TabletLeaveProximityEvent *>(e));break;
#ifndef QT_NO_GESTUREScase QWindowSystemInterfacePrivate::Gesture:QGuiApplicationPrivate::processGestureEvent(static_cast<QWindowSystemInterfacePrivate::GestureEvent *>(e));break;
#endifcase QWindowSystemInterfacePrivate::PlatformPanel:QGuiApplicationPrivate::processPlatformPanelEvent(static_cast<QWindowSystemInterfacePrivate::PlatformPanelEvent *>(e));break;case QWindowSystemInterfacePrivate::FileOpen:QGuiApplicationPrivate::processFileOpenEvent(static_cast<QWindowSystemInterfacePrivate::FileOpenEvent *>(e));break;
#ifndef QT_NO_CONTEXTMENUcase QWindowSystemInterfacePrivate::ContextMenu:QGuiApplicationPrivate::processContextMenuEvent(static_cast<QWindowSystemInterfacePrivate::ContextMenuEvent *>(e));break;
#endifcase QWindowSystemInterfacePrivate::EnterWhatsThisMode:QGuiApplication::postEvent(QGuiApplication::instance(), new QEvent(QEvent::EnterWhatsThisMode));break;default:qWarning() << "Unknown user input event type:" << e->type;break;}
}void QGuiApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::MouseEvent *e)
{QEvent::Type type = QEvent::None;Qt::MouseButton button = Qt::NoButton;QWindow *window = e->window.data();bool positionChanged = QGuiApplicationPrivate::lastCursorPosition != e->globalPos;bool mouseMove = false;bool mousePress = false;if (e->enhancedMouseEvent()) {type = e->buttonType;button = e->button;if (type == QEvent::NonClientAreaMouseMove || type == QEvent::MouseMove)mouseMove = true;else if (type == QEvent::NonClientAreaMouseButtonPress || type == QEvent::MouseButtonPress)mousePress = true;if (!mouseMove && positionChanged) {QWindowSystemInterfacePrivate::MouseEvent moveEvent(window, e->timestamp,e->localPos, e->globalPos, e->buttons ^ button, e->modifiers, Qt::NoButton,e->nonClientArea ? QEvent::NonClientAreaMouseMove : QEvent::MouseMove,e->source, e->nonClientArea);if (e->synthetic())moveEvent.flags |= QWindowSystemInterfacePrivate::WindowSystemEvent::Synthetic;processMouseEvent(&moveEvent); // mouse move excluding state changeprocessMouseEvent(e); // the original mouse eventreturn;}} else {Qt::MouseButtons stateChange = e->buttons ^ mouse_buttons;if (positionChanged && (stateChange != Qt::NoButton)) {QWindowSystemInterfacePrivate::MouseEvent moveEvent(window, e->timestamp, e->localPos,e->globalPos, mouse_buttons, e->modifiers, Qt::NoButton, QEvent::None, e->source,e->nonClientArea);if (e->synthetic())moveEvent.flags |= QWindowSystemInterfacePrivate::WindowSystemEvent::Synthetic;processMouseEvent(&moveEvent); // mouse move excluding state changeprocessMouseEvent(e); // the original mouse eventreturn;}// In the compatibility path we deduce event type and button that caused the eventif (positionChanged) {mouseMove = true;type = e->nonClientArea ? QEvent::NonClientAreaMouseMove : QEvent::MouseMove;} else {// Check to see if a new button has been pressed/released.for (uint mask = Qt::LeftButton; mask <= Qt::MaxMouseButton; mask <<= 1) {if (stateChange & mask) {button = Qt::MouseButton(mask);break;}}if (button == Qt::NoButton) {// Ignore mouse events that don't change the current state. This shouldn't// really happen, getting here can only mean that the stored button state// is out of sync with the actual physical button state.return;}if (button & e->buttons) {mousePress = true;type = e->nonClientArea ? QEvent::NonClientAreaMouseButtonPress: QEvent::MouseButtonPress;} else {type = e->nonClientArea ? QEvent::NonClientAreaMouseButtonRelease: QEvent::MouseButtonRelease;}}}modifier_buttons = e->modifiers;QPointF localPoint = e->localPos;QPointF globalPoint = e->globalPos;bool doubleClick = false;if (mouseMove) {QGuiApplicationPrivate::lastCursorPosition = globalPoint;const auto doubleClickDistance = e->source == Qt::MouseEventNotSynthesized ?mouseDoubleClickDistance : touchDoubleTapDistance;if (qAbs(globalPoint.x() - mousePressX) > doubleClickDistance ||qAbs(globalPoint.y() - mousePressY) > doubleClickDistance)mousePressButton = Qt::NoButton;} else {mouse_buttons = e->buttons;if (mousePress) {ulong doubleClickInterval = static_cast<ulong>(QGuiApplication::styleHints()->mouseDoubleClickInterval());doubleClick = e->timestamp - mousePressTime < doubleClickInterval && button == mousePressButton;mousePressTime = e->timestamp;mousePressButton = button;const QPoint point = QGuiApplicationPrivate::lastCursorPosition.toPoint();mousePressX = point.x();mousePressY = point.y();}}if (e->nullWindow()) {window = QGuiApplication::topLevelAt(globalPoint.toPoint());if (window) {// Moves and the release following a press must go to the same// window, even if the cursor has moved on over another window.if (e->buttons != Qt::NoButton) {if (!currentMousePressWindow)currentMousePressWindow = window;elsewindow = currentMousePressWindow;} else if (currentMousePressWindow) {window = currentMousePressWindow;currentMousePressWindow = 0;}QPointF delta = globalPoint - globalPoint.toPoint();localPoint = window->mapFromGlobal(globalPoint.toPoint()) + delta;}}if (!window)return;#ifndef QT_NO_CURSORif (!e->synthetic()) {if (const QScreen *screen = window->screen())if (QPlatformCursor *cursor = screen->handle()->cursor()) {const QPointF nativeLocalPoint = QHighDpi::toNativePixels(localPoint, screen);const QPointF nativeGlobalPoint = QHighDpi::toNativePixels(globalPoint, screen);QMouseEvent ev(type, nativeLocalPoint, nativeLocalPoint, nativeGlobalPoint,button, e->buttons, e->modifiers, e->source);ev.setTimestamp(e->timestamp);cursor->pointerEvent(ev);}}
#endifQMouseEvent ev(type, localPoint, localPoint, globalPoint, button, e->buttons, e->modifiers, e->source);ev.setTimestamp(e->timestamp);if (window->d_func()->blockedByModalWindow && !qApp->d_func()->popupActive()) {// a modal window is blocking this window, don't allow mouse events throughreturn;}if (doubleClick && (ev.type() == QEvent::MouseButtonPress)) {// QtBUG-25831, used to suppress delivery in qwidgetwindow.cppsetMouseEventFlags(&ev, ev.flags() | Qt::MouseEventCreatedDoubleClick);}QGuiApplication::sendSpontaneousEvent(window, &ev);e->eventAccepted = ev.isAccepted();if (!e->synthetic() && !ev.isAccepted()&& !e->nonClientArea&& qApp->testAttribute(Qt::AA_SynthesizeTouchForUnhandledMouseEvents)) {if (!m_fakeTouchDevice) {m_fakeTouchDevice = new QTouchDevice;QWindowSystemInterface::registerTouchDevice(m_fakeTouchDevice);}QList<QWindowSystemInterface::TouchPoint> points;QWindowSystemInterface::TouchPoint point;point.id = 1;point.area = QRectF(globalPoint.x() - 2, globalPoint.y() - 2, 4, 4);// only translate left button related events to// avoid strange touch event sequences when several// buttons are pressedif (type == QEvent::MouseButtonPress && button == Qt::LeftButton) {point.state = Qt::TouchPointPressed;} else if (type == QEvent::MouseButtonRelease && button == Qt::LeftButton) {point.state = Qt::TouchPointReleased;} else if (type == QEvent::MouseMove && (e->buttons & Qt::LeftButton)) {point.state = Qt::TouchPointMoved;} else {return;}points << point;QEvent::Type type;QList<QTouchEvent::TouchPoint> touchPoints =QWindowSystemInterfacePrivate::fromNativeTouchPoints(points, window, QTouchDevicePrivate::get(m_fakeTouchDevice)->id, &type);QWindowSystemInterfacePrivate::TouchEvent fake(window, e->timestamp, type, m_fakeTouchDevice, touchPoints, e->modifiers);fake.flags |= QWindowSystemInterfacePrivate::WindowSystemEvent::Synthetic;processTouchEvent(&fake);}if (doubleClick) {mousePressButton = Qt::NoButton;if (!e->window.isNull() || e->nullWindow()) { // QTBUG-36364, check if window closed in response to pressconst QEvent::Type doubleClickType = e->nonClientArea ? QEvent::NonClientAreaMouseButtonDblClick : QEvent::MouseButtonDblClick;QMouseEvent dblClickEvent(doubleClickType, localPoint, localPoint, globalPoint,button, e->buttons, e->modifiers, e->source);dblClickEvent.setTimestamp(e->timestamp);QGuiApplication::sendSpontaneousEvent(window, &dblClickEvent);}}
}/*!\internal
*/
bool QCoreApplication::sendSpontaneousEvent(QObject *receiver, QEvent *event)
{Q_TRACE(QCoreApplication_sendSpontaneousEvent, receiver, event, event->type());if (event)event->spont = true;return notifyInternal2(receiver, event);
}
/*!\internal
*/
bool QCoreApplication::sendSpontaneousEvent(QObject *receiver, QEvent *event)
{Q_TRACE(QCoreApplication_sendSpontaneousEvent, receiver, event, event->type());if (event)event->spont = true;return notifyInternal2(receiver, event);
}/*!\internal\since 5.6This function is here to make it possible for Qt extensions tohook into event notification without subclassing QApplication.
*/
bool QCoreApplication::notifyInternal2(QObject *receiver, QEvent *event)
{bool selfRequired = QCoreApplicationPrivate::threadRequiresCoreApplication();if (!self && selfRequired)return false;// Make it possible for Qt Script to hook into events even// though QApplication is subclassed...bool result = false;void *cbdata[] = { receiver, event, &result };if (QInternal::activateCallbacks(QInternal::EventNotifyCallback, cbdata)) {return result;}// Qt enforces the rule that events can only be sent to objects in// the current thread, so receiver->d_func()->threadData is// equivalent to QThreadData::current(), just without the function// call overhead.QObjectPrivate *d = receiver->d_func();QThreadData *threadData = d->threadData;QScopedScopeLevelCounter scopeLevelCounter(threadData);if (!selfRequired)return doNotify(receiver, event);return self->notify(receiver, event);
}bool QCoreApplication::notify(QObject *receiver, QEvent *event)
{// no events are delivered after ~QCoreApplication() has startedif (QCoreApplicationPrivate::is_app_closing)return true;return doNotify(receiver, event);
}static bool doNotify(QObject *receiver, QEvent *event)
{if (receiver == 0) { // serious errorqWarning("QCoreApplication::notify: Unexpected null receiver");return true;}#ifndef QT_NO_DEBUGQCoreApplicationPrivate::checkReceiverThread(receiver);
#endifreturn receiver->isWidgetType() ? false : QCoreApplicationPrivate::notify_helper(receiver, event);
}Helper function called by QCoreApplicationPrivate::notify() and qapplication.cpp*/
bool QCoreApplicationPrivate::notify_helper(QObject *receiver, QEvent * event)
{// Note: when adjusting the tracepoints in here// consider adjusting QApplicationPrivate::notify_helper too.Q_TRACE(QCoreApplication_notify_entry, receiver, event, event->type());bool consumed = false;bool filtered = false;Q_TRACE_EXIT(QCoreApplication_notify_exit, consumed, filtered);// send to all application event filters (only does anything in the main thread)if (QCoreApplication::self&& receiver->d_func()->threadData->thread.loadAcquire() == mainThread()&& QCoreApplication::self->d_func()->sendThroughApplicationEventFilters(receiver, event)) {filtered = true;return filtered;}// send to all receiver event filtersif (sendThroughObjectEventFilters(receiver, event)) {filtered = true;return filtered;}// deliver the eventconsumed = receiver->event(event);return consumed;
}receiver->event 指向QObject ;
可以看到,流程中涉及两个事件过滤器的调用:sendThroughApplicationEventFilters和sendThroughObjectEventFilters,事件过滤器调用完后,才是调用接收者的event函数。
QApplication的事件过滤器
上一小节提到的sendThroughApplicationEventFilters是处理app的事件过滤器。代码里会调用给app安装的所有事件过滤器(从代码中的注释看到,app的事件过滤器只能在主线程中被调用),我们给app安装的事件过滤器就是在这个阶段被执行的。
bool QCoreApplicationPrivate::sendThroughApplicationEventFilters(QObject *receiver, QEvent *event)
{// We can't access the application event filters outside of the main thread (race conditions)Q_ASSERT(receiver->d_func()->threadData->thread.loadAcquire() == mainThread());if (extraData) {// application event filters are only called for objects in the GUI threadfor (int i = 0; i < extraData->eventFilters.size(); ++i) {QObject *obj = extraData->eventFilters.at(i);...if (obj->eventFilter(receiver, event))return true;}}return false;
}
QObject的事件过滤器
sendThroughObjectEventFilters是处理对象的事件过滤器。里面会调用给接收者安装的全部事件过滤器,我们通过installEventFilter给某个对象安装的事件过滤器就是在这个阶段执行的。
bool QCoreApplicationPrivate::sendThroughObjectEventFilters(QObject *receiver, QEvent *event)
{...for (int i = 0; i < receiver->d_func()->extraData->eventFilters.size(); ++i) {QObject *obj = receiver->d_func()->extraData->eventFilters.at(i);...if (obj->eventFilter(receiver, event))return true;}}return false;
}
对象的event方法
从QCoreApplicationPrivate::notify_helper代码可以看到,处理完事件接收者的事件过滤器后,就会调用接收者的event方法来处理事件。
每一个从QObject继承出来的子类都有event方法,都可以处理自己的事件。
我们简单看下QWidget的event方法:
bool QWidget::event(QEvent *event)
{...switch (event->type()) {...case QEvent::MouseMove:mouseMoveEvent((QMouseEvent*)event);break;case QEvent::MouseButtonPress:mousePressEvent((QMouseEvent*)event);break;case QEvent::MouseButtonRelease:mouseReleaseEvent((QMouseEvent*)event);break;case QEvent::MouseButtonDblClick:mouseDoubleClickEvent((QMouseEvent*)event);break;...
}到这里
是不是很熟悉,各个具体的事件处理函数就是在这个阶段被调用的。