Add support for mouse and keyboard grab.
authorLaszlo Agocs <laszlo.p.agocs@nokia.com>
Fri, 3 Jun 2011 09:30:23 +0000 (11:30 +0200)
committerLaszlo Agocs <laszlo.p.agocs@nokia.com>
Fri, 3 Jun 2011 09:59:15 +0000 (11:59 +0200)
Reviewed-by: Samuel Rødal
src/gui/kernel/qplatformwindow_qpa.cpp
src/gui/kernel/qplatformwindow_qpa.h
src/gui/kernel/qwindow.cpp
src/gui/kernel/qwindow.h
src/plugins/platforms/xcb/qxcbwindow.cpp
src/plugins/platforms/xcb/qxcbwindow.h
src/widgets/kernel/qapplication_qpa.cpp
src/widgets/kernel/qwidget_p.h
src/widgets/kernel/qwidget_qpa.cpp
src/widgets/kernel/qwidgetwindow_qpa.cpp

index 7f07d0e..fcf375f 100644 (file)
@@ -212,6 +212,20 @@ QPlatformGLContext *QPlatformWindow::glContext() const
     return 0;
 }
 
+bool QPlatformWindow::setKeyboardGrabEnabled(bool grab)
+{
+    Q_UNUSED(grab);
+    qWarning("This plugin does not support grabbing the keyboard");
+    return false;
+}
+
+bool QPlatformWindow::setMouseGrabEnabled(bool grab)
+{
+    Q_UNUSED(grab);
+    qWarning("This plugin does not support grabbing the mouse");
+    return false;
+}
+
 /*!
     \class QPlatformWindow
     \since 4.8
index 2235d0f..7991e5e 100644 (file)
@@ -88,6 +88,10 @@ public:
     virtual void requestActivateWindow();
 
     virtual QPlatformGLContext *glContext() const;
+
+    virtual bool setKeyboardGrabEnabled(bool grab);
+    virtual bool setMouseGrabEnabled(bool grab);
+
 protected:
     QScopedPointer<QPlatformWindowPrivate> d_ptr;
 private:
index 1b65c4e..fca8aac 100644 (file)
@@ -427,6 +427,22 @@ QWindowSurface *QWindow::surface() const
     return d->surface;
 }
 
+bool QWindow::setKeyboardGrabEnabled(bool grab)
+{
+    Q_D(QWindow);
+    if (d->platformWindow)
+        return d->platformWindow->setKeyboardGrabEnabled(grab);
+    return false;
+}
+
+bool QWindow::setMouseGrabEnabled(bool grab)
+{
+    Q_D(QWindow);
+    if (d->platformWindow)
+        return d->platformWindow->setMouseGrabEnabled(grab);
+    return false;
+}
+
 void QWindow::showMinimized()
 {
     qDebug() << "unimplemented:" << __FILE__ << __LINE__;
index 5b002a4..d1c4848 100644 (file)
@@ -144,6 +144,9 @@ public:
     QPlatformWindow *handle() const;
     QWindowSurface *surface() const;
 
+    bool setKeyboardGrabEnabled(bool grab);
+    bool setMouseGrabEnabled(bool grab);
+
 public Q_SLOTS:
     inline void show() { setVisible(true); }
     inline void hide() { setVisible(false); }
index fc888b8..8182247 100644 (file)
@@ -1094,3 +1094,40 @@ void QXcbWindow::updateSyncRequestCounter()
         m_syncValue.hi = 0;
     }
 }
+
+bool QXcbWindow::setKeyboardGrabEnabled(bool grab)
+{
+    if (!grab) {
+        xcb_ungrab_keyboard(xcb_connection(), XCB_TIME_CURRENT_TIME);
+        return true;
+    }
+    xcb_grab_keyboard_cookie_t cookie = xcb_grab_keyboard(xcb_connection(), false,
+                                                          m_window, XCB_TIME_CURRENT_TIME,
+                                                          GrabModeAsync, GrabModeAsync);
+    xcb_generic_error_t *err;
+    xcb_grab_keyboard_reply_t *reply = xcb_grab_keyboard_reply(xcb_connection(), cookie, &err);
+    bool result = !(err || !reply || reply->status != XCB_GRAB_STATUS_SUCCESS);
+    free(reply);
+    free(err);
+    return result;
+}
+
+bool QXcbWindow::setMouseGrabEnabled(bool grab)
+{
+    if (!grab) {
+        xcb_ungrab_pointer(xcb_connection(), XCB_TIME_CURRENT_TIME);
+        return true;
+    }
+    xcb_grab_pointer_cookie_t cookie = xcb_grab_pointer(xcb_connection(), false, m_window,
+                                                        (ButtonPressMask | ButtonReleaseMask | ButtonMotionMask
+                                                         | EnterWindowMask | LeaveWindowMask | PointerMotionMask),
+                                                        GrabModeAsync, GrabModeAsync,
+                                                        XCB_WINDOW_NONE, XCB_CURSOR_NONE,
+                                                        XCB_TIME_CURRENT_TIME);
+    xcb_generic_error_t *err;
+    xcb_grab_pointer_reply_t *reply = xcb_grab_pointer_reply(xcb_connection(), cookie, &err);
+    bool result = !(err || !reply || reply->status != XCB_GRAB_STATUS_SUCCESS);
+    free(reply);
+    free(err);
+    return result;
+}
index 40d21f4..57794fd 100644 (file)
@@ -76,6 +76,9 @@ public:
 
     QPlatformGLContext *glContext() const;
 
+    bool setKeyboardGrabEnabled(bool grab);
+    bool setMouseGrabEnabled(bool grab);
+
     xcb_window_t xcb_window() const { return m_window; }
     uint depth() const { return m_depth; }
     QImage::Format format() const { return m_format; }
index ec67452..65aca42 100644 (file)
@@ -71,8 +71,13 @@ QT_BEGIN_NAMESPACE
 
 static QString appName;
 static QString appFont;
+static bool popupGrabOk;
 extern bool app_do_modal;
 extern QWidgetList *qt_modal_stack;
+extern QWidget *qt_button_down;
+extern QWidget *qt_popup_down;
+extern bool qt_replay_popup_mouse_event;
+int openPopupCount = 0;
 
 QString QApplicationPrivate::appName() const
 {
@@ -160,6 +165,35 @@ void QApplicationPrivate::notifyActiveWindowChange(QWindow *previous)
     q->setActiveWindow(tlw);
 }
 
+static void ungrabKeyboardForPopup(QWidget *popup)
+{
+    if (QWidget::keyboardGrabber())
+        qt_widget_private(QWidget::keyboardGrabber())->stealKeyboardGrab(true);
+    else
+        qt_widget_private(popup)->stealKeyboardGrab(false);
+}
+
+static void ungrabMouseForPopup(QWidget *popup)
+{
+    if (QWidget::mouseGrabber())
+        qt_widget_private(QWidget::mouseGrabber())->stealMouseGrab(true);
+    else
+        qt_widget_private(popup)->stealMouseGrab(false);
+}
+
+static void grabForPopup(QWidget *popup)
+{
+    Q_ASSERT(popup->testAttribute(Qt::WA_WState_Created));
+    popupGrabOk = qt_widget_private(popup)->stealKeyboardGrab(true);
+    if (popupGrabOk) {
+        popupGrabOk = qt_widget_private(popup)->stealMouseGrab(true);
+        if (!popupGrabOk) {
+            // transfer grab back to the keyboard grabber if any
+            ungrabKeyboardForPopup(popup);
+        }
+    }
+}
+
 void QApplicationPrivate::closePopup(QWidget *popup)
 {
     Q_Q(QApplication);
@@ -167,23 +201,34 @@ void QApplicationPrivate::closePopup(QWidget *popup)
         return;
     popupWidgets->removeAll(popup);
 
-//###
-//     if (popup == qt_popup_down) {
-//         qt_button_down = 0;
-//         qt_popup_down = 0;
-//     }
+     if (popup == qt_popup_down) {
+         qt_button_down = 0;
+         qt_popup_down = 0;
+     }
 
-    if (QApplicationPrivate::popupWidgets->count() == 0) {                // this was the last popup
+    if (QApplicationPrivate::popupWidgets->count() == 0) { // this was the last popup
         delete QApplicationPrivate::popupWidgets;
         QApplicationPrivate::popupWidgets = 0;
 
-        //### replay mouse event?
-
-        //### transfer/release mouse grab
+        if (popupGrabOk) {
+            popupGrabOk = false;
+
+            if (popup->geometry().contains(QPoint(QGuiApplicationPrivate::mousePressX,
+                                                  QGuiApplicationPrivate::mousePressY))
+                || popup->testAttribute(Qt::WA_NoMouseReplay)) {
+                // mouse release event or inside
+                qt_replay_popup_mouse_event = false;
+            } else { // mouse press event
+                QGuiApplicationPrivate::mousePressTime -= 10000; // avoid double click
+                qt_replay_popup_mouse_event = true;
+            }
 
-        //### transfer/release keyboard grab
+            // transfer grab back to mouse grabber if any, otherwise release the grab
+            ungrabMouseForPopup(popup);
 
-        //give back focus
+            // transfer grab back to keyboard grabber if any, otherwise release the grab
+            ungrabKeyboardForPopup(popup);
+        }
 
         if (active_window) {
             if (QWidget *fw = active_window->focusWidget()) {
@@ -198,31 +243,25 @@ void QApplicationPrivate::closePopup(QWidget *popup)
 
     } else {
         // A popup was closed, so the previous popup gets the focus.
-
         QWidget* aw = QApplicationPrivate::popupWidgets->last();
         if (QWidget *fw = aw->focusWidget())
             fw->setFocus(Qt::PopupFocusReason);
 
-        //### regrab the keyboard and mouse in case 'popup' lost the grab
-
-
+        if (QApplicationPrivate::popupWidgets->count() == 1) // grab mouse/keyboard
+            grabForPopup(aw);
     }
 
 }
 
-int openPopupCount = 0;
 void QApplicationPrivate::openPopup(QWidget *popup)
 {
     openPopupCount++;
-    if (!popupWidgets) {                        // create list
+    if (!popupWidgets) // create list
         popupWidgets = new QWidgetList;
+    popupWidgets->append(popup); // add to end of list
 
-        /* only grab if you are the first/parent popup */
-        //####   ->grabMouse(popup,true);
-        //####   ->grabKeyboard(popup,true);
-        //### popupGrabOk = true;
-    }
-    popupWidgets->append(popup);                // add to end of list
+    if (QApplicationPrivate::popupWidgets->count() == 1) // grab mouse/keyboard
+        grabForPopup(popup);
 
     // popups are not focus-handled by the window system (the first
     // popup grabbed the keyboard), so we have to do that manually: A
