qpa: enhance mouse event with type and button data

... and deprecate QWSI APIs that accepts mouse event without mouse button/
type data.

In the early days of Qt5 it was decided to centralize mouse button/type
handling in QGuiApplication (because of limitation of some now unknown
platform). This has proven to be problematic as mouse handling details
differ across platforms (e.g on X11 we do not receive mouse release event
when closing popup windows or ordinary windows that are closed from the
mouse press event). Instead of hacking around platform specific behaviors
in Qt Gui, we should move this task back to platform plugins (similar to
how this was done in Qt4 with native APIs sending mouse details directly
to QApplication). There are even cases where it simply is not possible
to deduce (from QGuiApplication) which button caused the event (e.g. when
more than one button is involved and some event goes missing). Besisdes,
throwing away information which is already available at QPA level (for free)
and trying to deduce it again at Qt Gui level seems impractical, fagile
(as probably noticed by people fixing all the unexpected issues) and adds
unnecessary complexity.

Note:

Removing the deprecated QWSI APIs from offscreen plugin depends on fixing
autotests that rely on QOffscreenCursor::setPos() logic.

For the convenience of testing use QT_QPA_DISABLE_ENHANCED_MOUSE to restore
to the old code path where QGuiApplication does the mouse state deducing.

Other platforms have similar issues. I do not have all supported platform
available on my desk, so other platform maintainers will need to take care
of porting those platforms to the new APIs. And mainly, I don't want to deal
with all the hacks that other platforms have added to workaround this broken
mouse logic.

In Qt6 we need to remove deprecated code path from QGuiApplication.

This patch:

- Extends QWindowSystemInterfacePrivate::MouseEvent ctor with QEvent::Type
and Qt::MouseButton. We use this extra data when processing mouse events in
QGuiApplication. This actually is similar to KeyEvent, where we do pass the
type (press or release) to QtGui.

- Refactors QGuiApplicationPrivate::processMouseEvent and qtestlib to use
the new APIs.

Task-number: QTBUG-59277
Task-number: QTBUG-62329
Task-number: QTBUG-63467
Change-Id: If94fd46a7cccfea8264dcb1368804c73334558b8
Reviewed-by: Paul Olav Tvete <paul.tvete@qt.io>
This commit is contained in:
Gatis Paeglis 2017-08-30 11:03:39 +02:00
parent 0900cf3581
commit a37785ec76
15 changed files with 297 additions and 107 deletions

View File

@ -1842,29 +1842,111 @@ void QGuiApplicationPrivate::processWindowSystemEvent(QWindowSystemInterfacePriv
}
}
/*! \internal
History is silent on why Qt splits mouse events that change position and
button state at the same time. We believe that this was done to emulate mouse
behavior on touch screens. If mouse tracking is enabled, we will get move
events before the button is pressed. A touch panel does not generally give
move events when not pressed, so without event splitting code path we would
only see a press in a new location without any intervening moves. This could
confuse code that is written for a real mouse. The same is true for mouse
release events that change position, see tst_QWidget::touchEventSynthesizedMouseEvent()
auto test.
*/
void QGuiApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::MouseEvent *e)
{
QEvent::Type type;
Qt::MouseButtons stateChange = e->buttons ^ mouse_buttons;
if (e->globalPos != QGuiApplicationPrivate::lastCursorPosition && (stateChange != Qt::NoButton)) {
// A mouse event should not change both position and buttons at the same time. Instead we
// should first send a move event followed by a button changed event. Since this is not the case
// with the current event, we split it in two.
QWindowSystemInterfacePrivate::MouseEvent mouseButtonEvent(e->window.data(), e->timestamp,
e->localPos, e->globalPos, e->buttons, e->modifiers, e->source, e->nonClientArea);
if (e->flags & QWindowSystemInterfacePrivate::WindowSystemEvent::Synthetic)
mouseButtonEvent.flags |= QWindowSystemInterfacePrivate::WindowSystemEvent::Synthetic;
e->buttons = mouse_buttons;
processMouseEvent(e);
processMouseEvent(&mouseButtonEvent);
return;
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;
}
}
}
QWindow *window = e->window.data();
modifier_buttons = e->modifiers;
QPointF localPoint = e->localPos;
QPointF globalPoint = e->globalPos;
bool doubleClick = false;
if (mouseMove) {
QGuiApplicationPrivate::lastCursorPosition = globalPoint;
if (qAbs(globalPoint.x() - mousePressX) > mouse_double_click_distance||
qAbs(globalPoint.y() - mousePressY) > mouse_double_click_distance)
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());
@ -1885,43 +1967,6 @@ void QGuiApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::Mo
}
}
Qt::MouseButton button = Qt::NoButton;
bool doubleClick = false;
if (QGuiApplicationPrivate::lastCursorPosition != globalPoint) {
type = e->nonClientArea ? QEvent::NonClientAreaMouseMove : QEvent::MouseMove;
QGuiApplicationPrivate::lastCursorPosition = globalPoint;
if (qAbs(globalPoint.x() - mousePressX) > mouse_double_click_distance||
qAbs(globalPoint.y() - mousePressY) > mouse_double_click_distance)
mousePressButton = Qt::NoButton;
} else { // Check to see if a new button has been pressed/released.
for (int check = Qt::LeftButton;
check <= int(Qt::MaxMouseButton);
check = check << 1) {
if (check & stateChange) {
button = Qt::MouseButton(check);
break;
}
}
if (button == Qt::NoButton) {
// Ignore mouse events that don't change the current state.
return;
}
mouse_buttons = e->buttons;
if (button & e->buttons) {
ulong doubleClickInterval = static_cast<ulong>(QGuiApplication::styleHints()->mouseDoubleClickInterval());
doubleClick = e->timestamp - mousePressTime < doubleClickInterval && button == mousePressButton;
type = e->nonClientArea ? QEvent::NonClientAreaMouseButtonPress : QEvent::MouseButtonPress;
mousePressTime = e->timestamp;
mousePressButton = button;
const QPoint point = QGuiApplicationPrivate::lastCursorPosition.toPoint();
mousePressX = point.x();
mousePressY = point.y();
} else {
type = e->nonClientArea ? QEvent::NonClientAreaMouseButtonRelease : QEvent::MouseButtonRelease;
}
}
if (!window)
return;

View File

@ -128,7 +128,7 @@ void QPlatformCursor::setPos(const QPoint &pos)
qWarning("This plugin does not support QCursor::setPos()"
"; emulating movement within the application.");
}
QWindowSystemInterface::handleMouseEvent(0, pos, pos, Qt::NoButton);
QWindowSystemInterface::handleMouseEvent(0, pos, pos, Qt::NoButton, Qt::NoButton, QEvent::MouseMove);
}
// End of display and pointer event handling code

View File

@ -341,36 +341,79 @@ void QWindowSystemInterface::handleCloseEvent(QWindow *window, bool *accepted)
\a w == 0 means that the event is in global coords only, \a local will be ignored in this case
*/
#if QT_DEPRECATED_SINCE(5, 11)
QT_DEFINE_QPA_EVENT_HANDLER(void, handleMouseEvent, QWindow *window, const QPointF &local, const QPointF &global, Qt::MouseButtons b,
Qt::KeyboardModifiers mods, Qt::MouseEventSource source)
{
unsigned long time = QWindowSystemInterfacePrivate::eventTime.elapsed();
handleMouseEvent<Delivery>(window, time, local, global, b, mods, source);
handleMouseEvent<Delivery>(window, local, global, b, Qt::NoButton, QEvent::None, mods, source);
}
QT_DEFINE_QPA_EVENT_HANDLER(void, handleMouseEvent, QWindow *window, ulong timestamp, const QPointF &local, const QPointF &global, Qt::MouseButtons b,
Qt::KeyboardModifiers mods, Qt::MouseEventSource source)
{
QWindowSystemInterfacePrivate::MouseEvent * e =
new QWindowSystemInterfacePrivate::MouseEvent(window, timestamp, QHighDpi::fromNativeLocalPosition(local, window), QHighDpi::fromNativePixels(global, window), b, mods, source);
QWindowSystemInterfacePrivate::handleWindowSystemEvent<Delivery>(e);
handleMouseEvent<Delivery>(window, timestamp, local, global, b, Qt::NoButton, QEvent::None, mods, source);
}
void QWindowSystemInterface::handleFrameStrutMouseEvent(QWindow *window, const QPointF &local, const QPointF &global, Qt::MouseButtons b,
Qt::KeyboardModifiers mods, Qt::MouseEventSource source)
{
const unsigned long time = QWindowSystemInterfacePrivate::eventTime.elapsed();
handleFrameStrutMouseEvent(window, time, local, global, b, mods, source);
handleFrameStrutMouseEvent(window, local, global, b, Qt::NoButton, QEvent::None, mods, source);
}
void QWindowSystemInterface::handleFrameStrutMouseEvent(QWindow *window, ulong timestamp, const QPointF &local, const QPointF &global, Qt::MouseButtons b,
Qt::KeyboardModifiers mods, Qt::MouseEventSource source)
{
QWindowSystemInterfacePrivate::MouseEvent * e =
new QWindowSystemInterfacePrivate::MouseEvent(window, timestamp,
QHighDpi::fromNativeLocalPosition(local, window),
QHighDpi::fromNativePixels(global, window),
b, mods, source, true);
handleFrameStrutMouseEvent(window, timestamp, local, global, b, Qt::NoButton, QEvent::None, mods, source);
}
#endif // QT_DEPRECATED_SINCE(5, 11)
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)
{
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);
}
void QWindowSystemInterface::handleFrameStrutMouseEvent(QWindow *window,
const QPointF &local, const QPointF &global,
Qt::MouseButtons state,
Qt::MouseButton button, QEvent::Type type,
Qt::KeyboardModifiers mods,
Qt::MouseEventSource source)
{
const unsigned long time = QWindowSystemInterfacePrivate::eventTime.elapsed();
handleFrameStrutMouseEvent(window, time, local, global, state, button, type, mods, source);
}
void QWindowSystemInterface::handleFrameStrutMouseEvent(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)
{
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, true);
QWindowSystemInterfacePrivate::handleWindowSystemEvent(e);
}
@ -1008,11 +1051,26 @@ bool QWindowSystemInterface::nonUserInputEventsQueued()
// The following functions are used by testlib, and need to be synchronous to avoid
// race conditions with plugins delivering native events from secondary threads.
// FIXME: It seems unnecessary to export these wrapper functions, when qtestlib could access
// QWindowSystemInterface directly (by adding dependency to gui-private), see QTBUG-63146.
Q_GUI_EXPORT void qt_handleMouseEvent(QWindow *window, const QPointF &local, const QPointF &global, Qt::MouseButtons b, Qt::KeyboardModifiers mods, int timestamp)
Q_GUI_EXPORT void qt_handleMouseEvent(QWindow *window, const QPointF &local, const QPointF &global,
Qt::MouseButtons state, Qt::MouseButton button,
QEvent::Type type, Qt::KeyboardModifiers mods, int timestamp)
{
const qreal factor = QHighDpiScaling::factor(window);
QWindowSystemInterface::handleMouseEvent<QWindowSystemInterface::SynchronousDelivery>(window, timestamp, local * factor, global * factor, b, mods);
QWindowSystemInterface::handleMouseEvent<QWindowSystemInterface::SynchronousDelivery>(window,
timestamp, local * factor, global * factor, state, button, type, mods);
}
// Wrapper for compatibility with Qt < 5.11
// ### Qt6: Remove
Q_GUI_EXPORT void qt_handleMouseEvent(QWindow *window, const QPointF &local, const QPointF &global,
Qt::MouseButtons b, Qt::KeyboardModifiers mods, int timestamp)
{
const qreal factor = QHighDpiScaling::factor(window);
QWindowSystemInterface::handleMouseEvent<QWindowSystemInterface::SynchronousDelivery>(window,
timestamp, local * factor, global * factor, b, Qt::NoButton, QEvent::None, mods);
}
// Wrapper for compatibility with Qt < 5.6

View File

@ -76,20 +76,47 @@ public:
struct AsynchronousDelivery {};
struct DefaultDelivery {};
#if QT_DEPRECATED_SINCE(5, 11)
template<typename Delivery = QWindowSystemInterface::DefaultDelivery>
static void handleMouseEvent(QWindow *window, const QPointF &local, const QPointF &global, Qt::MouseButtons b,
QT_DEPRECATED static void handleMouseEvent(QWindow *window, const QPointF &local, const QPointF &global, Qt::MouseButtons b,
Qt::KeyboardModifiers mods = Qt::NoModifier,
Qt::MouseEventSource source = Qt::MouseEventNotSynthesized);
template<typename Delivery = QWindowSystemInterface::DefaultDelivery>
static void handleMouseEvent(QWindow *window, ulong timestamp, const QPointF &local, const QPointF &global, Qt::MouseButtons b,
QT_DEPRECATED static void handleMouseEvent(QWindow *window, ulong timestamp, const QPointF &local, const QPointF &global, Qt::MouseButtons b,
Qt::KeyboardModifiers mods = Qt::NoModifier,
Qt::MouseEventSource source = Qt::MouseEventNotSynthesized);
static void handleFrameStrutMouseEvent(QWindow *window, const QPointF &local, const QPointF &global, Qt::MouseButtons b,
QT_DEPRECATED static void handleFrameStrutMouseEvent(QWindow *window, const QPointF &local, const QPointF &global, Qt::MouseButtons b,
Qt::KeyboardModifiers mods = Qt::NoModifier,
Qt::MouseEventSource source = Qt::MouseEventNotSynthesized);
static void handleFrameStrutMouseEvent(QWindow *window, ulong timestamp, const QPointF &local, const QPointF &global, Qt::MouseButtons b,
QT_DEPRECATED static void handleFrameStrutMouseEvent(QWindow *window, ulong timestamp, const QPointF &local, const QPointF &global, Qt::MouseButtons b,
Qt::KeyboardModifiers mods = Qt::NoModifier,
Qt::MouseEventSource source = Qt::MouseEventNotSynthesized);
#endif
template<typename Delivery = QWindowSystemInterface::DefaultDelivery>
static void handleMouseEvent(QWindow *window, const QPointF &local, const QPointF &global,
Qt::MouseButtons state, Qt::MouseButton button, QEvent::Type type,
Qt::KeyboardModifiers mods = Qt::NoModifier,
Qt::MouseEventSource source = Qt::MouseEventNotSynthesized);
template<typename Delivery = QWindowSystemInterface::DefaultDelivery>
static 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::NoModifier,
Qt::MouseEventSource source = Qt::MouseEventNotSynthesized);
static void handleFrameStrutMouseEvent(QWindow *window, const QPointF &local,
const QPointF &global, Qt::MouseButtons state,
Qt::MouseButton button, QEvent::Type type,
Qt::KeyboardModifiers mods = Qt::NoModifier,
Qt::MouseEventSource source =
Qt::MouseEventNotSynthesized);
static void handleFrameStrutMouseEvent(QWindow *window, ulong timestamp, const QPointF &local,
const QPointF &global, Qt::MouseButtons state,
Qt::MouseButton button, QEvent::Type type,
Qt::KeyboardModifiers mods = Qt::NoModifier,
Qt::MouseEventSource source =
Qt::MouseEventNotSynthesized);
static bool handleShortcutEvent(QWindow *window, ulong timestamp, int k, Qt::KeyboardModifiers mods, quint32 nativeScanCode,
quint32 nativeVirtualKey, quint32 nativeModifiers, const QString & text = QString(), bool autorep = false, ushort count = 1);

View File

@ -225,16 +225,34 @@ public:
class MouseEvent : public InputEvent {
public:
// TODO - remove this ctor when all usages of it in QGuiApplication are cleaned out
MouseEvent(QWindow * w, ulong time, const QPointF &local, const QPointF &global,
Qt::MouseButtons b, Qt::KeyboardModifiers mods,
Qt::MouseEventSource src = Qt::MouseEventNotSynthesized, bool frame = false)
: InputEvent(w, time, Mouse, mods), localPos(local), globalPos(global), buttons(b),
source(src), nonClientArea(frame) { }
source(src), nonClientArea(frame), button(Qt::NoButton), buttonType(QEvent::None) { }
MouseEvent(QWindow *w, ulong time, const QPointF &local, const QPointF &global,
Qt::MouseButtons state, Qt::KeyboardModifiers mods,
Qt::MouseButton b, QEvent::Type type,
Qt::MouseEventSource src = Qt::MouseEventNotSynthesized, bool frame = false)
: InputEvent(w, time, Mouse, mods), localPos(local), globalPos(global), buttons(state),
source(src), nonClientArea(frame), button(b), buttonType(type) { }
// ### In Qt6 this method can be removed as there won't be need for compatibility code path
bool enhancedMouseEvent() const
{
static const bool disableEnhanced = qEnvironmentVariableIsSet("QT_QPA_DISABLE_ENHANCED_MOUSE");
return !disableEnhanced && buttonType != QEvent::None;
}
QPointF localPos;
QPointF globalPos;
Qt::MouseButtons buttons;
Qt::MouseEventSource source;
bool nonClientArea;
Qt::MouseButton button;
QEvent::Type buttonType;
};
class WheelEvent : public InputEvent {

View File

@ -184,7 +184,7 @@ void QEvdevMouseHandler::sendMouseEvent()
m_prevInvalid = false;
}
emit handleMouseEvent(x, y, m_abs, m_buttons);
emit handleMouseEvent(x, y, m_abs, m_buttons, m_button, m_eventType);
m_prevx = m_x;
m_prevy = m_y;
@ -286,6 +286,8 @@ void QEvdevMouseHandler::readMouseData()
case 0x11f: button = Qt::ExtraButton13; break;
}
m_buttons.setFlag(button, data->value);
m_button = button;
m_eventType = data->value == 0 ? QEvent::MouseButtonRelease : QEvent::MouseButtonPress;
btnChanged = true;
} else if (data->type == EV_SYN && data->code == SYN_REPORT) {
if (btnChanged) {
@ -293,6 +295,7 @@ void QEvdevMouseHandler::readMouseData()
sendMouseEvent();
pendingMouseEvent = false;
} else if (posChanged) {
m_eventType = QEvent::MouseMove;
posChanged = false;
if (m_compression) {
pendingMouseEvent = true;

View File

@ -54,6 +54,7 @@
#include <QObject>
#include <QString>
#include <QPoint>
#include <QEvent>
QT_BEGIN_NAMESPACE
@ -69,7 +70,8 @@ public:
void readMouseData();
signals:
void handleMouseEvent(int x, int y, bool abs, Qt::MouseButtons buttons);
void handleMouseEvent(int x, int y, bool abs, Qt::MouseButtons buttons,
Qt::MouseButton button, QEvent::Type type);
void handleWheelEvent(QPoint delta);
private:
@ -86,6 +88,8 @@ private:
bool m_abs;
bool m_compression;
Qt::MouseButtons m_buttons;
Qt::MouseButton m_button;
QEvent::Type m_eventType;
int m_jitterLimitSquared;
bool m_prevInvalid;
int m_hardwareWidth;

View File

@ -131,7 +131,8 @@ void QEvdevMouseManager::clampPosition()
m_y = g.bottom() - m_yoffset;
}
void QEvdevMouseManager::handleMouseEvent(int x, int y, bool abs, Qt::MouseButtons buttons)
void QEvdevMouseManager::handleMouseEvent(int x, int y, bool abs, Qt::MouseButtons buttons,
Qt::MouseButton button, QEvent::Type type)
{
// update current absolute coordinates
if (!abs) {
@ -147,7 +148,8 @@ void QEvdevMouseManager::handleMouseEvent(int x, int y, bool abs, Qt::MouseButto
QPoint pos(m_x + m_xoffset, m_y + m_yoffset);
// Cannot track the keyboard modifiers ourselves here. Instead, report the
// modifiers from the last key event that has been seen by QGuiApplication.
QWindowSystemInterface::handleMouseEvent(0, pos, pos, buttons, QGuiApplication::keyboardModifiers());
Qt::KeyboardModifiers mods = QGuiApplication::keyboardModifiers();
QWindowSystemInterface::handleMouseEvent(0, pos, pos, buttons, button, type, mods);
}
void QEvdevMouseManager::handleWheelEvent(QPoint delta)

View File

@ -68,7 +68,8 @@ public:
QEvdevMouseManager(const QString &key, const QString &specification, QObject *parent = 0);
~QEvdevMouseManager();
void handleMouseEvent(int x, int y, bool abs, Qt::MouseButtons buttons);
void handleMouseEvent(int x, int y, bool abs, Qt::MouseButtons buttons,
Qt::MouseButton button, QEvent::Type type);
void handleWheelEvent(QPoint delta);
void addMouse(const QString &deviceNode = QString());

View File

@ -39,6 +39,7 @@
#include "qlibinputpointer_p.h"
#include <libinput.h>
#include <QtCore/QEvent>
#include <QtGui/QGuiApplication>
#include <QtGui/QScreen>
#include <qpa/qwindowsysteminterface.h>
@ -78,7 +79,10 @@ void QLibInputPointer::processButton(libinput_event_pointer *e)
m_buttons.setFlag(button, pressed);
QWindowSystemInterface::handleMouseEvent(nullptr, m_pos, m_pos, m_buttons, QGuiApplication::keyboardModifiers());
QEvent::Type type = pressed ? QEvent::MouseButtonPress : QEvent::MouseButtonRelease;
Qt::KeyboardModifiers mods = QGuiApplication::keyboardModifiers();
QWindowSystemInterface::handleMouseEvent(nullptr, m_pos, m_pos, m_buttons, button, type, mods);
}
void QLibInputPointer::processMotion(libinput_event_pointer *e)
@ -91,7 +95,10 @@ void QLibInputPointer::processMotion(libinput_event_pointer *e)
m_pos.setX(qBound(g.left(), qRound(m_pos.x() + dx), g.right()));
m_pos.setY(qBound(g.top(), qRound(m_pos.y() + dy), g.bottom()));
QWindowSystemInterface::handleMouseEvent(nullptr, m_pos, m_pos, m_buttons, QGuiApplication::keyboardModifiers());
Qt::KeyboardModifiers mods = QGuiApplication::keyboardModifiers();
QWindowSystemInterface::handleMouseEvent(nullptr, m_pos, m_pos, m_buttons,
Qt::NoButton, QEvent::MouseMove, mods);
}
void QLibInputPointer::processAxis(libinput_event_pointer *e)

View File

@ -983,6 +983,12 @@ static Qt::MouseButtons translateMouseButtons(int s)
return ret;
}
void QXcbConnection::setButtonState(Qt::MouseButton button, bool down)
{
m_buttonState.setFlag(button, down);
m_button = button;
}
Qt::MouseButton QXcbConnection::translateMouseButton(xcb_button_t s)
{
switch (s) {
@ -1055,7 +1061,7 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
// the event explicitly contains the state of the three first buttons,
// the rest we need to manage ourselves
m_buttonState = (m_buttonState & ~0x7) | translateMouseButtons(ev->state);
m_buttonState |= translateMouseButton(ev->detail);
setButtonState(translateMouseButton(ev->detail), true);
if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))
qCDebug(lcQpaXInputEvents, "legacy mouse press, button %d state %X", ev->detail, static_cast<unsigned int>(m_buttonState));
HANDLE_PLATFORM_WINDOW_EVENT(xcb_button_press_event_t, event, handleButtonPressEvent);
@ -1064,7 +1070,7 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
xcb_button_release_event_t *ev = (xcb_button_release_event_t *)event;
m_keyboard->updateXKBStateFromCore(ev->state);
m_buttonState = (m_buttonState & ~0x7) | translateMouseButtons(ev->state);
m_buttonState &= ~translateMouseButton(ev->detail);
setButtonState(translateMouseButton(ev->detail), false);
if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))
qCDebug(lcQpaXInputEvents, "legacy mouse release, button %d state %X", ev->detail, static_cast<unsigned int>(m_buttonState));
HANDLE_PLATFORM_WINDOW_EVENT(xcb_button_release_event_t, event, handleButtonReleaseEvent);

View File

@ -485,8 +485,9 @@ public:
xcb_window_t getSelectionOwner(xcb_atom_t atom) const;
xcb_window_t getQtSelectionOwner();
void setButtonState(Qt::MouseButton button, bool down) { m_buttonState.setFlag(button, down); }
void setButtonState(Qt::MouseButton button, bool down);
Qt::MouseButtons buttonState() const { return m_buttonState; }
Qt::MouseButton button() const { return m_button; }
Qt::MouseButton translateMouseButton(xcb_button_t s);
QXcbWindow *focusWindow() const { return m_focusWindow; }
@ -700,6 +701,7 @@ private:
bool has_render_extension = false;
Qt::MouseButtons m_buttonState = 0;
Qt::MouseButton m_button = Qt::NoButton;
QXcbWindow *m_focusWindow = nullptr;
QXcbWindow *m_mouseGrabber = nullptr;

View File

@ -2111,7 +2111,8 @@ void QXcbWindow::handleUnmapNotifyEvent(const xcb_unmap_notify_event_t *event)
}
void QXcbWindow::handleButtonPressEvent(int event_x, int event_y, int root_x, int root_y,
int detail, Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp, Qt::MouseEventSource source)
int detail, Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp,
QEvent::Type type, Qt::MouseEventSource source)
{
const bool isWheel = detail >= 4 && detail <= 7;
if (!isWheel && window() != QGuiApplication::focusWindow()) {
@ -2160,11 +2161,12 @@ void QXcbWindow::handleButtonPressEvent(int event_x, int event_y, int root_x, in
connection()->setMousePressWindow(this);
handleMouseEvent(timestamp, local, global, modifiers, source);
handleMouseEvent(timestamp, local, global, modifiers, type, source);
}
void QXcbWindow::handleButtonReleaseEvent(int event_x, int event_y, int root_x, int root_y,
int detail, Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp, Qt::MouseEventSource source)
int detail, Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp,
QEvent::Type type, Qt::MouseEventSource source)
{
QPoint local(event_x, event_y);
QPoint global(root_x, root_y);
@ -2177,7 +2179,7 @@ void QXcbWindow::handleButtonReleaseEvent(int event_x, int event_y, int root_x,
if (connection()->buttonState() == Qt::NoButton)
connection()->setMousePressWindow(nullptr);
handleMouseEvent(timestamp, local, global, modifiers, source);
handleMouseEvent(timestamp, local, global, modifiers, type, source);
}
static inline bool doCheckUnGrabAncestor(QXcbConnection *conn)
@ -2278,7 +2280,8 @@ void QXcbWindow::handleLeaveNotifyEvent(int root_x, int root_y,
}
void QXcbWindow::handleMotionNotifyEvent(int event_x, int event_y, int root_x, int root_y,
Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp, Qt::MouseEventSource source)
Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp,
QEvent::Type type, Qt::MouseEventSource source)
{
QPoint local(event_x, event_y);
QPoint global(root_x, root_y);
@ -2292,27 +2295,28 @@ void QXcbWindow::handleMotionNotifyEvent(int event_x, int event_y, int root_x, i
else if (hasMousePressWindow && !isMouseButtonPressed)
connection()->setMousePressWindow(nullptr);
handleMouseEvent(timestamp, local, global, modifiers, source);
handleMouseEvent(timestamp, local, global, modifiers, type, source);
}
void QXcbWindow::handleButtonPressEvent(const xcb_button_press_event_t *event)
{
Qt::KeyboardModifiers modifiers = connection()->keyboard()->translateModifiers(event->state);
handleButtonPressEvent(event->event_x, event->event_y, event->root_x, event->root_y, event->detail,
modifiers, event->time);
modifiers, event->time, QEvent::MouseButtonPress);
}
void QXcbWindow::handleButtonReleaseEvent(const xcb_button_release_event_t *event)
{
Qt::KeyboardModifiers modifiers = connection()->keyboard()->translateModifiers(event->state);
handleButtonReleaseEvent(event->event_x, event->event_y, event->root_x, event->root_y, event->detail,
modifiers, event->time);
modifiers, event->time, QEvent::MouseButtonRelease);
}
void QXcbWindow::handleMotionNotifyEvent(const xcb_motion_notify_event_t *event)
{
Qt::KeyboardModifiers modifiers = connection()->keyboard()->translateModifiers(event->state);
handleMotionNotifyEvent(event->event_x, event->event_y, event->root_x, event->root_y, modifiers, event->time);
handleMotionNotifyEvent(event->event_x, event->event_y, event->root_x, event->root_y, modifiers,
event->time, QEvent::MouseMove);
}
#if QT_CONFIG(xinput2)
@ -2363,18 +2367,18 @@ void QXcbWindow::handleXIMouseEvent(xcb_ge_event_t *event, Qt::MouseEventSource
if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))
qCDebug(lcQpaXInputEvents, "XI2 mouse press, button %d, time %d, source %s", button, ev->time, sourceName);
conn->setButtonState(button, true);
handleButtonPressEvent(event_x, event_y, root_x, root_y, ev->detail, modifiers, ev->time, source);
handleButtonPressEvent(event_x, event_y, root_x, root_y, ev->detail, modifiers, ev->time, QEvent::MouseButtonPress, source);
break;
case XI_ButtonRelease:
if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))
qCDebug(lcQpaXInputEvents, "XI2 mouse release, button %d, time %d, source %s", button, ev->time, sourceName);
conn->setButtonState(button, false);
handleButtonReleaseEvent(event_x, event_y, root_x, root_y, ev->detail, modifiers, ev->time, source);
handleButtonReleaseEvent(event_x, event_y, root_x, root_y, ev->detail, modifiers, ev->time, QEvent::MouseButtonRelease, source);
break;
case XI_Motion:
if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))
qCDebug(lcQpaXInputEvents, "XI2 mouse motion %d,%d, time %d, source %s", event_x, event_y, ev->time, sourceName);
handleMotionNotifyEvent(event_x, event_y, root_x, root_y, modifiers, ev->time, source);
handleMotionNotifyEvent(event_x, event_y, root_x, root_y, modifiers, ev->time, QEvent::MouseMove, source);
break;
default:
qWarning() << "Unrecognized XI2 mouse event" << ev->evtype;
@ -2419,10 +2423,13 @@ void QXcbWindow::handleXIEnterLeave(xcb_ge_event_t *event)
QXcbWindow *QXcbWindow::toWindow() { return this; }
void QXcbWindow::handleMouseEvent(xcb_timestamp_t time, const QPoint &local, const QPoint &global,
Qt::KeyboardModifiers modifiers, Qt::MouseEventSource source)
Qt::KeyboardModifiers modifiers, QEvent::Type type, Qt::MouseEventSource source)
{
connection()->setTime(time);
QWindowSystemInterface::handleMouseEvent(window(), time, local, global, connection()->buttonState(), modifiers, source);
Qt::MouseButton button = type == QEvent::MouseMove ? Qt::NoButton : connection()->button();
QWindowSystemInterface::handleMouseEvent(window(), time, local, global,
connection()->buttonState(), button,
type, modifiers, source);
}
void QXcbWindow::handleEnterNotifyEvent(const xcb_enter_notify_event_t *event)

