Blob Blame History Raw
From 5c9d1063505335ea9734936c5549bc68acf7f5f9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Caol=C3=A1n=20McNamara?= <caolanm@redhat.com>
Date: Fri, 1 Jul 2016 15:42:34 +0100
Subject: [PATCH] Resolves: rhbz#1351224 wayland grab related crashes

only one popup active at a time. Try and find the right path through the
uncanny valley which allows popups to appear, to get all mouse input that
happens to them, forward keyboard input to their parents, dismiss when the
mouse is clicked outside them and not crash if another popup wants to appear
to replace it

gtk3: XEmbed isn't going to happen

Change-Id: I7a7589a159a7fccdc224262bf5f91f8d98f5f619
(cherry picked from commit fe7b8bc9fdb57087ba9daa22cdec77d735eb71cd)

gtk3: if a popup is withdrawn close the popup

we don't always get the click that causes the popup
to be withdrawn when the mouse is clicked outside
the application during a grab

Change-Id: I2dbef23813972ebd75c8899711a2d1309110f968
(cherry picked from commit 1190e7385291fb0e6cd3505e88e589c80b02db00)

Change-Id: If4b39df41ca3dccde1e506d5328b06731a8c80eb
---
 vcl/inc/unx/gtk/gtkframe.hxx  |   9 ++-
 vcl/unx/gtk3/gtk3gtkframe.cxx | 173 +++++++++++++++++++-----------------------
 2 files changed, 85 insertions(+), 97 deletions(-)

diff --git a/vcl/inc/unx/gtk/gtkframe.hxx b/vcl/inc/unx/gtk/gtkframe.hxx
index 4677923..a660567 100644
--- a/vcl/inc/unx/gtk/gtkframe.hxx
+++ b/vcl/inc/unx/gtk/gtkframe.hxx
@@ -269,6 +269,8 @@ class GtkSalFrame : public SalFrame
 #endif
 #else
     static gboolean     signalExpose( GtkWidget*, GdkEventExpose*, gpointer );
+    void askForXEmbedFocus( sal_Int32 nTimecode );
+    void grabKeyboard(bool bGrab);
 #endif
     static gboolean     signalFocus( GtkWidget*, GdkEventFocus*, gpointer );
     static gboolean     signalMap( GtkWidget*, GdkEvent*, gpointer );
@@ -299,7 +301,6 @@ class GtkSalFrame : public SalFrame
     static GdkNativeWindow findTopLevelSystemWindow( GdkNativeWindow aWindow );
 
     static int m_nFloats;
-    static std::vector<GtkWidget*> m_aGrabWidgetsBeforeShowFloat;
 
     bool isFloatGrabWindow() const
     {
@@ -335,7 +336,6 @@ class GtkSalFrame : public SalFrame
 
     void setMinMaxSize();
     void createNewWindow( ::Window aParent, bool bXEmbed, SalX11Screen nXScreen );
-    void askForXEmbedFocus( sal_Int32 nTimecode );
 
     void AllocateFrame();
     void TriggerPaintEvent();
@@ -366,7 +366,6 @@ public:
     // be swallowed
     bool Dispatch( const XEvent* pEvent );
     void grabPointer(bool bGrab, bool bOwnerEvents = false);
-    void grabKeyboard(bool bGrab);
 
     static GtkSalDisplay*  getDisplay();
     static GdkDisplay*     getGdkDisplay();
@@ -424,6 +423,10 @@ public:
     void startDrag(gint nButton, gint nDragOriginX, gint nDragOriginY,
                    GdkDragAction sourceActions, GtkTargetList* pTargetList);
 
+    void WithDrawn();
+
+    static void closePopup();
+
 #endif
     virtual ~GtkSalFrame();
 
diff --git a/vcl/unx/gtk3/gtk3gtkframe.cxx b/vcl/unx/gtk3/gtk3gtkframe.cxx
index 2320356..04af596 100644
--- a/vcl/unx/gtk3/gtk3gtkframe.cxx
+++ b/vcl/unx/gtk3/gtk3gtkframe.cxx
@@ -107,7 +107,6 @@
 using namespace com::sun::star;
 
 int GtkSalFrame::m_nFloats = 0;
-std::vector<GtkWidget*> GtkSalFrame::m_aGrabWidgetsBeforeShowFloat;
 
 #if defined ENABLE_GMENU_INTEGRATION
 static GDBusConnection* pSessionBus = nullptr;
@@ -1292,13 +1291,6 @@ void GtkSalFrame::Init( SystemParentData* pSysData )
     //FIXME: Handling embedded windows, is going to be fun ...
 }
 
-void GtkSalFrame::askForXEmbedFocus( sal_Int32 i_nTimeCode )
-{
-    (void) this; // loplugin:staticmethods
-    (void)i_nTimeCode;
-    //FIXME: no askForXEmbedFocus for gtk3 yet
-}
-
 void GtkSalFrame::SetExtendedFrameStyle( SalExtStyle nStyle )
 {
     if( nStyle != m_nExtStyle && ! isChild() )
@@ -1433,30 +1425,26 @@ void GtkSalFrame::Show( bool bVisible, bool bNoActivate )
             if( ! bNoActivate && (m_nStyle & SalFrameStyleFlags::TOOLWINDOW) )
                 m_bSetFocusOnMap = true;
 
-            gtk_widget_show( m_pWindow );
+            if (isFloatGrabWindow() && !getDisplay()->GetCaptureFrame() && m_nFloats == 0)
+            {
+                m_pParent->grabPointer(true, true);
+                gtk_grab_add(m_pParent->getMouseEventWidget());
+            }
+
+            gtk_widget_show(m_pWindow);
 
             if( isFloatGrabWindow() )
             {
                 m_nFloats++;
                 if( ! getDisplay()->GetCaptureFrame() && m_nFloats == 1 )
                 {
-                    GtkWindowGroup *pWindowGroup = gtk_window_get_group(GTK_WINDOW(m_pWindow));
-                    GtkWidget* pGrabWidgetBeforeShowFloat;
-                    while ((pGrabWidgetBeforeShowFloat = gtk_window_group_get_current_grab(pWindowGroup)))
-                    {
-                        m_aGrabWidgetsBeforeShowFloat.push_back(pGrabWidgetBeforeShowFloat);
-                        gtk_grab_remove(pGrabWidgetBeforeShowFloat);
-                    }
                     grabPointer(true, true);
-                    GtkSalFrame *pKeyboardFrame = m_pParent ? m_pParent : this;
-                    pKeyboardFrame->grabKeyboard(true);
+                    gtk_grab_add(getMouseEventWidget());
                 }
                 // #i44068# reset parent's IM context
                 if( m_pParent )
                     m_pParent->EndExtTextInput(EndExtTextInputFlags::NONE);
             }
-            if( m_bWindowIsGtkPlug )
-                askForXEmbedFocus( 0 );
         }
         else
         {
@@ -1465,12 +1453,10 @@ void GtkSalFrame::Show( bool bVisible, bool bNoActivate )
                 m_nFloats--;
                 if( ! getDisplay()->GetCaptureFrame() && m_nFloats == 0)
                 {
-                    GtkSalFrame *pKeyboardFrame = m_pParent ? m_pParent : this;
-                    pKeyboardFrame->grabKeyboard(false);
+                    gtk_grab_remove(getMouseEventWidget());
                     grabPointer(false);
-                    for (auto i = m_aGrabWidgetsBeforeShowFloat.rbegin(); i != m_aGrabWidgetsBeforeShowFloat.rend(); ++i)
-                        gtk_grab_add(*i);
-                    m_aGrabWidgetsBeforeShowFloat.clear();
+                    gtk_grab_remove(m_pParent->getMouseEventWidget());
+                    m_pParent->grabPointer(false);
                 }
             }
             gtk_widget_hide( m_pWindow );
@@ -2070,37 +2056,32 @@ void GtkSalFrame::grabPointer( bool bGrab, bool bOwnerEvents )
     if (!m_pWindow)
         return;
 
-    const int nMask = (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK);
-
-    GdkDeviceManager* pDeviceManager = gdk_display_get_device_manager(getGdkDisplay());
-    GdkDevice* pPointer = gdk_device_manager_get_client_pointer(pDeviceManager);
+#if GTK_CHECK_VERSION(3, 19, 2)
+    GdkSeat* pSeat = gdk_display_get_default_seat(getGdkDisplay());
     if (bGrab)
-        gdk_device_grab(pPointer, widget_get_window(getMouseEventWidget()), GDK_OWNERSHIP_NONE, bOwnerEvents, (GdkEventMask) nMask, m_pCurrentCursor, gtk_get_current_event_time());
+    {
+        gdk_seat_grab(pSeat, widget_get_window(getMouseEventWidget()), GDK_SEAT_CAPABILITY_ALL_POINTING,
+                      bOwnerEvents, NULL, NULL, NULL, NULL);
+    }
     else
