summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuboš Luňák <l.lunak@collabora.com>2015-02-04 19:39:52 +0100
committerLuboš Luňák <l.lunak@collabora.com>2015-02-04 19:49:08 +0100
commit4fe21d490b073a970364eaaff830d5734975124a (patch)
tree1b2fb54d4c0005d6a7e67eb8e070a3b6a4510f8b
parentcedee021e45ddd0655e9cb791cf706ab4ef042af (diff)
discard excessive (X11) autorepeat keyboard events if they overload LOprivate/llunak/munich_12587
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. (cherry picked from commit fc29d34dc60dda5ddbc8f0444684bd2f4eb3668e) 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 1a58b904b00a..acdc800a8cc8 100644
--- a/vcl/inc/unx/salframe.h
+++ b/vcl/inc/unx/salframe.h
@@ -76,7 +76,6 @@ class VCLPLUG_GEN_PUBLIC X11SalFrame : public SalFrame
X11SalGraphics *pGraphics_; // current frame graphics
X11SalGraphics *pFreeGraphics_; // first free frame graphics
- XLIB_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 fdf4723c5219..0ca58c273c9c 100644
--- a/vcl/unx/generic/window/salframe.cxx
+++ b/vcl/unx/generic/window/salframe.cxx
@@ -717,7 +717,6 @@ X11SalFrame::X11SalFrame( SalFrame *pParent, sal_uLong nSalFrameStyle,
hCursor_ = None;
nCaptured_ = 0;
- nReleaseTime_ = 0;
nKeyCode_ = 0;
nKeyState_ = 0;
nCompose_ = -1;
@@ -3107,6 +3106,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 == XLIB_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;
@@ -3974,24 +4033,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 = (X11SalFrame*)pX11SalFrame;
- return
- pCheck->type == XLIB_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;
@@ -4017,14 +4058,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, (XPointer)this ) )
- XPutBackEvent( pEvent->xkey.display, &aEvent );
- else
- nRet = HandleKeyEvent( &pEvent->xkey );
- }
+ nRet = HandleKeyEvent( &pEvent->xkey );
break;
case ButtonPress: