forked from github_mirror/framelesshelper
Linux: fix the mouse grab issue, for real
Port previous workaround from 1.x to 2.0 And some minor tweaks. Signed-off-by: Yuhang Zhao <2546789017@qq.com>
This commit is contained in:
parent
2915d1f33a
commit
3c0209c979
|
@ -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}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -209,8 +209,10 @@ void Utils::startSystemResize(QWindow *window, const Qt::Edges edges, const QPoi
|
|||
}
|
||||
#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
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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))
|
||||
|
|
|
@ -97,7 +97,6 @@ private:
|
|||
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:
|
||||
|
@ -116,6 +115,7 @@ private:
|
|||
QPointer<QQuickItem> m_titleBarItem = nullptr;
|
||||
QList<QQuickItem *> m_hitTestVisibleItems = {};
|
||||
QuickGlobal::Options m_quickOptions = {};
|
||||
bool m_mouseLeftButtonPressed = false;
|
||||
};
|
||||
|
||||
FRAMELESSHELPER_END_NAMESPACE
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -277,28 +277,41 @@ void FramelessWidgetsHelper::paintEventHandler(QPaintEvent *event)
|
|||
|
||||
void FramelessWidgetsHelper::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 FramelessWidgetsHelper::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 FramelessWidgetsHelper::mouseReleaseEventHandler(QMouseEvent *event)
|
||||
|
@ -307,10 +320,14 @@ void FramelessWidgetsHelper::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))
|
||||
|
@ -667,31 +684,6 @@ bool FramelessWidgetsHelper::shouldIgnoreMouseEvents(const QPoint &pos) const
|
|||
return (isNormal() && withinFrameBorder);
|
||||
}
|
||||
|
||||
void FramelessWidgetsHelper::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);
|
||||
}
|
||||
|
||||
void FramelessWidgetsHelper::updateContentsMargins()
|
||||
{
|
||||
#ifdef Q_OS_WINDOWS
|
||||
|
@ -706,29 +698,26 @@ void FramelessWidgetsHelper::updateSystemTitleBarStyleSheet()
|
|||
}
|
||||
const bool active = q->isActiveWindow();
|
||||
const bool dark = Utils::shouldAppsUseDarkMode();
|
||||
#ifdef Q_OS_WINDOWS
|
||||
const bool colorizedTitleBar = Utils::isTitleBarColorized();
|
||||
#else
|
||||
constexpr const bool colorizedTitleBar = false;
|
||||
#endif
|
||||
const QColor systemTitleBarWidgetBackgroundColor = [active, colorizedTitleBar, dark]() -> QColor {
|
||||
#ifndef Q_OS_WINDOWS
|
||||
Q_UNUSED(colorizedTitleBar);
|
||||
#endif
|
||||
if (active) {
|
||||
#ifdef Q_OS_WINDOWS
|
||||
if (colorizedTitleBar) {
|
||||
#ifdef Q_OS_WINDOWS
|
||||
return Utils::getDwmColorizationColor();
|
||||
} else {
|
||||
#endif
|
||||
#ifdef Q_OS_LINUX
|
||||
return Utils::getWmThemeColor();
|
||||
#endif
|
||||
#ifdef Q_OS_MACOS
|
||||
return Utils::getControlsAccentColor();
|
||||
#endif
|
||||
} else {
|
||||
if (dark) {
|
||||
return kDefaultBlackColor;
|
||||
} else {
|
||||
return kDefaultWhiteColor;
|
||||
}
|
||||
#ifdef Q_OS_WINDOWS
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
if (dark) {
|
||||
return kDefaultSystemDarkColor;
|
||||
|
@ -841,17 +830,14 @@ bool FramelessWidgetsHelper::eventFilter(QObject *object, QEvent *event)
|
|||
const auto paintEvent = static_cast<QPaintEvent *>(event);
|
||||
paintEventHandler(paintEvent);
|
||||
} 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);
|
||||
|
|
|
@ -94,7 +94,6 @@ private:
|
|||
Q_NODISCARD bool isInTitleBarDraggableArea(const QPoint &pos) const;
|
||||
Q_NODISCARD bool shouldDrawFrameBorder() const;
|
||||
Q_NODISCARD bool shouldIgnoreMouseEvents(const QPoint &pos) const;
|
||||
void doStartSystemMove2(QMouseEvent *event);
|
||||
|
||||
private Q_SLOTS:
|
||||
void updateContentsMargins();
|
||||
|
@ -119,6 +118,7 @@ private:
|
|||
Global::UserSettings m_settings = {};
|
||||
Global::SystemParameters m_params = {};
|
||||
bool m_windowExposed = false;
|
||||
bool m_mouseLeftButtonPressed = false;
|
||||
};
|
||||
|
||||
FRAMELESSHELPER_END_NAMESPACE
|
||||
|
|
Loading…
Reference in New Issue