index 24047cb..d39e318 100644 (file)
@@ -901,6 +901,8 @@ public:
 #elif defined(Q_WS_QPA) // <--------------------------------------------------------- QPA
     void setMaxWindowState_helper();
     void setFullScreenSize_helper();
+    bool stealKeyboardGrab(bool grab);
+    bool stealMouseGrab(bool grab);
 #ifndef QT_NO_CURSOR
     void updateCursor() const;
 #endif
index b8a7cb5..8764f63 100644 (file)
@@ -338,8 +338,8 @@ void QWidget::grabMouse()
     if (qt_mouseGrb)
         qt_mouseGrb->releaseMouse();
 
-    // XXX
-    //qwsDisplay()->grabMouse(this,true);
+    if (windowHandle())
+        windowHandle()->setMouseGrabEnabled(true);
 
     qt_mouseGrb = this;
     qt_pressGrab = 0;
@@ -353,19 +353,27 @@ void QWidget::grabMouse(const QCursor &cursor)
     if (qt_mouseGrb)
         qt_mouseGrb->releaseMouse();
 
-    // XXX
-    //qwsDisplay()->grabMouse(this,true);
-    //qwsDisplay()->selectCursor(this, cursor.handle());
+    if (windowHandle())
+        windowHandle()->setMouseGrabEnabled(true);
+
     qt_mouseGrb = this;
     qt_pressGrab = 0;
 }
 #endif
 