View File

@ -146,7 +146,7 @@ public:
QXcbWindow *toWindow() override;
void handleMouseEvent(xcb_timestamp_t time, const QPoint &local, const QPoint &global,
Qt::KeyboardModifiers modifiers, Qt::MouseEventSource source);
Qt::KeyboardModifiers modifiers, QEvent::Type type, Qt::MouseEventSource source);
void updateNetWmUserTime(xcb_timestamp_t timestamp);
@ -221,13 +221,16 @@ protected:
bool compressExposeEvent(QRegion &exposeRegion);
void handleButtonPressEvent(int event_x, int event_y, int root_x, int root_y,
int detail, Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp, Qt::MouseEventSource source = Qt::MouseEventNotSynthesized);
int detail, Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp,
QEvent::Type type, Qt::MouseEventSource source = Qt::MouseEventNotSynthesized);
void handleButtonReleaseEvent(int event_x, int event_y, int root_x, int root_y,
int detail, Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp, Qt::MouseEventSource source = Qt::MouseEventNotSynthesized);
int detail, Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp,
QEvent::Type type, Qt::MouseEventSource source = Qt::MouseEventNotSynthesized);
void handleMotionNotifyEvent(int event_x, int event_y, int root_x, int root_y,
Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp, Qt::MouseEventSource source = Qt::MouseEventNotSynthesized);
Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp,
QEvent::Type type, Qt::MouseEventSource source = Qt::MouseEventNotSynthesized);
void handleEnterNotifyEvent(int event_x, int event_y, int root_x, int root_y,
quint8 mode, quint8 detail, xcb_timestamp_t timestamp);

View File

@ -64,7 +64,9 @@
QT_BEGIN_NAMESPACE
Q_GUI_EXPORT void qt_handleMouseEvent(QWindow *w, const QPointF &local, const QPointF &global, Qt::MouseButtons b, Qt::KeyboardModifiers mods, int timestamp);
Q_GUI_EXPORT void qt_handleMouseEvent(QWindow *window, const QPointF &local, const QPointF &global,
Qt::MouseButtons state, Qt::MouseButton button,
QEvent::Type type, Qt::KeyboardModifiers mods, int timestamp);
namespace QTest
{
@ -120,23 +122,28 @@ namespace QTest
switch (action)
{
case MouseDClick:
qt_handleMouseEvent(w, pos, global, button, stateKey, ++lastMouseTimestamp);
qt_handleMouseEvent(w, pos, global, Qt::NoButton, stateKey, ++lastMouseTimestamp);
qt_handleMouseEvent(w, pos, global, button, button, QEvent::MouseButtonPress,
stateKey, ++lastMouseTimestamp);
qt_handleMouseEvent(w, pos, global, Qt::NoButton, button, QEvent::MouseButtonRelease,
stateKey, ++lastMouseTimestamp);
Q_FALLTHROUGH();
case MousePress:
case MouseClick:
qt_handleMouseEvent(w, pos, global, button, stateKey, ++lastMouseTimestamp);
qt_handleMouseEvent(w, pos, global, button, button, QEvent::MouseButtonPress,
stateKey, ++lastMouseTimestamp);
lastMouseButton = button;
if (action == MousePress)
break;
Q_FALLTHROUGH();
case MouseRelease:
qt_handleMouseEvent(w, pos, global, Qt::NoButton, stateKey, ++lastMouseTimestamp);
qt_handleMouseEvent(w, pos, global, Qt::NoButton, button, QEvent::MouseButtonRelease,
stateKey, ++lastMouseTimestamp);
lastMouseTimestamp += mouseDoubleClickInterval; // avoid double clicks being generated
lastMouseButton = Qt::NoButton;
break;
case MouseMove:
qt_handleMouseEvent(w, pos, global, lastMouseButton, stateKey, ++lastMouseTimestamp);
qt_handleMouseEvent(w, pos, global, lastMouseButton, Qt::NoButton, QEvent::MouseMove,
stateKey, ++lastMouseTimestamp);
// No QCursor::setPos() call here. That could potentially result in mouse events sent by the windowing system
// which is highly undesired here. Tests must avoid relying on QCursor.
break;