-        gdk_device_ungrab(pPointer, gtk_get_current_event_time());
-}
-
-void GtkSalFrame::grabKeyboard( bool bGrab )
-{
-    static const char* pEnv = getenv("SAL_NO_MOUSEGRABS"); // let's not introduce a special var for this
-    if (pEnv && *pEnv)
-        return;
-
-    if (!m_pWindow)
-        return;
+    {
+        gdk_seat_ungrab(pSeat);
+    }
+#else
+    const int nMask = (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK);
 
     GdkDeviceManager* pDeviceManager = gdk_display_get_device_manager(getGdkDisplay());
     GdkDevice* pPointer = gdk_device_manager_get_client_pointer(pDeviceManager);
-    GdkDevice* pKeyboard = gdk_device_get_associated_device(pPointer);
     if (bGrab)
     {
-        gdk_device_grab(pKeyboard, widget_get_window(m_pWindow), GDK_OWNERSHIP_NONE,
-                        true, (GdkEventMask)(GDK_KEY_PRESS | GDK_KEY_RELEASE), nullptr, gtk_get_current_event_time());
+        gdk_device_grab(pPointer, widget_get_window(getMouseEventWidget()), GDK_OWNERSHIP_NONE,
+                        bOwnerEvents, (GdkEventMask) nMask, m_pCurrentCursor, gtk_get_current_event_time());
     }
     else
     {
-        gdk_device_ungrab(pKeyboard, gtk_get_current_event_time());
+        gdk_device_ungrab(pPointer, gtk_get_current_event_time());
     }
+#endif
 }
 
 void GtkSalFrame::CaptureMouse( bool bCapture )
@@ -2562,6 +2543,22 @@ void GtkSalFrame::StartToolKitMoveBy()
                                pEvent->button.time);
 }
 
