summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuboš Luňák <l.lunak@collabora.com>2015-02-04 15:25:03 +0100
committerLuboš Luňák <l.lunak@collabora.com>2015-02-04 15:44:49 +0100
commitfc29d34dc60dda5ddbc8f0444684bd2f4eb3668e (patch)
treef70e64c59bd1fd7eec7c7af13abbec88394b4d30
parent847513d409b146400515d7796d196b8b2a142036 (diff)
discard excessive (X11) autorepeat keyboard events if they overload LO
If e.g. a document is slow to redraw, scrolling it by holding down a key such as PageDown can make LO lag behind and continue processing the queued up input events even (long) after the key has been released. Since with autorepeat keyboard events the normal user expectations are to hold it until something happens and the exact number of the events doesn't matter, simply discard excessive autorepeat keyboard events if LO can't keep up with them. Change-Id: I45acdc9aa5033647fb80760991437dddfcb6591c
-rw-r--r--vcl/inc/unx/salframe.h1
-rw-r--r--vcl/unx/generic/window/salframe.cxx88
2 files changed, 61 insertions, 28 deletions
diff --git a/vcl/inc/unx/salframe.h b/vcl/inc/unx/salframe.h
index d7ce0c29d9be..af89ba8fc1f3 100644
--- a/vcl/inc/unx/salframe.h
+++ b/vcl/inc/unx/salframe.h
@@ -77,7 +77,6 @@ class VCLPLUG_GEN_PUBLIC X11SalFrame : public SalFrame, public X11WindowProvider
X11SalGraphics *pGraphics_; // current frame graphics
X11SalGraphics *pFreeGraphics_; // first free frame graphics
- Time nReleaseTime_; // timestamp of last key release
sal_uInt16 nKeyCode_; // last key code
sal_uInt16 nKeyState_; // last key state
int nCompose_; // compose state
diff --git a/vcl/unx/generic/window/salframe.cxx b/vcl/unx/generic/window/salframe.cxx
index 9dee67235dd5..77e70e7e7e84 100644
--- a/vcl/unx/generic/window/salframe.cxx
+++ b/vcl/unx/generic/window/salframe.cxx
@@ -778,7 +778,6 @@ X11SalFrame::X11SalFrame( SalFrame *pParent, sal_uLong nSalFrameStyle,
hCursor_ = None;
nCaptured_ = 0;
- nReleaseTime_ = 0;
nKeyCode_ = 0;
nKeyState_ = 0;
nCompose_ = -1;
@@ -3157,6 +3156,66 @@ bool X11SalFrame::endUnicodeSequence()
long X11SalFrame::HandleKeyEvent( XKeyEvent *pEvent )
{
+ if( pEvent->type == KeyRelease )
+ {
+ // Ignore autorepeat keyrelease events. If there is a series of keypress+keyrelease+keypress events
+ // generated by holding down a key, and if these are from autorepeat (keyrelease and the following keypress
+ // have the same timestamp), drop the autorepeat keyrelease event. Not exactly sure why this is done
+ // (possibly hiding differences between platforms, or just making it more sensible, because technically
+ // the key has not been released at all).
+ bool ignore = false;
+ // Discard queued excessive autorepeat events.
+ // If the user presses and holds down a key, the autorepeating keypress events
+ // may overload LO (e.g. if the key is PageDown and the LO cannot keep up scrolling).
+ // Reduce the load by simply discarding such excessive events (so for a KeyRelease event,
+ // check if it's followed by matching KeyPress+KeyRelease pair(s) and discard those).
+ // This shouldn't have any negative effects - unlike with normal (non-autorepeat
+ // events), the user is unlikely to rely on the exact number of resulting actions
+ // (since autorepeat generates keypress events rather quickly and it's hard to estimate
+ // how many exactly) and the idea should be just keeping the key pressed until something
+ // happens (in which case more events that just lag LO shouldn't make a difference).
+ Display* dpy = pEvent->display;
+ XKeyEvent previousRelease = *pEvent;
+ while( XPending( dpy ))
+ {
+ XEvent nextEvent1;
+ bool discard1 = false;
+ XNextEvent( dpy, &nextEvent1 );
+ if( nextEvent1.type == KeyPress && nextEvent1.xkey.time == previousRelease.time
+ && !nextEvent1.xkey.send_event && nextEvent1.xkey.window == previousRelease.window
+ && nextEvent1.xkey.state == previousRelease.state && nextEvent1.xkey.keycode == previousRelease.keycode )
+ { // This looks like another autorepeat keypress.
+ ignore = true;
+ if( XPending( dpy ))
+ {
+ XEvent nextEvent2;
+ XNextEvent( dpy, &nextEvent2 );
+ if( nextEvent2.type == KeyRelease && nextEvent2.xkey.time <= ( previousRelease.time + 100 )
+ && !nextEvent2.xkey.send_event && nextEvent2.xkey.window == previousRelease.window
+ && nextEvent2.xkey.state == previousRelease.state && nextEvent2.xkey.keycode == previousRelease.keycode )
+ { // And the matching keyrelease -> drop them both.
+ discard1 = true;
+ previousRelease = nextEvent2.xkey;
+ ignore = false; // There either will be another autorepeating keypress that'll lead to discarding
+ // the pEvent keyrelease, it this discarding makes that keyrelease the last one.
+ }
+ else
+ {
+ XPutBackEvent( dpy, &nextEvent2 );
+ break;
+ }
+ }
+ }
+ if( !discard1 )
+ { // Unrelated event, put back and stop compressing.
+ XPutBackEvent( dpy, &nextEvent1 );
+ break;
+ }
+ }
+ if( ignore ) // This autorepeating keyrelease is followed by another keypress.
+ return 0;
+ }
+
KeySym nKeySym;
KeySym nUnmodifiedKeySym;
int nLen = 2048;
@@ -4019,24 +4078,6 @@ long X11SalFrame::HandleClientMessage( XClientMessageEvent *pEvent )
return 0;
}
-extern "C"
-{
-Bool call_checkKeyReleaseForRepeat( Display* pDisplay, XEvent* pCheck, XPointer pX11SalFrame )
-{
- return X11SalFrame::checkKeyReleaseForRepeat( pDisplay, pCheck, pX11SalFrame );
-}
-}
-
-Bool X11SalFrame::checkKeyReleaseForRepeat( Display*, XEvent* pCheck, XPointer pX11SalFrame )
-{
- X11SalFrame* pThis = reinterpret_cast<X11SalFrame*>(pX11SalFrame);
- return
- pCheck->type == KeyPress &&
- pCheck->xkey.state == pThis->nKeyState_ &&
- pCheck->xkey.keycode == pThis->nKeyCode_ &&
- pCheck->xkey.time == pThis->nReleaseTime_ ? True : False;
-}
-
long X11SalFrame::Dispatch( XEvent *pEvent )
{
long nRet = 0;
@@ -4062,14 +4103,7 @@ long X11SalFrame::Dispatch( XEvent *pEvent )
case KeyRelease:
if( -1 == nCompose_ )
- {
- nReleaseTime_ = pEvent->xkey.time;
- XEvent aEvent;
- if( XCheckIfEvent( pEvent->xkey.display, &aEvent, call_checkKeyReleaseForRepeat, reinterpret_cast<XPointer>(this) ) )
- XPutBackEvent( pEvent->xkey.display, &aEvent );
- else
- nRet = HandleKeyEvent( &pEvent->xkey );
- }
+ nRet = HandleKeyEvent( &pEvent->xkey );
break;
case ButtonPress: