VirtualBox

Ignore:
Timestamp:
Aug 12, 2016 6:56:10 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.

File:
1 edited

Legend:

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

    r62493 r63383  
    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 the keyboard is captured
     23 *    but the mouse not, and both when both are.
     24 *  * That the keyboard is captured when the mouse capture notification is
     25 *    displayed.
     26 *  * That keyboard capture works on X11 hosts when windows are dragged with
     27 *    various window managers.
     28 *  * That multiple machine windows do not fight for the focus on X11 hosts
     29 *    (noticeable through strange modifier key and capitals behaviour).
    1630 */
    1731
     
    269283        return;
    270284
     285    /* On X11 we do not grab the keyboard as soon as it is captured, but delay it until the
     286     * first keypress after the capture.  We do this for several reasons.  First, when several
     287     * windows are created they all try to capture the keyboard when they get the focus.  Due to
     288     * the asynchronous nature of X11 the first window may only gets notified after the last is
     289     * created, and there is a dance if they respond to the notifications by grabbing the keyboard
     290     * and trigger new focus changes in the process.  Second, it is sometimes inconvenient to
     291     * grab the keyboard immediately on focus change: some window managers set the focus then
     292     * try to grab the keyboard themselves, and sulk if they fail by refusing to e.g. drag a
     293     * window using its title bar.  This code uses a hack by taking (ulong)~0 as a parameter to
     294     * finalise the capture. */
     295
     296    /* If such view exists: */
     297    if (m_views.contains(uScreenId)
    271298#if defined(VBOX_WS_X11) && QT_VERSION >= 0x050000
    272     /* Due to X11 async nature we may have lost the focus already by the time we get the focus
    273      * notification, so we do a sanity check that we still have it. If we don't have the focus
    274      * and grab the keyboard now that will cause focus change which we want to avoid. This change
    275      * potentially leads to a loop where two windows are continually responding to outdated focus events. */
    276     const xcb_get_input_focus_cookie_t xcbFocusCookie = xcb_get_input_focus(QX11Info::connection());
    277     xcb_get_input_focus_reply_t *pFocusReply = xcb_get_input_focus_reply(QX11Info::connection(), xcbFocusCookie, NULL);
    278     WId actualWinId = 0;
    279     if (pFocusReply)
    280     {
    281         actualWinId = pFocusReply->focus;
    282         free(pFocusReply);
    283     }
    284     else
    285         LogRel(("GUI: UIKeyboardHandler::captureKeyboard: XCB error on acquiring focus information detected!\n"));
    286     if (m_windows.value(uScreenId)->winId() != actualWinId)
    287         return;
    288 
    289     /* Delay capturing the keyboard if the mouse button is held down and the mouse is not
    290      * captured to work around window managers which transfer the focus when the user
    291      * clicks in the title bar and then try to grab the keyboard and sulk if they fail.
    292      * If the click is inside of our views we will do the capture when it is released. */
    293     const xcb_query_pointer_cookie_t xcbPointerCookie = xcb_query_pointer(QX11Info::connection(), QX11Info::appRootWindow());
    294     xcb_query_pointer_reply_t *pPointerReply = xcb_query_pointer_reply(QX11Info::connection(), xcbPointerCookie, NULL);
    295     if (!uisession()->isMouseCaptured() && pPointerReply && (pPointerReply->mask & XCB_KEY_BUT_MASK_BUTTON_1))
    296     {
    297         free(pPointerReply);
    298         return;
    299     }
    300     free(pPointerReply);
    301 #endif /* VBOX_WS_X11 && QT_VERSION >= 0x050000 */
    302 
    303     /* If such view exists: */
    304     if (m_views.contains(uScreenId))
     299        || ((uScreenId == (ulong)~0) && m_views.contains(m_idxDelayedKeyboardCaptureView))
     300#endif
     301        )
    305302    {
    306303#if defined(VBOX_WS_MAC)
     
    371368# else /* QT_VERSION >= 0x050000 */
    372369
     370        if (uScreenId != (ulong)~0)
     371        {
     372            m_idxDelayedKeyboardCaptureView = uScreenId;
     373            return;
     374        }
     375        uScreenId = m_idxDelayedKeyboardCaptureView;
    373376        /* On X11, we are using XCB stuff to grab the keyboard.
    374377         * This stuff is a part of the active keyboard grabbing functionality.
    375          * Active keyboard grabbing causes a problems on certain old window managers - a window cannot
     378         * Active keyboard grabbing causes a problems on many window managers - a window cannot
    376379         * be moved using the mouse. So we additionally grabbing the mouse as well to detect that user
    377380         * is trying to click outside of internal window geometry. */
    378381
    379         /* Grab the mouse button (if mouse is not captured),
    380          * We do not check for failure as we do not currently implement a back-up plan. */
    381         if (!uisession()->isMouseCaptured())
    382             xcb_grab_button_checked(QX11Info::connection(), 0, QX11Info::appRootWindow(),
    383                                     XCB_EVENT_MASK_BUTTON_PRESS, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC,
    384                                     XCB_NONE, XCB_NONE, XCB_BUTTON_INDEX_1, XCB_MOD_MASK_ANY);
     382        /* Grab the mouse button.  We do not check for failure as we do not currently implement a
     383         * back-up plan. */
     384        xcb_grab_button_checked(QX11Info::connection(), 0, QX11Info::appRootWindow(),
     385                                XCB_EVENT_MASK_BUTTON_PRESS, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC,
     386                                XCB_NONE, XCB_NONE, XCB_BUTTON_INDEX_1, XCB_MOD_MASK_ANY);
    385387        /* And grab the keyboard, using XCB directly, as Qt does not report failure. */
    386388        xcb_grab_keyboard_cookie_t xcbGrabCookie = xcb_grab_keyboard(QX11Info::connection(), false, m_views[uScreenId]->winId(),
     
    390392        {
    391393            /* Try again later: */
    392             m_idxDelayedKeyboardCaptureView = uScreenId;
    393394            free(pGrabReply);
    394395            return;
     
    13791380            /* If we were asked to grab the keyboard previously but had to delay it
    13801381             * then try again on every key press and release event until we manage: */
    1381             if (m_idxDelayedKeyboardCaptureView != -1)
    1382                 captureKeyboard(m_idxDelayedKeyboardCaptureView);
     1382            captureKeyboard((ulong)~0);
    13831383
    13841384            /* Cast to XCB key-event: */
     
    14841484            }
    14851485            /* Else if the event happened outside of our view areas then release the keyboard,
    1486              * but set the delayed capture index so that it will be captured again if we still
    1487              * have the focus after the event is handled: */
     1486             * but capture it again (delayed) immediately.  If the event causes us to loose the
     1487             * focus then the delayed capture will not happen: */
    14881488            releaseKeyboard();
    1489             m_idxDelayedKeyboardCaptureView = uScreenId;
     1489            captureKeyboard(uScreenId);
    14901490            /* And re-send the event so that the window which it was meant for actually gets it: */
    14911491            xcb_allow_events_checked(QX11Info::connection(), XCB_ALLOW_REPLAY_POINTER, pButtonEvent->time);
     
    17991799                    if (!isAutoCaptureDisabled() && autoCaptureSetGlobally())
    18001800#endif /* !VBOX_WS_WIN */
    1801 #if defined(VBOX_WS_X11) && QT_VERSION >= 0x050000
    1802                         m_idxDelayedKeyboardCaptureView = uScreenId;
    1803 #else /* !VBOX_WS_X11 || QT_VERSION < 0x050000 */
    18041801                        captureKeyboard(uScreenId);
    1805 #endif /* !VBOX_WS_X11 || QT_VERSION < 0x050000 */
    18061802                    /* Reset the single-time disable capture flag: */
    18071803                    if (isAutoCaptureDisabled())
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