+void GtkSalFrame::WithDrawn()
+{
+    if (isFloatGrabWindow())
+        closePopup();
+}
+
+void GtkSalFrame::closePopup()
+{
+    if (!m_nFloats)
+        return;
+    ImplSVData* pSVData = ImplGetSVData();
+    if (!pSVData->maWinData.mpFirstFloat)
+        return;
+    pSVData->maWinData.mpFirstFloat->EndPopupMode(FloatWinPopupEndFlags::Cancel | FloatWinPopupEndFlags::CloseAll);
+}
+
 gboolean GtkSalFrame::signalButton( GtkWidget*, GdkEventButton* pEvent, gpointer frame )
 {
     UpdateLastInputEventTime(pEvent->time);
@@ -2593,69 +2590,40 @@ gboolean GtkSalFrame::signalButton( GtkWidget*, GdkEventButton* pEvent, gpointer
     aEvent.mnY      = (long)pEvent->y_root - pThis->maGeometry.nY;
     aEvent.mnCode   = GetMouseModCode( pEvent->state );
 
-    bool bClosePopups = false;
-    if( pEvent->type == GDK_BUTTON_PRESS &&
-        !(pThis->m_nStyle & SalFrameStyleFlags::OWNERDRAWDECORATION)
-        )
-    {
-        if( m_nFloats > 0 )
-        {
-            // close popups if user clicks outside our application
-            gint x, y;
-            bClosePopups = (gdk_display_get_window_at_pointer( GtkSalFrame::getGdkDisplay(), &x, &y ) == nullptr);
-        }
-        /*  #i30306# release implicit pointer grab if no popups are open; else
-         *  Drag cannot grab the pointer and will fail.
-         */
-        if( m_nFloats < 1 || bClosePopups )
-            gdk_display_pointer_ungrab( GtkSalFrame::getGdkDisplay(), GDK_CURRENT_TIME );
-    }
+    vcl::DeletionListener aDel( pThis );
 
-    if( pThis->m_bWindowIsGtkPlug &&
-        pEvent->type == GDK_BUTTON_PRESS &&
-        pEvent->button == 1 )
+    if (pEvent->type == GDK_BUTTON_PRESS && pThis->isFloatGrabWindow())
     {
-        pThis->askForXEmbedFocus( pEvent->time );
+        bool bClosePopups = (pEvent->window != widget_get_window(pThis->getMouseEventWidget()));
+        if (bClosePopups)
+            closePopup();
     }
 
     // --- RTL --- (mirror mouse pos)
     if( AllSettings::GetLayoutRTL() )
         aEvent.mnX = pThis->maGeometry.nWidth-1-aEvent.mnX;
 
-    vcl::DeletionListener aDel( pThis );
-
-    pThis->CallCallback( nEventType, &aEvent );
-
-    if( ! aDel.isDeleted() )
+    if (!aDel.isDeleted())
     {
-        if( bClosePopups )
-        {
-            ImplSVData* pSVData = ImplGetSVData();
-            if ( pSVData->maWinData.mpFirstFloat )
-            {
-                static const char* pEnv = getenv( "SAL_FLOATWIN_NOAPPFOCUSCLOSE" );
-                if ( !(pSVData->maWinData.mpFirstFloat->GetPopupModeFlags() & FloatWinPopupFlags::NoAppFocusClose) && !(pEnv && *pEnv) )
-                    pSVData->maWinData.mpFirstFloat->EndPopupMode( FloatWinPopupEndFlags::Cancel | FloatWinPopupEndFlags::CloseAll );
-            }
-        }
+        pThis->CallCallback( nEventType, &aEvent );
+    }
 
-        if( ! aDel.isDeleted() )
+    if (!aDel.isDeleted())
+    {
+        int frame_x = (int)(pEvent->x_root - pEvent->x);
+        int frame_y = (int)(pEvent->y_root - pEvent->y);
+        if( frame_x != pThis->maGeometry.nX || frame_y != pThis->maGeometry.nY )
         {
-            int frame_x = (int)(pEvent->x_root - pEvent->x);
-            int frame_y = (int)(pEvent->y_root - pEvent->y);
-            if( frame_x != pThis->maGeometry.nX || frame_y != pThis->maGeometry.nY )
-            {
-                pThis->maGeometry.nX = frame_x;
-                pThis->maGeometry.nY = frame_y;
-                pThis->CallCallback( SalEvent::Move, nullptr );
-            }
+            pThis->maGeometry.nX = frame_x;
+            pThis->maGeometry.nY = frame_y;
+            pThis->CallCallback( SalEvent::Move, nullptr );
         }
     }
 
     return true;
 }
 
-gboolean GtkSalFrame::signalScroll( GtkWidget*, GdkEventScroll* pEvent, gpointer frame )
+gboolean GtkSalFrame::signalScroll(GtkWidget*, GdkEventScroll* pEvent, gpointer frame)
 {
     UpdateLastInputEventTime(pEvent->time);
 
@@ -2757,6 +2725,13 @@ gboolean GtkSalFrame::signalMotion( GtkWidget*, GdkEventMotion* pEvent, gpointer
 
     GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame);
 
+    //If a menu, e.g. font name dropdown, is open, then under wayland moving the
+    //mouse in the top left corner of the toplevel window in a
+    //0,0,float-width,float-height area generates motion events which are
+    //delivered to the dropdown
+    if (pThis->isFloatGrabWindow() && pEvent->window != widget_get_window(pThis->getMouseEventWidget()))
+        return true;
+
     SalMouseEvent aEvent;
     aEvent.mnTime   = pEvent->time;
     aEvent.mnX      = (long)pEvent->x_root - pThis->maGeometry.nX;
@@ -2972,12 +2947,15 @@ gboolean GtkSalFrame::signalUnmap( GtkWidget*, GdkEvent*, gpointer frame )
     return false;
 }
 
-gboolean GtkSalFrame::signalKey( GtkWidget*, GdkEventKey* pEvent, gpointer frame )
+gboolean GtkSalFrame::signalKey(GtkWidget* pWidget, GdkEventKey* pEvent, gpointer frame)
 {
     UpdateLastInputEventTime(pEvent->time);
 
     GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame);
 
+    if (pThis->isFloatGrabWindow())
+        return signalKey(pWidget, pEvent, pThis->m_pParent);
+
     vcl::DeletionListener aDel( pThis );
 
     if( pThis->m_pIMHandler )
@@ -3132,11 +3110,18 @@ gboolean GtkSalFrame::signalWindowState( GtkWidget*, GdkEvent* pEvent, gpointer
         pThis->TriggerPaintEvent();
     }
 
-    if(   (pEvent->window_state.new_window_state & GDK_WINDOW_STATE_MAXIMIZED) &&
-        ! (pThis->m_nState & GDK_WINDOW_STATE_MAXIMIZED) )
+    if ((pEvent->window_state.new_window_state & GDK_WINDOW_STATE_MAXIMIZED) &&
+        !(pThis->m_nState & GDK_WINDOW_STATE_MAXIMIZED))
     {
         pThis->m_aRestorePosSize = GetPosAndSize(GTK_WINDOW(pThis->m_pWindow));
     }
+
+    if ((pEvent->window_state.new_window_state & GDK_WINDOW_STATE_WITHDRAWN) &&
+        !(pThis->m_nState & GDK_WINDOW_STATE_WITHDRAWN))
+    {
+        pThis->WithDrawn();
+    }
+
     pThis->m_nState = pEvent->window_state.new_window_state;
 
     #if OSL_DEBUG_LEVEL > 1
-- 
2.7.4