VirtualBox

Changeset 63587 in vbox


Ignore:
Timestamp:
Aug 18, 2016 2:44:00 PM (8 years ago)
Author:
vboxsync
Message:

bugref:8151: FE/Qt: improve X11 keyboard capturing: try to fix regressions when mouse and keyboard capturing are combined on X11 hosts. We simplify keyboard capturing noticeably by always delaying grabbing the keyboard until the next keypress occurs. This lets us get rid of a few special cases in the code which were causing problems. Second version.

Location:
trunk/src/VBox/Frontends/VirtualBox/src/runtime
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Frontends/VirtualBox/src/runtime/UIKeyboardHandler.cpp

    r63579 r63587  
    1414 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
    1515 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
     16 */
     17
     18/*
     19 * Things worth testing when changing this code:
     20 * - That automatic keyboard capture works.
     21 * - That the keyboard is captured when the mouse is.
     22 * - That the host key releases the keyboard when
     23 *   the keyboard is captured but the mouse not, and both when both are.
     24 * - That the host key captures both keyboard and mouse.
     25 * - That the keyboard is captured when the mouse capture notification is
     26 *   displayed.
     27 * - That keyboard capture works on X11 hosts when windows are dragged with
     28 *   various window managers.
     29 * - That multiple machine windows do not fight for the focus on X11 hosts
     30 *   (noticeable through strange modifier key and capitals behaviour).
    1631 */
    1732
     
    234249    /* Do NOT capture the keyboard if it is already captured: */
    235250    if (m_fIsKeyboardCaptured)
     251    {
     252        /* Make sure the right screen had captured the keyboard: */
     253        Assert((int)uScreenId == m_iKeyboardCaptureViewIndex);
    236254        return;
    237 
    238 #if defined(VBOX_WS_X11) && QT_VERSION >= 0x050000
    239     /* Due to X11 async nature we may have lost the focus already by the time we get the focus
    240      * notification, so we do a sanity check that we still have it. If we don't have the focus
    241      * and grab the keyboard now that will cause focus change which we want to avoid. This change
    242      * potentially leads to a loop where two windows are continually responding to outdated focus events. */
    243     const xcb_get_input_focus_cookie_t xcbFocusCookie = xcb_get_input_focus(QX11Info::connection());
    244     xcb_get_input_focus_reply_t *pFocusReply = xcb_get_input_focus_reply(QX11Info::connection(), xcbFocusCookie, NULL);
    245     WId actualWinId = 0;
    246     if (pFocusReply)
    247     {
    248         actualWinId = pFocusReply->focus;
    249         free(pFocusReply);
    250     }
    251     else
    252         LogRel(("GUI: UIKeyboardHandler::captureKeyboard: XCB error on acquiring focus information detected!\n"));
    253     if (m_windows.value(uScreenId)->winId() != actualWinId)
    254         return;
    255 
    256     /* Delay capturing the keyboard if the mouse button is held down and the mouse is not
    257      * captured to work around window managers which transfer the focus when the user
    258      * clicks in the title bar and then try to grab the keyboard and sulk if they fail.
    259      * If the click is inside of our views we will do the capture when it is released. */
    260     const xcb_query_pointer_cookie_t xcbPointerCookie = xcb_query_pointer(QX11Info::connection(), QX11Info::appRootWindow());
    261     xcb_query_pointer_reply_t *pPointerReply = xcb_query_pointer_reply(QX11Info::connection(), xcbPointerCookie, NULL);
    262     if (!uisession()->isMouseCaptured() && pPointerReply && (pPointerReply->mask & XCB_KEY_BUT_MASK_BUTTON_1))
    263     {
    264         free(pPointerReply);
    265         return;
    266     }
    267     free(pPointerReply);
    268 #endif /* VBOX_WS_X11 && QT_VERSION >= 0x050000 */
     255    }
    269256
    270257    /* If such view exists: */
     
    338325# else /* QT_VERSION >= 0x050000 */
    339326
     327        /* On X11, we do not grab the keyboard as soon as it is captured, but delay it
     328         * until the first keypress after the capture. We do this for several reasons:
     329         * - First, when several windows are created they all try to capture the keyboard when
     330         *   they get the focus. Due to the asynchronous nature of X11 the first window may only
     331         *   gets notified after the last is created, and there is a dance if they respond to
     332         *   the notifications by grabbing the keyboard and trigger new focus changes in the process.
     333         * - Second, grabbing the keyboard immediately on focus change upsets some window managers,
     334         *   they give us the focus then try to grab the keyboard themselves, and sulk if they fail
     335         *   by refusing to e.g. drag a window using its title bar. */
     336
     337# endif /* QT_VERSION >= 0x050000 */
     338#else
     339
     340        /* On other platforms we are just praying Qt method to work: */
     341        m_views[uScreenId]->grabKeyboard();
     342
     343#endif
     344
     345        /* Remember which screen wish to capture the keyboard: */
     346        m_iKeyboardCaptureViewIndex = uScreenId;
     347
     348#if !defined(VBOX_WS_X11) || QT_VERSION < 0x050000
     349        /* Finalising keyboard capture: */
     350        finaliseCaptureKeyboard();
     351#endif
     352    }
     353}
     354
     355void UIKeyboardHandler::finaliseCaptureKeyboard()
     356{
     357    /* Do NOT capture the keyboard if it is already captured: */
     358    if (m_fIsKeyboardCaptured)
     359        return;
     360
     361    /* Make sure capture was really requested: */
     362    if (m_iKeyboardCaptureViewIndex == -1)
     363        return;
     364
     365    /* If such view exists: */
     366    if (m_views.contains(m_iKeyboardCaptureViewIndex))
     367    {
     368#if defined(VBOX_WS_X11) && QT_VERSION >= 0x050000
     369
    340370        /* On X11, we are using XCB stuff to grab the keyboard.
    341371         * This stuff is a part of the active keyboard grabbing functionality.
    342          * Active keyboard grabbing causes a problems on certain old window managers - a window cannot
    343          * be moved using the mouse. So we additionally grabbing the mouse as well to detect that user
    344          * is trying to click outside of internal window geometry. */
    345 
    346         /* Grab the mouse button (if mouse is not captured),
    347          * We do not check for failure as we do not currently implement a back-up plan. */
    348         if (!uisession()->isMouseCaptured())
    349             xcb_grab_button_checked(QX11Info::connection(), 0, QX11Info::appRootWindow(),
    350                                     XCB_EVENT_MASK_BUTTON_PRESS, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC,
    351                                     XCB_NONE, XCB_NONE, XCB_BUTTON_INDEX_1, XCB_MOD_MASK_ANY);
     372         * Active keyboard grabbing causes a problems on many window managers - a window cannot
     373         * be moved using the mouse. So we additionally grabbing the mouse as well to detect that
     374         * user is trying to click outside of internal window geometry. */
     375
     376        /* Grab the mouse button. We do not check for failure as we do not currently implement a back-up plan. */
     377        xcb_grab_button_checked(QX11Info::connection(), 0, QX11Info::appRootWindow(),
     378                                XCB_EVENT_MASK_BUTTON_PRESS, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC,
     379                                XCB_NONE, XCB_NONE, XCB_BUTTON_INDEX_1, XCB_MOD_MASK_ANY);
    352380        /* And grab the keyboard, using XCB directly, as Qt does not report failure. */
    353         xcb_grab_keyboard_cookie_t xcbGrabCookie = xcb_grab_keyboard(QX11Info::connection(), false, m_views[uScreenId]->winId(),
     381        xcb_grab_keyboard_cookie_t xcbGrabCookie = xcb_grab_keyboard(QX11Info::connection(), false, m_views[m_iKeyboardCaptureViewIndex]->winId(),
    354382                                                                     XCB_TIME_CURRENT_TIME, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC);
    355383        xcb_grab_keyboard_reply_t *pGrabReply = xcb_grab_keyboard_reply(QX11Info::connection(), xcbGrabCookie, NULL);
     
    357385        {
    358386            /* Try again later: */
    359             m_idxDelayedKeyboardCaptureView = uScreenId;
    360387            free(pGrabReply);
    361388            return;
     
    363390        free(pGrabReply);
    364391
    365         /* Successfully captured, stop delayed capture attempts: */
    366         m_idxDelayedKeyboardCaptureView = -1;
    367 
    368 # endif /* QT_VERSION >= 0x050000 */
    369 #else
    370 
    371         /* On other platforms we are just praying Qt method to work: */
    372         m_views[uScreenId]->grabKeyboard();
    373 
    374 #endif
    375 
    376         /* Remember which screen had captured keyboard: */
    377         m_iKeyboardCaptureViewIndex = uScreenId;
     392#endif /* VBOX_WS_X11 && QT_VERSION >= 0x050000 */
    378393
    379394        /* Store new keyboard-captured state value: */
     
    387402void UIKeyboardHandler::releaseKeyboard()
    388403{
    389 #if defined(VBOX_WS_X11) && QT_VERSION >= 0x050000
    390     /* If we haven't captured the keyboard by now it is too late: */
    391     m_idxDelayedKeyboardCaptureView = -1;
    392 #endif /* VBOX_WS_X11 && QT_VERSION >= 0x050000 */
    393 
    394404    /* Do NOT release the keyboard if it is already released: */
    395405    if (!m_fIsKeyboardCaptured)
     406    {
     407        /* If a delayed capture is scheduled then cancel it: */
     408        m_iKeyboardCaptureViewIndex = -1;
    396409        return;
     410    }
    397411
    398412    /* If such view exists: */
     
    454468        /* On X11, we are using XCB stuff to grab the keyboard.
    455469         * This stuff is a part of the active keyboard grabbing functionality.
    456          * Active keyboard grabbing causes a problems on certain old window managers - a window cannot
    457          * be moved using the mouse. So we finally releasing additionally grabbed mouse as well to
    458          * allow further user interactions. */
     470         * Active keyboard grabbing causes a problems on many window managers - a window cannot
     471         * be moved using the mouse. So we finally releasing additionally grabbed mouse as well
     472         * to allow further user interactions. */
    459473
    460474        /* Ungrab using XCB: */
     
    472486#endif
    473487
    474         /* Forget which screen had captured keyboard: */
     488        /* Forget which screen had captured the keyboard: */
    475489        m_iKeyboardCaptureViewIndex = -1;
    476490
     
    13441358            /* If we were asked to grab the keyboard previously but had to delay it
    13451359             * then try again on every key press and release event until we manage: */
    1346             if (m_idxDelayedKeyboardCaptureView != -1)
    1347                 captureKeyboard(m_idxDelayedKeyboardCaptureView);
     1360            finaliseCaptureKeyboard();
    13481361
    13491362            /* Cast to XCB key-event: */
     
    14491462            }
    14501463            /* Else if the event happened outside of our view areas then release the keyboard,
    1451              * but set the delayed capture index so that it will be captured again if we still
    1452              * have the focus after the event is handled: */
     1464             * but capture it again (delayed) immediately. If the event causes us to loose the
     1465             * focus then the delayed capture will not happen: */
    14531466            releaseKeyboard();
    1454             m_idxDelayedKeyboardCaptureView = uScreenId;
     1467            captureKeyboard(uScreenId);
    14551468            /* And re-send the event so that the window which it was meant for actually gets it: */
    14561469            xcb_allow_events_checked(QX11Info::connection(), XCB_ALLOW_REPLAY_POINTER, pButtonEvent->time);
     
    15321545    , m_pMachineLogic(pMachineLogic)
    15331546    , m_iKeyboardCaptureViewIndex(-1)
    1534 #if defined(VBOX_WS_X11) && QT_VERSION >= 0x050000
    1535     , m_idxDelayedKeyboardCaptureView(-1)
    1536 #endif /* VBOX_WS_X11 && QT_VERSION >= 0x050000 */
    15371547    , m_globalSettings(vboxGlobal().settings())
    15381548    , m_fIsKeyboardCaptured(false)
     
    17291739                    if (!isAutoCaptureDisabled() && autoCaptureSetGlobally())
    17301740#endif /* !VBOX_WS_WIN */
    1731 #if defined(VBOX_WS_X11) && QT_VERSION >= 0x050000
    1732                         m_idxDelayedKeyboardCaptureView = uScreenId;
    1733 #else /* !VBOX_WS_X11 || QT_VERSION < 0x050000 */
    17341741                        captureKeyboard(uScreenId);
    1735 #endif /* !VBOX_WS_X11 || QT_VERSION < 0x050000 */
    17361742                    /* Reset the single-time disable capture flag: */
    17371743                    if (isAutoCaptureDisabled())
     
    20832089                if (ok)
    20842090                {
     2091                    /* Determine whether the mouse can be captured: */
     2092                    bool fCaptureMouse =    !uisession()->isMouseSupportsAbsolute()
     2093                                         || !uisession()->isMouseIntegrated();
     2094
    20852095                    if (m_fIsKeyboardCaptured)
     2096                    {
    20862097                        releaseKeyboard();
     2098                        if (fCaptureMouse)
     2099                            machineLogic()->mouseHandler()->releaseMouse();
     2100                    }
    20872101                    else
     2102                    {
    20882103                        captureKeyboard(uScreenId);
    2089                     if (!uisession()->isMouseSupportsAbsolute() || !uisession()->isMouseIntegrated())
    2090                     {
     2104                        finaliseCaptureKeyboard();
    20912105#ifdef VBOX_WS_X11
    20922106                        /* Make sure that pending FocusOut events from the
    20932107                         * previous message box are handled, otherwise the
    20942108                         * mouse is immediately ungrabbed: */
     2109                        /// @todo Is that really needed?
    20952110                        qApp->processEvents();
    20962111#endif /* VBOX_WS_X11 */
    2097                         if (m_fIsKeyboardCaptured)
     2112                        if (fCaptureMouse)
    20982113                        {
    20992114                            const MouseCapturePolicy mcp = gEDataManager->mouseCapturePolicy(vboxGlobal().managedVMUuid());
     
    21012116                                machineLogic()->mouseHandler()->captureMouse(uScreenId);
    21022117                        }
    2103                         else
    2104                             machineLogic()->mouseHandler()->releaseMouse();
    21052118                    }
    21062119                }
  • trunk/src/VBox/Frontends/VirtualBox/src/runtime/UIKeyboardHandler.h

    r63578 r63587  
    8080# endif /* QT_VERSION < 0x050000 */
    8181#endif /* VBOX_WS_X11 */
     82
     83    /** Captures the keyboard for @a uScreenId. */
    8284    void captureKeyboard(ulong uScreenId);
     85    /** Finalises keyboard capturing. */
     86    void finaliseCaptureKeyboard();
     87    /** Releases the keyboard. */
    8388    void releaseKeyboard();
     89
    8490    void releaseAllPressedKeys(bool aReleaseHostKey = true);
    8591
     
    196202    /* Other keyboard variables: */
    197203    int m_iKeyboardCaptureViewIndex;
    198 #if defined(VBOX_WS_X11) && QT_VERSION >= 0x050000
    199     /* Holds the index of the screen to capture keyboard when ready. */
    200     int m_idxDelayedKeyboardCaptureView;
    201 #endif /* VBOX_WS_X11 && QT_VERSION >= 0x050000 */
    202204    const VBoxGlobalSettings &m_globalSettings;
    203205
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette