Skip to content
This repository has been archived by the owner on Dec 19, 2023. It is now read-only.

Commit

Permalink
Linux: fix the mouse grab issue, for real
Browse files Browse the repository at this point in the history
Port previous workaround from 1.x to 2.0

And some minor tweaks.

Signed-off-by: Yuhang Zhao <[email protected]>
  • Loading branch information
wangwenx190 committed Apr 24, 2022
1 parent 2915d1f commit 3c0209c
Show file tree
Hide file tree
Showing 8 changed files with 120 additions and 105 deletions.
2 changes: 1 addition & 1 deletion src/core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ if(UNIX AND NOT APPLE)
)
target_link_libraries(${SUB_PROJ_NAME} PRIVATE
${GTK3_LIBRARIES}
X11::X11
X11::xcb
)
target_include_directories(${SUB_PROJ_NAME} PRIVATE
${GTK3_INCLUDE_DIRS}
Expand Down
64 changes: 49 additions & 15 deletions src/core/utils_linux.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
#include <QtCore/qregularexpression.h>
#include <QtGui/qwindow.h>
#include <gtk/gtk.h>
#include <X11/Xlib.h>
#include <xcb/xcb.h>

FRAMELESSHELPER_BEGIN_NAMESPACE

Expand Down Expand Up @@ -113,6 +113,33 @@ template<typename T>
return -1;
}

static inline void
emulateMouseButtonRelease(const WId windowId, const QPoint &globalPos, const QPoint &localPos)
{
Q_ASSERT(windowId);
if (!windowId) {
return;
}
xcb_connection_t * const connection = QX11Info::connection();
Q_ASSERT(connection);
const quint32 rootWindow = QX11Info::appRootWindow(QX11Info::appScreen());
Q_ASSERT(rootWindow);
xcb_button_release_event_t xev;
memset(&xev, 0, sizeof(xev));
xev.response_type = XCB_BUTTON_RELEASE;
xev.time = XCB_CURRENT_TIME;
xev.root = rootWindow;
xev.root_x = globalPos.x();
xev.root_y = globalPos.y();
xev.event = windowId;
xev.event_x = localPos.x();
xev.event_y = localPos.y();
xev.same_screen = true;
xcb_send_event(connection, false, rootWindow, XCB_EVENT_MASK_BUTTON_RELEASE,
reinterpret_cast<const char *>(&xev));
xcb_flush(connection);
}

[[maybe_unused]] static inline void
doStartSystemMoveResize(const WId windowId, const QPoint &globalPos, const int edges)
{
Expand All @@ -123,7 +150,7 @@ template<typename T>
}
xcb_connection_t * const connection = QX11Info::connection();
Q_ASSERT(connection);
static const xcb_atom_t moveResize = [connection]() -> xcb_atom_t {
static const xcb_atom_t netMoveResize = [connection]() -> xcb_atom_t {
const xcb_intern_atom_cookie_t cookie = xcb_intern_atom(connection, false,
qstrlen(WM_MOVERESIZE_OPERATION_NAME), WM_MOVERESIZE_OPERATION_NAME);
xcb_intern_atom_reply_t * const reply = xcb_intern_atom_reply(connection, cookie, nullptr);
Expand All @@ -138,17 +165,20 @@ template<typename T>
xcb_client_message_event_t xev;
memset(&xev, 0, sizeof(xev));
xev.response_type = XCB_CLIENT_MESSAGE;
xev.type = moveResize;
xev.type = netMoveResize;
xev.window = windowId;
xev.format = 32;
xev.data.data32[0] = globalPos.x();
xev.data.data32[1] = globalPos.y();
xev.data.data32[2] = edges;
xev.data.data32[3] = XCB_BUTTON_INDEX_1;
// First we need to ungrab the pointer that may have been
// automatically grabbed by Qt on ButtonPressEvent.
xcb_ungrab_pointer(connection, XCB_CURRENT_TIME);
xcb_send_event(connection, false, rootWindow,
(XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY),
reinterpret_cast<const char *>(&xev));
xcb_flush(connection);
}

SystemTheme Utils::getSystemTheme()
Expand All @@ -163,15 +193,17 @@ void Utils::startSystemMove(QWindow *window, const QPoint &globalPos)
if (!window) {
return;
}
const WId windowId = window->winId();
const qreal dpr = window->devicePixelRatio();
const QPoint deviceGlobalPos = QPointF(QPointF(globalPos) * dpr).toPoint();
const QPoint logicalLocalPos = window->mapFromGlobal(globalPos);
const QPoint deviceLocalPos = QPointF(QPointF(logicalLocalPos) * dpr).toPoint();
// Before we start the dragging we need to tell Qt that the mouse is released.
emulateMouseButtonRelease(windowId, deviceGlobalPos, deviceLocalPos);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
Q_UNUSED(globalPos);
window->startSystemMove();
#else
// Qt always gives us logical coordinates, however, the native APIs
// are expecting device coordinates.
const qreal dpr = window->devicePixelRatio();
const QPoint globalPos2 = QPointF(QPointF(globalPos) * dpr).toPoint();
doStartSystemMoveResize(window->winId(), globalPos2, _NET_WM_MOVERESIZE_MOVE);
doStartSystemMoveResize(windowId, deviceGlobalPos, _NET_WM_MOVERESIZE_MOVE);
#endif
}

Expand All @@ -184,19 +216,21 @@ void Utils::startSystemResize(QWindow *window, const Qt::Edges edges, const QPoi
if (edges == Qt::Edges{}) {
return;
}
const WId windowId = window->winId();
const qreal dpr = window->devicePixelRatio();
const QPoint deviceGlobalPos = QPointF(QPointF(globalPos) * dpr).toPoint();
const QPoint logicalLocalPos = window->mapFromGlobal(globalPos);
const QPoint deviceLocalPos = QPointF(QPointF(logicalLocalPos) * dpr).toPoint();
// Before we start the resizing we need to tell Qt that the mouse is released.
emulateMouseButtonRelease(windowId, deviceGlobalPos, deviceLocalPos);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
Q_UNUSED(globalPos);
window->startSystemResize(edges);
#else
const int section = qtEdgesToWmMoveOrResizeOperation(edges);
if (section < 0) {
return;
}
// Qt always gives us logical coordinates, however, the native APIs
// are expecting device coordinates.
const qreal dpr = window->devicePixelRatio();
const QPoint globalPos2 = QPointF(QPointF(globalPos) * dpr).toPoint();
doStartSystemMoveResize(window->winId(), globalPos2, section);
doStartSystemMoveResize(windowId, deviceGlobalPos, section);
#endif
}

Expand Down
2 changes: 2 additions & 0 deletions src/core/utils_mac.mm
Original file line number Diff line number Diff line change
Expand Up @@ -209,8 +209,10 @@ void setSystemTitleBarVisible(const bool visible)
}
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
Q_UNUSED(globalPos);
// Actually Qt doesn't implement this function, it will do nothing and always returns false.
window->startSystemResize(edges);
#else
// ### TODO
Q_UNUSED(globalPos);
#endif
}
Expand Down
66 changes: 28 additions & 38 deletions src/quick/framelessquickwindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,7 @@ void FramelessQuickWindowPrivate::setOptions(const QuickGlobal::Options value)
if (m_quickOptions == value) {
return;
}
// ### TODO: re-evaluate some usable options.
m_quickOptions = value;
m_settings.options = optionsQuickToCore(m_quickOptions);
Q_EMIT q->optionsChanged();
Expand All @@ -367,17 +368,14 @@ bool FramelessQuickWindowPrivate::eventFilter(QObject *object, QEvent *event)
const auto showEvent = static_cast<QShowEvent *>(event);
showEventHandler(showEvent);
} break;
#ifdef Q_OS_WINDOWS
case QEvent::MouseMove: {
const auto mouseEvent = static_cast<QMouseEvent *>(event);
mouseMoveEventHandler(mouseEvent);
} break;
#else
case QEvent::MouseButtonPress: {
const auto mouseEvent = static_cast<QMouseEvent *>(event);
mousePressEventHandler(mouseEvent);
} break;
#endif
case QEvent::MouseButtonRelease: {
const auto mouseEvent = static_cast<QMouseEvent *>(event);
mouseReleaseEventHandler(mouseEvent);
Expand Down Expand Up @@ -640,31 +638,6 @@ bool FramelessQuickWindowPrivate::shouldIgnoreMouseEvents(const QPoint &pos) con
return (isNormal() && withinFrameBorder);
}

void FramelessQuickWindowPrivate::doStartSystemMove2(QMouseEvent *event)
{
Q_ASSERT(event);
if (!event) {
return;
}
if (m_settings.options & Option::DisableDragging) {
return;
}
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
const QPoint scenePos = event->scenePosition().toPoint();
const QPoint globalPos = event->globalPosition().toPoint();
#else
const QPoint scenePos = event->windowPos().toPoint();
const QPoint globalPos = event->screenPos().toPoint();
#endif
if (shouldIgnoreMouseEvents(scenePos)) {
return;
}
if (!isInTitleBarDraggableArea(scenePos)) {
return;
}
startSystemMove2(globalPos);
}

bool FramelessQuickWindowPrivate::shouldDrawFrameBorder() const
{
#ifdef Q_OS_WINDOWS
Expand Down Expand Up @@ -702,28 +675,41 @@ void FramelessQuickWindowPrivate::showEventHandler(QShowEvent *event)

void FramelessQuickWindowPrivate::mouseMoveEventHandler(QMouseEvent *event)
{
#ifdef Q_OS_WINDOWS
Q_ASSERT(event);
if (!event) {
return;
}
doStartSystemMove2(event);
if (!m_mouseLeftButtonPressed) {
return;
}
if (m_settings.options & Option::DisableDragging) {
return;
}
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
const QPoint scenePos = event->scenePosition().toPoint();
const QPoint globalPos = event->globalPosition().toPoint();
#else
Q_UNUSED(event);
const QPoint scenePos = event->windowPos().toPoint();
const QPoint globalPos = event->screenPos().toPoint();
#endif
if (shouldIgnoreMouseEvents(scenePos)) {
return;
}
if (!isInTitleBarDraggableArea(scenePos)) {
return;
}
startSystemMove2(globalPos);
}

void FramelessQuickWindowPrivate::mousePressEventHandler(QMouseEvent *event)
{
#ifdef Q_OS_WINDOWS
Q_UNUSED(event);
#else
Q_ASSERT(event);
if (!event) {
return;
}
doStartSystemMove2(event);
#endif
if (event->button() == Qt::LeftButton) {
m_mouseLeftButtonPressed = true;
}
}

void FramelessQuickWindowPrivate::mouseReleaseEventHandler(QMouseEvent *event)
Expand All @@ -732,10 +718,14 @@ void FramelessQuickWindowPrivate::mouseReleaseEventHandler(QMouseEvent *event)
if (!event) {
return;
}
if (m_settings.options & Option::DisableSystemMenu) {
const Qt::MouseButton button = event->button();
if (button == Qt::LeftButton) {
m_mouseLeftButtonPressed = false;
}
if (button != Qt::RightButton) {
return;
}
if (event->button() != Qt::RightButton) {
if (m_settings.options & Option::DisableSystemMenu) {
return;
}
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
Expand Down
2 changes: 1 addition & 1 deletion src/quick/framelessquickwindow_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,6 @@ public Q_SLOTS:
Q_NODISCARD bool isInSystemButtons(const QPoint &pos, QuickGlobal::SystemButtonType *button) const;
Q_NODISCARD bool isInTitleBarDraggableArea(const QPoint &pos) const;
Q_NODISCARD bool shouldIgnoreMouseEvents(const QPoint &pos) const;
void doStartSystemMove2(QMouseEvent *event);
Q_NODISCARD bool shouldDrawFrameBorder() const;

private Q_SLOTS:
Expand All @@ -116,6 +115,7 @@ private Q_SLOTS:
QPointer<QQuickItem> m_titleBarItem = nullptr;
QList<QQuickItem *> m_hitTestVisibleItems = {};
QuickGlobal::Options m_quickOptions = {};
bool m_mouseLeftButtonPressed = false;
};

FRAMELESSHELPER_END_NAMESPACE
3 changes: 3 additions & 0 deletions src/quick/quickstandardtitlebar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,9 @@ void QuickStandardTitleBar::updateTitleBarColor()
#ifdef Q_OS_WINDOWS
backgroundColor = Utils::getDwmColorizationColor();
#endif
#ifdef Q_OS_LINUX
backgroundColor = Utils::getWmThemeColor();
#endif
#ifdef Q_OS_MACOS
backgroundColor = Utils::getControlsAccentColor();
#endif
Expand Down
Loading

0 comments on commit 3c0209c

Please sign in to comment.