diff --git a/autotests/test_screen_edges.cpp b/autotests/test_screen_edges.cpp --- a/autotests/test_screen_edges.cpp +++ b/autotests/test_screen_edges.cpp @@ -466,62 +466,65 @@ event.same_screen_focus = 1; event.time = QDateTime::currentMSecsSinceEpoch(); setPos(QPoint(0, 50)); - QVERIFY(s->isEntered(&event)); + auto isEntered = [s] (xcb_enter_notify_event_t *event) { + return s->handleEnterNotifiy(event->event, QPoint(event->root_x, event->root_y), QDateTime::fromMSecsSinceEpoch(event->time)); + }; + QVERIFY(isEntered(&event)); // doesn't trigger as the edge was not triggered yet QVERIFY(spy.isEmpty()); QCOMPARE(Cursor::pos(), QPoint(1, 50)); // test doesn't trigger due to too much offset QTest::qWait(160); setPos(QPoint(0, 100)); event.time = QDateTime::currentMSecsSinceEpoch(); - QVERIFY(s->isEntered(&event)); + QVERIFY(isEntered(&event)); QVERIFY(spy.isEmpty()); QCOMPARE(Cursor::pos(), QPoint(1, 100)); // doesn't trigger as we are waiting too long already QTest::qWait(200); setPos(QPoint(0, 101)); event.time = QDateTime::currentMSecsSinceEpoch(); - QVERIFY(s->isEntered(&event)); + QVERIFY(isEntered(&event)); QVERIFY(spy.isEmpty()); QCOMPARE(Cursor::pos(), QPoint(1, 101)); // doesn't activate as we are waiting too short QTest::qWait(50); setPos(QPoint(0, 100)); event.time = QDateTime::currentMSecsSinceEpoch(); - QVERIFY(s->isEntered(&event)); + QVERIFY(isEntered(&event)); QVERIFY(spy.isEmpty()); QCOMPARE(Cursor::pos(), QPoint(1, 100)); // and this one triggers QTest::qWait(110); setPos(QPoint(0, 101)); event.time = QDateTime::currentMSecsSinceEpoch(); - QVERIFY(s->isEntered(&event)); + QVERIFY(isEntered(&event)); QVERIFY(!spy.isEmpty()); QCOMPARE(Cursor::pos(), QPoint(1, 101)); // now let's try to trigger again QTest::qWait(351); setPos(QPoint(0, 100)); event.time = QDateTime::currentMSecsSinceEpoch(); - QVERIFY(s->isEntered(&event)); + QVERIFY(isEntered(&event)); QCOMPARE(spy.count(), 1); QCOMPARE(Cursor::pos(), QPoint(1, 100)); // it's still under the reactivation QTest::qWait(50); setPos(QPoint(0, 100)); event.time = QDateTime::currentMSecsSinceEpoch(); - QVERIFY(s->isEntered(&event)); + QVERIFY(isEntered(&event)); QCOMPARE(spy.count(), 1); QCOMPARE(Cursor::pos(), QPoint(1, 100)); // now it should trigger again QTest::qWait(250); setPos(QPoint(0, 100)); event.time = QDateTime::currentMSecsSinceEpoch(); - QVERIFY(s->isEntered(&event)); + QVERIFY(isEntered(&event)); QCOMPARE(spy.count(), 2); QCOMPARE(spy.first().first().value(), ElectricLeft); QCOMPARE(spy.last().first().value(), ElectricLeft); @@ -536,7 +539,7 @@ // it should trigger directly QTest::qWait(350); event.time = QDateTime::currentMSecsSinceEpoch(); - QVERIFY(s->isEntered(&event)); + QVERIFY(isEntered(&event)); QCOMPARE(spy.count(), 3); QCOMPARE(spy.at(0).first().value(), ElectricLeft); QCOMPARE(spy.at(1).first().value(), ElectricLeft); @@ -645,7 +648,10 @@ event.event = s->windows().first(); event.same_screen_focus = 1; event.time = QDateTime::currentMSecsSinceEpoch(); - QVERIFY(s->isEntered(&event)); + auto isEntered = [s] (xcb_enter_notify_event_t *event) { + return s->handleEnterNotifiy(event->event, QPoint(event->root_x, event->root_y), QDateTime::fromMSecsSinceEpoch(event->time)); + }; + QVERIFY(isEntered(&event)); QVERIFY(spy.isEmpty()); QTEST(Cursor::pos(), "expected"); @@ -692,7 +698,10 @@ event.event = s->windows().first(); event.same_screen_focus = 1; event.time = QDateTime::currentMSecsSinceEpoch(); - QVERIFY(s->isEntered(&event)); + auto isEntered = [s] (xcb_enter_notify_event_t *event) { + return s->handleEnterNotifiy(event->event, QPoint(event->root_x, event->root_y), QDateTime::fromMSecsSinceEpoch(event->time)); + }; + QVERIFY(isEntered(&event)); QVERIFY(spy.isEmpty()); QCOMPARE(Cursor::pos(), QPoint(1, 50)); @@ -710,7 +719,7 @@ QTest::qWait(160); Cursor::setPos(0, 50); event.time = QDateTime::currentMSecsSinceEpoch(); - QVERIFY(s->isEntered(&event)); + QVERIFY(isEntered(&event)); QVERIFY(spy.isEmpty()); // and no pushback QCOMPARE(Cursor::pos(), QPoint(0, 50)); @@ -722,7 +731,7 @@ QCOMPARE(e->activatesForTouchGesture(), e->border() == KWin::ElectricRight); } event.time = QDateTime::currentMSecsSinceEpoch(); - QVERIFY(s->isEntered(&event)); + QVERIFY(isEntered(&event)); QVERIFY(!spy.isEmpty()); QCOMPARE(Cursor::pos(), QPoint(1, 50)); @@ -734,16 +743,16 @@ spy.clear(); Cursor::setPos(0, 50); event.time = QDateTime::currentMSecsSinceEpoch(); - QVERIFY(s->isEntered(&event)); + QVERIFY(isEntered(&event)); QVERIFY(spy.isEmpty()); // and a pushback QCOMPARE(Cursor::pos(), QPoint(1, 50)); // just to be sure, let's set geometry back client.setGeometry(screens()->geometry()); emit s->checkBlocking(); Cursor::setPos(0, 50); - QVERIFY(s->isEntered(&event)); + QVERIFY(isEntered(&event)); QVERIFY(spy.isEmpty()); // and no pushback QCOMPARE(Cursor::pos(), QPoint(0, 50)); @@ -757,14 +766,14 @@ event.event = s->windows().first(); event.time = QDateTime::currentMSecsSinceEpoch(); Cursor::setPos(99, 99); - QVERIFY(s->isEntered(&event)); + QVERIFY(isEntered(&event)); QVERIFY(spy.isEmpty()); // and pushback QCOMPARE(Cursor::pos(), QPoint(98, 98)); QTest::qWait(160); event.time = QDateTime::currentMSecsSinceEpoch(); Cursor::setPos(99, 99); - QVERIFY(s->isEntered(&event)); + QVERIFY(isEntered(&event)); QVERIFY(!spy.isEmpty()); } @@ -812,7 +821,10 @@ event.event = s->windows().first(); event.same_screen_focus = 1; event.time = QDateTime::currentMSecsSinceEpoch(); - QVERIFY(s->isEntered(&event)); + auto isEntered = [s] (xcb_enter_notify_event_t *event) { + return s->handleEnterNotifiy(event->event, QPoint(event->root_x, event->root_y), QDateTime::fromMSecsSinceEpoch(event->time)); + }; + QVERIFY(isEntered(&event)); // autohiding panels shall activate instantly QCOMPARE(client.isHiddenInternal(), false); QCOMPARE(Cursor::pos(), QPoint(1, 50)); @@ -882,7 +894,7 @@ event2.event = s->windows().first(); event2.same_screen_focus = 1; event2.time = QDateTime::currentMSecsSinceEpoch(); - QVERIFY(s->isEntered(&event2)); + QVERIFY(isEntered(&event2)); QCOMPARE(client.keepBelow(), false); QCOMPARE(client.isHiddenInternal(), false); QCOMPARE(Cursor::pos(), QPoint(1, 50)); @@ -946,7 +958,10 @@ event.same_screen_focus = 1; event.time = QDateTime::currentMSecsSinceEpoch(); setPos(QPoint(0, 50)); - QCOMPARE(s->isEntered(&event), false); + auto isEntered = [s] (xcb_enter_notify_event_t *event) { + return s->handleEnterNotifiy(event->event, QPoint(event->root_x, event->root_y), QDateTime::fromMSecsSinceEpoch(event->time)); + }; + QCOMPARE(isEntered(&event), false); QVERIFY(approachingSpy.isEmpty()); // let's also verify the check s->check(QPoint(0, 50), QDateTime::currentDateTime(), false); diff --git a/events.cpp b/events.cpp --- a/events.cpp +++ b/events.cpp @@ -39,7 +39,6 @@ #include "unmanaged.h" #include "useractions.h" #include "effects.h" -#include "screenedge.h" #include "screens.h" #include "xcbutils.h" @@ -276,15 +275,9 @@ return true; } auto *mouseEvent = reinterpret_cast(e); - const QPoint rootPos(mouseEvent->root_x, mouseEvent->root_y); if (effects && static_cast(effects)->checkInputWindowEvent(mouseEvent)) { return true; } - if (QWidget::mouseGrabber()) { - ScreenEdges::self()->check(rootPos, QDateTime::fromMSecsSinceEpoch(xTime()), true); - } else { - ScreenEdges::self()->check(rootPos, QDateTime::fromMSecsSinceEpoch(mouseEvent->time)); - } break; } case XCB_CONFIGURE_NOTIFY: @@ -383,11 +376,6 @@ return (event->event != event->window); // hide wm typical event from Qt } - case XCB_ENTER_NOTIFY: { - if (ScreenEdges::self()->isEntered(reinterpret_cast(e))) - return true; - break; - } case XCB_CONFIGURE_REQUEST: { const auto *event = reinterpret_cast(e); if (event->parent == rootWindow()) { @@ -439,10 +427,6 @@ // fall through case XCB_FOCUS_OUT: return true; // always eat these, they would tell Qt that KWin is the active app - case XCB_CLIENT_MESSAGE: - if (ScreenEdges::self()->isEntered(reinterpret_cast(e))) - return true; - break; default: if (eventType == Xcb::Extensions::self()->randrNotifyEvent() && Xcb::Extensions::self()->isRandrAvailable()) { auto *event = reinterpret_cast(e); diff --git a/plugins/platforms/x11/standalone/CMakeLists.txt b/plugins/platforms/x11/standalone/CMakeLists.txt --- a/plugins/platforms/x11/standalone/CMakeLists.txt +++ b/plugins/platforms/x11/standalone/CMakeLists.txt @@ -6,6 +6,7 @@ screens_xrandr.cpp windowselector.cpp overlaywindow_x11.cpp + screenedges_filter.cpp ) if(X11_Xinput_FOUND) diff --git a/plugins/platforms/x11/standalone/screenedges_filter.h b/plugins/platforms/x11/standalone/screenedges_filter.h new file mode 100644 --- /dev/null +++ b/plugins/platforms/x11/standalone/screenedges_filter.h @@ -0,0 +1,37 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2017 Martin Flöser + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*********************************************************************/ +#ifndef KWIN_SCREENEDGES_FILTER_H +#define KWIN_SCREENEDGES_FILTER_H +#include "x11eventfilter.h" + +namespace KWin +{ + +class ScreenEdgesFilter : public X11EventFilter +{ +public: + explicit ScreenEdgesFilter(); + + bool event(xcb_generic_event_t *event) override; +}; + +} + +#endif diff --git a/plugins/platforms/x11/standalone/screenedges_filter.cpp b/plugins/platforms/x11/standalone/screenedges_filter.cpp new file mode 100644 --- /dev/null +++ b/plugins/platforms/x11/standalone/screenedges_filter.cpp @@ -0,0 +1,65 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2017 Martin Flöser + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*********************************************************************/ +#include "screenedges_filter.h" +#include "atoms.h" +#include "screenedge.h" + +#include +#include + +namespace KWin +{ + +ScreenEdgesFilter::ScreenEdgesFilter() + : X11EventFilter(QVector{XCB_MOTION_NOTIFY, XCB_ENTER_NOTIFY, XCB_CLIENT_MESSAGE}) +{ +} + +bool ScreenEdgesFilter::event(xcb_generic_event_t *event) +{ + const uint8_t eventType = event->response_type & ~0x80; + switch (eventType) { + case XCB_MOTION_NOTIFY: { + const auto mouseEvent = reinterpret_cast(event); + const QPoint rootPos(mouseEvent->root_x, mouseEvent->root_y); + if (QWidget::mouseGrabber()) { + ScreenEdges::self()->check(rootPos, QDateTime::fromMSecsSinceEpoch(xTime()), true); + } else { + ScreenEdges::self()->check(rootPos, QDateTime::fromMSecsSinceEpoch(mouseEvent->time)); + } + // not filtered out + break; + } + case XCB_ENTER_NOTIFY: { + const auto enter = reinterpret_cast(event); + return ScreenEdges::self()->handleEnterNotifiy(enter->event, QPoint(enter->root_x, enter->root_y), QDateTime::fromMSecsSinceEpoch(enter->time)); + } + case XCB_CLIENT_MESSAGE: { + const auto ce = reinterpret_cast(event); + if (ce->type != atoms->xdnd_position) { + return false; + } + return ScreenEdges::self()->handleDndNotify(ce->window, QPoint(ce->data.data32[2] >> 16, ce->data.data32[2] & 0xffff)); + } + } + return false; +} + +} diff --git a/plugins/platforms/x11/standalone/x11_platform.h b/plugins/platforms/x11/standalone/x11_platform.h --- a/plugins/platforms/x11/standalone/x11_platform.h +++ b/plugins/platforms/x11/standalone/x11_platform.h @@ -29,6 +29,7 @@ { class XInputIntegration; class WindowSelector; +class X11EventFilter; class KWIN_EXPORT X11StandalonePlatform : public Platform { @@ -80,6 +81,7 @@ QTimer *m_openGLFreezeProtection = nullptr; Display *m_x11Display; QScopedPointer m_windowSelector; + QScopedPointer m_screenEdgesFilter; }; diff --git a/plugins/platforms/x11/standalone/x11_platform.cpp b/plugins/platforms/x11/standalone/x11_platform.cpp --- a/plugins/platforms/x11/standalone/x11_platform.cpp +++ b/plugins/platforms/x11/standalone/x11_platform.cpp @@ -33,6 +33,7 @@ #include "keyboard_input.h" #include "logging.h" #include "screens_xrandr.h" +#include "screenedges_filter.h" #include "options.h" #include "overlaywindow_x11.h" @@ -113,6 +114,9 @@ Edge *X11StandalonePlatform::createScreenEdge(ScreenEdges *edges) { + if (m_screenEdgesFilter.isNull()) { + m_screenEdgesFilter.reset(new ScreenEdgesFilter); + } return new WindowBasedEdge(edges); } diff --git a/screenedge.h b/screenedge.h --- a/screenedge.h +++ b/screenedge.h @@ -312,14 +312,6 @@ * to do this if an effect input window is active. */ void ensureOnTop(); - /** - * Called when the user entered an electric border with the mouse. - * It may switch to another virtual desktop. - * @param e the X event which is passed to this method. - */ - bool isEntered(xcb_generic_event_t *e); - bool isEntered(xcb_enter_notify_event_t *e); - bool isEntered(xcb_client_message_event_t *e); bool isEntered(QMouseEvent *event); /** @@ -352,6 +344,9 @@ return m_gestureRecognizer; } + bool handleDndNotify(xcb_window_t window, const QPoint &point); + bool handleEnterNotifiy(xcb_window_t window, const QPoint &point, const QDateTime ×tamp); + public Q_SLOTS: void reconfigure(); /** @@ -387,8 +382,6 @@ void setActionForTouchBorder(ElectricBorder border, ElectricBorderAction newValue); ElectricBorderAction actionForEdge(Edge *edge) const; ElectricBorderAction actionForTouchEdge(Edge *edge) const; - bool handleEnterNotifiy(xcb_window_t window, const QPoint &point, const QDateTime ×tamp); - bool handleDndNotify(xcb_window_t window, const QPoint &point); void createEdgeForClient(AbstractClient *client, ElectricBorder border); void deleteEdgeForClient(AbstractClient *client); bool m_desktopSwitching; diff --git a/screenedge.cpp b/screenedge.cpp --- a/screenedge.cpp +++ b/screenedge.cpp @@ -30,7 +30,6 @@ #include "screenedge.h" // KWin -#include "atoms.h" #include "gestures.h" #include #include "cursor.h" @@ -1367,22 +1366,6 @@ } } -bool ScreenEdges::isEntered(xcb_enter_notify_event_t *event) -{ - return handleEnterNotifiy(event->event, - QPoint(event->root_x, event->root_y), - QDateTime::fromMSecsSinceEpoch(event->time)); -} - -bool ScreenEdges::isEntered(xcb_client_message_event_t *event) -{ - if (event->type != atoms->xdnd_position) { - return false; - } - return handleDndNotify(event->window, - QPoint(event->data.data32[2] >> 16, event->data.data32[2] & 0xffff)); -} - bool ScreenEdges::isEntered(QMouseEvent *event) { if (event->type() != QEvent::MouseMove) {