diff --git a/events.cpp b/events.cpp index c1ebbeed29..b433db6d7b 100644 --- a/events.cpp +++ b/events.cpp @@ -181,6 +181,7 @@ bool Workspace::workspaceEvent(xcb_generic_event_t *e) break; } case XCB_MOTION_NOTIFY: { + m_mouseMotionTimer->cancel(); auto *mouseEvent = reinterpret_cast(e); const QPoint rootPos(mouseEvent->root_x, mouseEvent->root_y); #ifdef KWIN_BUILD_TABBOX @@ -1237,39 +1238,6 @@ bool Client::buttonReleaseEvent(xcb_window_t w, int /*button*/, int state, int x return true; } -static bool was_motion = false; -static Time next_motion_time = CurrentTime; -// Check whole incoming X queue for MotionNotify events -// checking whole queue is done by always returning False in the predicate. -// If there are more MotionNotify events in the queue, all until the last -// one may be safely discarded (if a ButtonRelease event comes, a MotionNotify -// will be faked from it, so there's no need to check other events). -// This helps avoiding being overloaded by being flooded from many events -// from the XServer. -static Bool motion_predicate(Display*, XEvent* ev, XPointer) -{ - if (ev->type == MotionNotify) { - was_motion = true; - next_motion_time = ev->xmotion.time; // for setting time - } - return False; -} - -static bool waitingMotionEvent() -{ -// The queue doesn't need to be checked until the X timestamp -// of processes events reaches the timestamp of the last suitable -// MotionNotify event in the queue. - if (next_motion_time != CurrentTime - && timestampCompare(xTime(), next_motion_time) < 0) - return true; - was_motion = false; - XSync(display(), False); // this helps to discard more MotionNotify events - XEvent dummy; - XCheckIfEvent(display(), &dummy, motion_predicate, NULL); - return was_motion; -} - // Checks if the mouse cursor is near the edge of the screen and if so activates quick tiling or maximization void Client::checkQuickTilingMaximizationZones(int xroot, int yroot) { @@ -1320,18 +1288,22 @@ bool Client::motionNotifyEvent(xcb_window_t w, int state, int x, int y, int x_ro mode = newmode; updateCursor(); } - // reset the timestamp for the optimization, otherwise with long passivity - // the option in waitingMotionEvent() may be always true - next_motion_time = CurrentTime; return false; } if (w == moveResizeGrabWindow()) { x = this->x(); // translate from grab window to local coords y = this->y(); } -#warning Mouse event compression is lost - if (/*!waitingMotionEvent()*/true) { - QRect oldGeo = geometry(); + // mouse motion event compression: the event queue might have multiple motion events + // in that case we are only interested in the last event to not cause too much overhead + // by useless move/resize operations. + // The compression is done using a singleshot QTimer of 0 msec to just move the processing + // to the end of the event queue. In case there is another motion event in the queue it will + // be processed before the timer fires and the processing of the newer motion event cancels + // the running timer. Eventually this code path will be reached again and the timer is + // started again + workspace()->scheduleMouseMotionCompression([this, x, y, x_root, y_root]() { + const QRect oldGeo = geometry(); handleMoveResize(x, y, x_root, y_root); if (!isFullScreen() && isMove() && oldGeo != geometry()) { if (quick_tile_mode != QuickTileNone) { @@ -1345,7 +1317,7 @@ bool Client::motionNotifyEvent(xcb_window_t w, int state, int x, int y, int x_ro checkQuickTilingMaximizationZones(x_root, y_root); } } - } + }); return true; } diff --git a/workspace.cpp b/workspace.cpp index e7feadc009..b7b68d8c16 100644 --- a/workspace.cpp +++ b/workspace.cpp @@ -104,6 +104,29 @@ void ColorMapper::update() } } +MouseMotionCompressionTimer::MouseMotionCompressionTimer(QObject *parent) + : QTimer(parent) +{ + setSingleShot(true); + setInterval(0); +} + +MouseMotionCompressionTimer::~MouseMotionCompressionTimer() +{ +} + +void MouseMotionCompressionTimer::cancel() +{ + disconnect(m_connection); +} + +void MouseMotionCompressionTimer::schedule(const std::function< void () > &functor) +{ + cancel(); + m_connection = connect(this, &MouseMotionCompressionTimer::timeout, functor); + start(); +} + Workspace* Workspace::_self = 0; Workspace::Workspace(bool restore) @@ -137,6 +160,7 @@ Workspace::Workspace(bool restore) , set_active_client_recursion(0) , block_stacking_updates(0) , forced_global_mouse_grab(false) + , m_mouseMotionTimer(new MouseMotionCompressionTimer(this)) { // If KWin was already running it saved its configuration after loosing the selection -> Reread QFuture reparseConfigFuture = QtConcurrent::run(options, &Options::reparseConfiguration); diff --git a/workspace.h b/workspace.h index 4d1e7e39b9..fb87e00db7 100644 --- a/workspace.h +++ b/workspace.h @@ -58,6 +58,7 @@ class KillWindow; class ShortcutDialog; class UserActionsMenu; class Compositor; +class MouseMotionCompressionTimer; class Workspace : public QObject, public KDecorationDefines { @@ -72,6 +73,7 @@ public: bool workspaceEvent(xcb_generic_event_t*); bool workspaceEvent(QEvent*); + void scheduleMouseMotionCompression(const std::function &functor); bool hasClient(const Client*); @@ -547,6 +549,9 @@ private: QScopedPointer m_windowKiller; + // compression of mouse motion events + MouseMotionCompressionTimer *m_mouseMotionTimer; + private: friend bool performTransiencyCheck(); friend Workspace *workspace(); @@ -583,6 +588,18 @@ private: xcb_colormap_t m_installed; }; +class MouseMotionCompressionTimer : public QTimer +{ + Q_OBJECT +public: + explicit MouseMotionCompressionTimer(QObject *parent = 0); + virtual ~MouseMotionCompressionTimer(); + void schedule(const std::function &functor); + void cancel(); +private: + QMetaObject::Connection m_connection; +}; + //--------------------------------------------------------- // Unsorted @@ -736,6 +753,12 @@ inline bool Workspace::hasClient(const Client* c) return findClient(ClientMatchPredicate(c)); } +inline +void Workspace::scheduleMouseMotionCompression(const std::function< void () > &functor) +{ + m_mouseMotionTimer->schedule(functor); +} + inline Workspace *workspace() { return Workspace::_self;