diff options
author | Luboš Luňák <l.lunak@collabora.com> | 2015-02-04 15:25:03 +0100 |
---|---|---|
committer | Luboš Luňák <l.lunak@collabora.com> | 2015-02-04 15:44:49 +0100 |
commit | fc29d34dc60dda5ddbc8f0444684bd2f4eb3668e (patch) | |
tree | f70e64c59bd1fd7eec7c7af13abbec88394b4d30 | |
parent | 847513d409b146400515d7796d196b8b2a142036 (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.h | 1 | ||||
-rw-r--r-- | vcl/unx/generic/window/salframe.cxx | 88 |
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: |