+bool QWidgetPrivate::stealMouseGrab(bool grab)
+{
+    // This is like a combination of grab/releaseMouse() but with error checking
+    // and it has no effect on the result of mouseGrabber().
+    Q_Q(QWidget);
+    return q->windowHandle() ? q->windowHandle()->setMouseGrabEnabled(grab) : false;
+}
+
 void QWidget::releaseMouse()
 {
     if (qt_mouseGrb == this) {
-        // XXX
-        //qwsDisplay()->grabMouse(this,false);
+        if (windowHandle())
+            windowHandle()->setMouseGrabEnabled(false);
         qt_mouseGrb = 0;
     }
 }
@@ -374,16 +382,24 @@ void QWidget::grabKeyboard()
 {
     if (keyboardGrb)
         keyboardGrb->releaseKeyboard();
-    // XXX
-    //qwsDisplay()->grabKeyboard(this, true);
+    if (windowHandle())
+        windowHandle()->setKeyboardGrabEnabled(true);
     keyboardGrb = this;
 }
 
+bool QWidgetPrivate::stealKeyboardGrab(bool grab)
+{
+    // This is like a combination of grab/releaseKeyboard() but with error
+    // checking and it has no effect on the result of keyboardGrabber().
+    Q_Q(QWidget);
+    return q->windowHandle() ? q->windowHandle()->setKeyboardGrabEnabled(grab) : false;
+}
+
 void QWidget::releaseKeyboard()
 {
     if (keyboardGrb == this) {
-        // XXX
-        //qwsDisplay()->grabKeyboard(this, false);
+        if (windowHandle())
+            windowHandle()->setKeyboardGrabEnabled(false);
         keyboardGrb = 0;
     }
 }
index 38156d3..dbb8112 100644 (file)
@@ -49,9 +49,9 @@ QT_BEGIN_NAMESPACE
 QWidget *qt_button_down = 0; // widget got last button-down
 
 // popup control
-static QWidget *qt_popup_down = 0; // popup that contains the pressed widget
+QWidget *qt_popup_down = 0; // popup that contains the pressed widget
 extern int openPopupCount;
-static bool replayPopupMouseEvent = false;
+bool qt_replay_popup_mouse_event = false;
 extern bool qt_try_modal(QWidget *widget, QEvent::Type type);
 
 QWidgetWindow::QWidgetWindow(QWidget *widget)
@@ -155,7 +155,7 @@ void QWidgetWindow::handleMouseEvent(QMouseEvent *event)
 
         if (popup->isEnabled()) {
             // deliver event
-            replayPopupMouseEvent = false;
+            qt_replay_popup_mouse_event = false;
             QWidget *receiver = popup;
             QPoint widgetPos = mapped;
             if (qt_button_down)
@@ -181,10 +181,10 @@ void QWidgetWindow::handleMouseEvent(QMouseEvent *event)
         }
 
         if (qApp->activePopupWidget() != activePopupWidget
-            && replayPopupMouseEvent) {
+            && qt_replay_popup_mouse_event) {
             if (m_widget->windowType() != Qt::Popup)
                 qt_button_down = 0;
-            replayPopupMouseEvent = false;
+            qt_replay_popup_mouse_event = false;
         } else if (event->type() == QEvent::MouseButtonPress
                    && event->button() == Qt::RightButton
                    && (openPopupCount == oldOpenPopupCount)) {