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 surfaces
if (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 updated
updateCursor();
#endif
printf("%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);
#endif
return;
}
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);
else
return 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 the
accepted 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_GESTURES
case QWindowSystemInterfacePrivate::Gesture:
QGuiApplicationPrivate::processGestureEvent(
static_cast<QWindowSystemInterfacePrivate::GestureEvent *>(e));
break;
#endif
case QWindowSystemInterfacePrivate::PlatformPanel:
QGuiApplicationPrivate::processPlatformPanelEvent(
static_cast<QWindowSystemInterfacePrivate::PlatformPanelEvent *>(e));
break;
case QWindowSystemInterfacePrivate::FileOpen:
QGuiApplicationPrivate::processFileOpenEvent(
static_cast<QWindowSystemInterfacePrivate::FileOpenEvent *>(e));
break;
#ifndef QT_NO_CONTEXTMENU
case QWindowSystemInterfacePrivate::ContextMenu:
QGuiApplicationPrivate::processContextMenuEvent(
static_cast<QWindowSystemInterfacePrivate::ContextMenuEvent *>(e));
break;
#endif
case 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 change
processMouseEvent(e); // the original mouse event
return;
}
} 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 change
processMouseEvent(e); // the original mouse event
return;
}
// In the compatibility path we deduce event type and button that caused the event
if (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;
else
window = 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_CURSOR
if (!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);
}
}
#endif
QMouseEvent 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 through
return;
}
if (doubleClick && (ev.type() == QEvent::MouseButtonPress)) {
// QtBUG-25831, used to suppress delivery in qwidgetwindow.cpp
setMouseEventFlags(&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 pressed
if (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 press
const 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.6
This function is here to make it possible for Qt extensions to
hook 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 started
if (QCoreApplicationPrivate::is_app_closing)
return true;
return doNotify(receiver, event);
}
static bool doNotify(QObject *receiver, QEvent *event)
{
if (receiver == 0) { // serious error
qWarning("QCoreApplication::notify: Unexpected null receiver");
return true;
}
#ifndef QT_NO_DEBUG
QCoreApplicationPrivate::checkReceiverThread(receiver);
#endif
return 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 filters
if (sendThroughObjectEventFilters(receiver, event)) {
filtered = true;
return filtered;
}
// deliver the event
consumed = 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 thread
for (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;
...
}
到这里
是不是很熟悉,各个具体的事件处理函数就是在这个阶段被调用的。