Mouse motion event compression

Using a QTimer to move the resize/move related code to the end of the
event queue. In case there is another motion event in the queue it will
cancel the timer.
This commit is contained in:
Martin Gräßlin 2013-08-06 10:16:11 +02:00
parent 0ac47d9b18
commit 46f57221e4
3 changed files with 59 additions and 40 deletions

View file

@ -181,6 +181,7 @@ bool Workspace::workspaceEvent(xcb_generic_event_t *e)
break; break;
} }
case XCB_MOTION_NOTIFY: { case XCB_MOTION_NOTIFY: {
m_mouseMotionTimer->cancel();
auto *mouseEvent = reinterpret_cast<xcb_motion_notify_event_t*>(e); auto *mouseEvent = reinterpret_cast<xcb_motion_notify_event_t*>(e);
const QPoint rootPos(mouseEvent->root_x, mouseEvent->root_y); const QPoint rootPos(mouseEvent->root_x, mouseEvent->root_y);
#ifdef KWIN_BUILD_TABBOX #ifdef KWIN_BUILD_TABBOX
@ -1237,39 +1238,6 @@ bool Client::buttonReleaseEvent(xcb_window_t w, int /*button*/, int state, int x
return true; 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 // 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) 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; mode = newmode;
updateCursor(); 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; return false;
} }
if (w == moveResizeGrabWindow()) { if (w == moveResizeGrabWindow()) {
x = this->x(); // translate from grab window to local coords x = this->x(); // translate from grab window to local coords
y = this->y(); y = this->y();
} }
#warning Mouse event compression is lost // mouse motion event compression: the event queue might have multiple motion events
if (/*!waitingMotionEvent()*/true) { // in that case we are only interested in the last event to not cause too much overhead
QRect oldGeo = geometry(); // 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); handleMoveResize(x, y, x_root, y_root);
if (!isFullScreen() && isMove() && oldGeo != geometry()) { if (!isFullScreen() && isMove() && oldGeo != geometry()) {
if (quick_tile_mode != QuickTileNone) { 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); checkQuickTilingMaximizationZones(x_root, y_root);
} }
} }
} });
return true; return true;
} }

View file

@ -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::_self = 0;
Workspace::Workspace(bool restore) Workspace::Workspace(bool restore)
@ -137,6 +160,7 @@ Workspace::Workspace(bool restore)
, set_active_client_recursion(0) , set_active_client_recursion(0)
, block_stacking_updates(0) , block_stacking_updates(0)
, forced_global_mouse_grab(false) , forced_global_mouse_grab(false)
, m_mouseMotionTimer(new MouseMotionCompressionTimer(this))
{ {
// If KWin was already running it saved its configuration after loosing the selection -> Reread // If KWin was already running it saved its configuration after loosing the selection -> Reread
QFuture<void> reparseConfigFuture = QtConcurrent::run(options, &Options::reparseConfiguration); QFuture<void> reparseConfigFuture = QtConcurrent::run(options, &Options::reparseConfiguration);

View file

@ -58,6 +58,7 @@ class KillWindow;
class ShortcutDialog; class ShortcutDialog;
class UserActionsMenu; class UserActionsMenu;
class Compositor; class Compositor;
class MouseMotionCompressionTimer;
class Workspace : public QObject, public KDecorationDefines class Workspace : public QObject, public KDecorationDefines
{ {
@ -72,6 +73,7 @@ public:
bool workspaceEvent(xcb_generic_event_t*); bool workspaceEvent(xcb_generic_event_t*);
bool workspaceEvent(QEvent*); bool workspaceEvent(QEvent*);
void scheduleMouseMotionCompression(const std::function<void ()> &functor);
bool hasClient(const Client*); bool hasClient(const Client*);
@ -547,6 +549,9 @@ private:
QScopedPointer<KillWindow> m_windowKiller; QScopedPointer<KillWindow> m_windowKiller;
// compression of mouse motion events
MouseMotionCompressionTimer *m_mouseMotionTimer;
private: private:
friend bool performTransiencyCheck(); friend bool performTransiencyCheck();
friend Workspace *workspace(); friend Workspace *workspace();
@ -583,6 +588,18 @@ private:
xcb_colormap_t m_installed; xcb_colormap_t m_installed;
}; };
class MouseMotionCompressionTimer : public QTimer
{
Q_OBJECT
public:
explicit MouseMotionCompressionTimer(QObject *parent = 0);
virtual ~MouseMotionCompressionTimer();
void schedule(const std::function<void ()> &functor);
void cancel();
private:
QMetaObject::Connection m_connection;
};
//--------------------------------------------------------- //---------------------------------------------------------
// Unsorted // Unsorted
@ -736,6 +753,12 @@ inline bool Workspace::hasClient(const Client* c)
return findClient(ClientMatchPredicate(c)); return findClient(ClientMatchPredicate(c));
} }
inline
void Workspace::scheduleMouseMotionCompression(const std::function< void () > &functor)
{
m_mouseMotionTimer->schedule(functor);
}
inline Workspace *workspace() inline Workspace *workspace()
{ {
return Workspace::_self; return Workspace::_self;