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:
parent
0ac47d9b18
commit
46f57221e4
3 changed files with 59 additions and 40 deletions
52
events.cpp
52
events.cpp
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
23
workspace.h
23
workspace.h
|
@ -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;
|
||||||
|
|
Loading…
Reference in a new issue