include/sal/log-areas.dox                   |    2 
 solenv/clang-format/excludelist             |    2 
 vcl/Library_vcl.mk                          |    2 
 vcl/inc/qt5/QtFrame.hxx                     |    2 
 vcl/inc/strings.hrc                         |    3 
 vcl/inc/unx/gtk/gtkframe.hxx                |    9 +
 vcl/inc/unx/salframe.h                      |    2 
 vcl/inc/unx/sessioninhibitor.hxx            |    3 
 vcl/unx/generic/window/sessioninhibitor.cxx |   19 +--
 vcl/unx/gtk3/gtkframe.cxx                   |  174 +++++++++++++++++++++++++++-
 10 files changed, 197 insertions(+), 21 deletions(-)

New commits:
commit c04d0570b6c8c78e35c2c9b58bf37bb1218021f5
Author:     Caolán McNamara <caol...@redhat.com>
AuthorDate: Tue May 9 08:46:09 2023 +0100
Commit:     Caolán McNamara <caol...@redhat.com>
CommitDate: Tue May 9 20:42:49 2023 +0200

    Resolves: tdf#142176 under GNOME inhibit logout if there are modified docs
    
    and if we are forced to quit anyway, unset modifications on documents and
    terminate.
    
    Change-Id: If4a3aed48a4621950e2d630fdfef34b28ba1b089
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/151575
    Tested-by: Jenkins
    Reviewed-by: Caolán McNamara <caol...@redhat.com>

diff --git a/include/sal/log-areas.dox b/include/sal/log-areas.dox
index 15bcf5a03573..51b005e1bb51 100644
--- a/include/sal/log-areas.dox
+++ b/include/sal/log-areas.dox
@@ -506,7 +506,7 @@ certain functionality.
 @li @c vcl.quartz
 @li @c vcl.schedule - scheduler / main-loop information
 @li @c vcl.schedule.deinit
-@li @c vcl.screensaverinhibitor
+@li @c vcl.sessioninhibitor
 @li @c vcl.scrollbar - Scroll Bars
 @li @c vcl.se - VCL Session Manager
 @li @c vcl.se.debug
diff --git a/solenv/clang-format/excludelist b/solenv/clang-format/excludelist
index 3453747854d3..a4f04ad0c9e7 100644
--- a/solenv/clang-format/excludelist
+++ b/solenv/clang-format/excludelist
@@ -14978,7 +14978,7 @@ vcl/unx/generic/printer/ppdparser.cxx
 vcl/unx/generic/printer/printerinfomanager.cxx
 vcl/unx/generic/window/salframe.cxx
 vcl/unx/generic/window/salobj.cxx
-vcl/unx/generic/window/screensaverinhibitor.cxx
+vcl/unx/generic/window/sessioninhibitor.cxx
 vcl/unx/gtk3/a11y/atklistener.hxx
 vcl/unx/gtk3/a11y/atkwrapper.hxx
 vcl/unx/gtk3/a11y/atkaction.cxx
diff --git a/vcl/Library_vcl.mk b/vcl/Library_vcl.mk
index 9eca7534218e..a7c1fbc22b2f 100644
--- a/vcl/Library_vcl.mk
+++ b/vcl/Library_vcl.mk
@@ -579,7 +579,7 @@ endif
 
 ifeq ($(USING_X11),TRUE)
 $(eval $(call gb_Library_add_exception_objects,vcl,\
-    vcl/unx/generic/window/screensaverinhibitor \
+    vcl/unx/generic/window/sessioninhibitor \
     vcl/unx/generic/printer/cpdmgr \
 ))
 
diff --git a/vcl/inc/qt5/QtFrame.hxx b/vcl/inc/qt5/QtFrame.hxx
index b927d366765d..09abe5301538 100644
--- a/vcl/inc/qt5/QtFrame.hxx
+++ b/vcl/inc/qt5/QtFrame.hxx
@@ -34,7 +34,7 @@
 #include <QtCore/QObject>
 
 #if CHECK_ANY_QT_USING_X11
-#include <unx/screensaverinhibitor.hxx>
+#include <unx/sessioninhibitor.hxx>
 // any better way to get rid of the X11 / Qt type clashes?
 #undef Bool
 #undef CursorShape
diff --git a/vcl/inc/strings.hrc b/vcl/inc/strings.hrc
index 83beea008e9e..c2e95f20ceac 100644
--- a/vcl/inc/strings.hrc
+++ b/vcl/inc/strings.hrc
@@ -122,6 +122,9 @@
 #define STR_QUIRKY                                  NC_("STR_QUIRKY", "Quirky 
Tests: %1")
 #define STR_FAILED                                  NC_("STR_FAILED", "Failed 
Tests: %1")
 #define STR_SKIPPED                                 NC_("STR_SKIPPED", 
"Skipped Tests: %1")
+
+#define STR_UNSAVED_DOCUMENTS                       
NC_("STR_UNSAVED_DOCUMENTS", "There are unsaved documents")
+
 #endif // INCLUDED_VCL_INC_STRINGS_HRC
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/inc/unx/gtk/gtkframe.hxx b/vcl/inc/unx/gtk/gtkframe.hxx
index 1a83a7fc39d3..890bcdb8a6ea 100644
--- a/vcl/inc/unx/gtk/gtkframe.hxx
+++ b/vcl/inc/unx/gtk/gtkframe.hxx
@@ -32,7 +32,7 @@
 #include <vcl/idle.hxx>
 #include <vcl/sysdata.hxx>
 #include <unx/saltype.h>
-#include <unx/screensaverinhibitor.hxx>
+#include <unx/sessioninhibitor.hxx>
 
 #include <tools/link.hxx>
 
@@ -187,6 +187,9 @@ class GtkSalFrame final : public SalFrame
 #endif
     gulong                          m_nPortalSettingChangedSignalId;
     GDBusProxy*                     m_pSettingsPortal;
+    gulong                          m_nSessionClientSignalId;
+    GDBusProxy*                     m_pSessionManager;
+    GDBusProxy*                     m_pSessionClient;
 #if !GTK_CHECK_VERSION(4, 0, 0)
     GdkWindow*                      m_pForeignParent;
     GdkNativeWindow                 m_aForeignParentWindow;
@@ -425,6 +428,8 @@ class GtkSalFrame final : public SalFrame
 
     void ListenPortalSettings();
 
+    void ListenSessionManager();
+
     void UpdateGeometryFromEvent(int x_root, int y_root, int nEventX, int 
nEventY);
 
 public:
@@ -654,6 +659,8 @@ public:
 
     void SetColorScheme(GVariant* variant);
 
+    void SessionManagerInhibit(bool bStart, ApplicationInhibitFlags eType, 
std::u16string_view sReason, const char* application_id);
+
     void DisallowCycleFocusOut();
     bool IsCycleFocusOutDisallowed() const;
     void AllowCycleFocusOut();
diff --git a/vcl/inc/unx/salframe.h b/vcl/inc/unx/salframe.h
index d8a177d9a867..c14c3254c489 100644
--- a/vcl/inc/unx/salframe.h
+++ b/vcl/inc/unx/salframe.h
@@ -24,7 +24,7 @@
 
 #include <unx/saltype.h>
 #include <unx/saldisp.hxx>
-#include <unx/screensaverinhibitor.hxx>
+#include <unx/sessioninhibitor.hxx>
 #include <salframe.hxx>
 #include <salwtype.hxx>
 #include <salinst.hxx>
diff --git a/vcl/inc/unx/screensaverinhibitor.hxx 
b/vcl/inc/unx/sessioninhibitor.hxx
similarity index 97%
rename from vcl/inc/unx/screensaverinhibitor.hxx
rename to vcl/inc/unx/sessioninhibitor.hxx
index 6cfa3e2fd700..5385cde383cf 100644
--- a/vcl/inc/unx/screensaverinhibitor.hxx
+++ b/vcl/inc/unx/sessioninhibitor.hxx
@@ -31,7 +31,8 @@ class VCL_PLUGIN_PUBLIC SessionManagerInhibitor
 {
 public:
     void inhibit(bool bInhibit, std::u16string_view sReason, 
ApplicationInhibitFlags eType,
-                 unsigned int window_system_id, std::optional<Display*> 
pDisplay);
+                 unsigned int window_system_id, std::optional<Display*> 
pDisplay,
+                 const char* application_id = nullptr);
 
 private:
     // These are all used as guint, however this header may be included
diff --git a/vcl/unx/generic/window/screensaverinhibitor.cxx 
b/vcl/unx/generic/window/sessioninhibitor.cxx
similarity index 94%
rename from vcl/unx/generic/window/screensaverinhibitor.cxx
rename to vcl/unx/generic/window/sessioninhibitor.cxx
index b1cfcb3f4993..300df9ff8031 100644
--- a/vcl/unx/generic/window/screensaverinhibitor.cxx
+++ b/vcl/unx/generic/window/sessioninhibitor.cxx
@@ -12,7 +12,7 @@
 #include <functional>
 
 #include <unx/gensys.h>
-#include <unx/screensaverinhibitor.hxx>
+#include <unx/sessioninhibitor.hxx>
 
 #include <X11/Xlib.h>
 #include <X11/Xatom.h>
@@ -47,9 +47,10 @@
 #include <sal/log.hxx>
 
 void SessionManagerInhibitor::inhibit(bool bInhibit, std::u16string_view 
sReason, ApplicationInhibitFlags eType,
-                                      unsigned int window_system_id, 
std::optional<Display*> pDisplay)
+                                      unsigned int window_system_id, 
std::optional<Display*> pDisplay,
+                                      const char* application_id)
 {
-    const char* appname = SalGenericSystem::getFrameClassName();
+    const char* appname = application_id ? application_id : 
SalGenericSystem::getFrameClassName();
     const OString aReason = OUStringToOString( sReason, RTL_TEXTENCODING_UTF8 
);
 
     if (eType == APPLICATION_INHIBIT_IDLE)
@@ -85,10 +86,10 @@ static void dbusInhibit( bool bInhibit,
     GError          *error = nullptr;
     GDBusConnection *session_connection = g_bus_get_sync( G_BUS_TYPE_SESSION, 
nullptr, &error );
     if (session_connection == nullptr) {
-        SAL_WARN( "vcl.screensaverinhibitor", "failed to connect to dbus 
session bus" );
+        SAL_WARN( "vcl.sessioninhibitor", "failed to connect to dbus session 
bus" );
 
         if (error != nullptr) {
-            SAL_WARN( "vcl.screensaverinhibitor", "Error: " << error->message 
);
+            SAL_WARN( "vcl.sessioninhibitor", "Error: " << error->message );
             g_error_free( error );
         }
 
@@ -107,7 +108,7 @@ static void dbusInhibit( bool bInhibit,
     g_object_unref( G_OBJECT( session_connection ) );
 
     if (proxy == nullptr) {
-        SAL_INFO( "vcl.screensaverinhibitor", "could not get dbus proxy: " << 
service );
+        SAL_INFO( "vcl.sessioninhibitor", "could not get dbus proxy: " << 
service );
         return;
     }
 
@@ -128,7 +129,7 @@ static void dbusInhibit( bool bInhibit,
         }
         else
         {
-            SAL_INFO( "vcl.screensaverinhibitor", service << ".Inhibit 
failed");
+            SAL_INFO( "vcl.sessioninhibitor", service << ".Inhibit failed");
         }
     }
     else
@@ -142,13 +143,13 @@ static void dbusInhibit( bool bInhibit,
         }
         else
         {
-            SAL_INFO( "vcl.screensaverinhibitor", service << ".UnInhibit 
failed" );
+            SAL_INFO( "vcl.sessioninhibitor", service << ".UnInhibit failed" );
         }
     }
 
     if (error != nullptr)
     {
-        SAL_INFO( "vcl.screensaverinhibitor", "Error: " << error->message );
+        SAL_INFO( "vcl.sessioninhibitor", "Error: " << error->message );
         g_error_free( error );
     }
 
diff --git a/vcl/unx/gtk3/gtkframe.cxx b/vcl/unx/gtk3/gtkframe.cxx
index b6b4cd52f8cf..ea13a70ad0ca 100644
--- a/vcl/unx/gtk3/gtkframe.cxx
+++ b/vcl/unx/gtk3/gtkframe.cxx
@@ -31,6 +31,7 @@
 #include <sal/log.hxx>
 #include <comphelper/diagnose_ex.hxx>
 #include <vcl/toolkit/floatwin.hxx>
+#include <vcl/toolkit/unowrap.hxx>
 #include <vcl/svapp.hxx>
 #include <vcl/weld.hxx>
 #include <vcl/window.hxx>
@@ -42,6 +43,7 @@
 #include <X11/Xutil.h>
 #include <unx/gtk/gtkbackend.hxx>
 
+#include <strings.hrc>
 #include <window.h>
 
 #include <basegfx/vector/b2ivector.hxx>
@@ -62,6 +64,8 @@
 
 #include <com/sun/star/awt/MouseButton.hpp>
 #include <com/sun/star/datatransfer/dnd/DNDConstants.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/util/XModifiable.hpp>
 
 #if !GTK_CHECK_VERSION(4, 0, 0)
 #   define GDK_ALT_MASK GDK_MOD1_MASK
@@ -630,7 +634,6 @@ void on_registrar_unavailable( GDBusConnection * 
/*connection*/,
 
     SAL_INFO("vcl.unity", "on_registrar_unavailable");
 
-    //pSessionBus = NULL;
     GtkSalFrame* pSalFrame = static_cast< GtkSalFrame* >( user_data );
 
     SalMenu* pSalMenu = pSalFrame->GetMenu();
@@ -720,6 +723,15 @@ GtkSalFrame::~GtkSalFrame()
 
         if (m_pSettingsPortal)
             g_object_unref(m_pSettingsPortal);
+
+        if (m_nSessionClientSignalId)
+            g_signal_handler_disconnect(m_pSessionClient, 
m_nSessionClientSignalId);
+
+        if (m_pSessionClient)
+            g_object_unref(m_pSessionClient);
+
+        if (m_pSessionManager)
+            g_object_unref(m_pSessionManager);
     }
 
     GtkWidget *pEventWidget = getMouseEventWidget();
@@ -940,7 +952,10 @@ void GtkSalFrame::InitCommon()
     m_nGrabLevel = 0;
     m_bSalObjectSetPosSize = false;
     m_nPortalSettingChangedSignalId = 0;
+    m_nSessionClientSignalId = 0;
     m_pSettingsPortal = nullptr;
+    m_pSessionManager = nullptr;
+    m_pSessionClient = nullptr;
 
     m_aDamageHandler.handle = this;
     m_aDamageHandler.damaged = ::damaged;
@@ -1417,9 +1432,151 @@ void GtkSalFrame::ListenPortalSettings()
 
     UpdateDarkMode();
 
+    if (!m_pSettingsPortal)
+        return;
+
     m_nPortalSettingChangedSignalId = g_signal_connect(m_pSettingsPortal, 
"g-signal", G_CALLBACK(settings_portal_changed_cb), this);
 }
 
+static void session_client_response(GDBusProxy* client_proxy)
+{
+    g_dbus_proxy_call(client_proxy,
+                      "EndSessionResponse",
+                      g_variant_new ("(bs)", true, ""),
+                      G_DBUS_CALL_FLAGS_NONE,
+                      G_MAXINT,
+                      nullptr, nullptr, nullptr);
+}
+
+// unset documents "modify" flag so they won't veto closing
+static void clear_modify_and_terminate()
+{
+    css::uno::Reference<css::uno::XComponentContext> xContext = 
::comphelper::getProcessComponentContext();
+    uno::Reference<frame::XDesktop> xDesktop(frame::Desktop::create(xContext));
+    uno::Reference<css::container::XEnumeration> xComponents = 
xDesktop->getComponents()->createEnumeration();
+    while (xComponents->hasMoreElements())
+    {
+        css::uno::Reference<css::util::XModifiable> 
xModifiable(xComponents->nextElement(), css::uno::UNO_QUERY);
+        if (xModifiable)
+            xModifiable->setModified(false);
+    }
+    xDesktop->terminate();
+}
+
+static void session_client_signal(GDBusProxy* client_proxy, const char*, const 
char* signal_name,
+                                  GVariant* /*parameters*/, gpointer frame)
+{
+    GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame);
+
+    if (g_str_equal (signal_name, "QueryEndSession"))
+    {
+        css::uno::Reference<css::uno::XComponentContext> xContext = 
::comphelper::getProcessComponentContext();
+        uno::Reference<frame::XDesktop2> 
xDesktop(frame::Desktop::create(xContext));
+
+        bool bModified = false;
+
+        // find the XModifiable for this GtkSalFrame
+        if (UnoWrapperBase* pWrapper = UnoWrapperBase::GetUnoWrapper(false))
+        {
+            VclPtr<vcl::Window> xThisWindow = pThis->GetWindow();
+            css::uno::Reference<css::container::XIndexAccess> xList = 
xDesktop->getFrames();
+            sal_Int32 nFrameCount = xList->getCount();
+            for (sal_Int32 i = 0; i < nFrameCount; ++i)
+            {
+                css::uno::Reference<css::frame::XFrame> xFrame;
+                xList->getByIndex(i) >>= xFrame;
+                if (!xFrame)
+                    continue;
+                VclPtr<vcl::Window> xWin = 
pWrapper->GetWindow(xFrame->getContainerWindow());
+                if (!xWin)
+                   continue;
+                if (xWin->GetFrameWindow() != xThisWindow)
+                    continue;
+                css::uno::Reference<css::frame::XController> xController = 
xFrame->getController();
+                if (!xController)
+                    break;
+                css::uno::Reference<css::util::XModifiable> 
xModifiable(xController->getModel(), css::uno::UNO_QUERY);
+                if (!xModifiable)
+                    break;
+                bModified = xModifiable->isModified();
+                break;
+            }
+        }
+
+        pThis->SessionManagerInhibit(bModified, APPLICATION_INHIBIT_LOGOUT, 
VclResId(STR_UNSAVED_DOCUMENTS),
+                                     
gtk_window_get_icon_name(GTK_WINDOW(pThis->getWindow())));
+
+        session_client_response(client_proxy);
+    }
+    else if (g_str_equal (signal_name, "CancelEndSession"))
+    {
+        // restore back to uninhibited (to set again if queried), so frames
+        // that go away before the next logout don't affect that logout
+        pThis->SessionManagerInhibit(false, APPLICATION_INHIBIT_LOGOUT, 
VclResId(STR_UNSAVED_DOCUMENTS),
+                                     
gtk_window_get_icon_name(GTK_WINDOW(pThis->getWindow())));
+    }
+    else if (g_str_equal (signal_name, "EndSession"))
+    {
+        session_client_response(client_proxy);
+        clear_modify_and_terminate();
+    }
+    else if (g_str_equal (signal_name, "Stop"))
+    {
+        clear_modify_and_terminate();
+    }
+}
+
+void GtkSalFrame::ListenSessionManager()
+{
+    EnsureSessionBus();
+
+    if (!pSessionBus)
+        return;
+
+    m_pSessionManager = g_dbus_proxy_new_sync(pSessionBus,
+                                              G_DBUS_PROXY_FLAGS_NONE,
+                                              nullptr,
+                                              "org.gnome.SessionManager",
+                                              "/org/gnome/SessionManager",
+                                              "org.gnome.SessionManager",
+                                              nullptr,
+                                              nullptr);
+
+    if (!m_pSessionManager)
+        return;
+
+    GVariant* res = g_dbus_proxy_call_sync(m_pSessionManager,
+                                 "RegisterClient",
+                                 g_variant_new ("(ss)", "org.libreoffice", ""),
+                                 G_DBUS_CALL_FLAGS_NONE,
+                                 G_MAXINT,
+                                 nullptr,
+                                 nullptr);
+
+    if (!res)
+        return;
+
+    gchar* client_path;
+    g_variant_get(res, "(o)", &client_path);
+    g_variant_unref(res);
+
+    m_pSessionClient = g_dbus_proxy_new_sync(pSessionBus,
+                                             G_DBUS_PROXY_FLAGS_NONE,
+                                             nullptr,
+                                             "org.gnome.SessionManager",
+                                             client_path,
+                                             
"org.gnome.SessionManager.ClientPrivate",
+                                             nullptr,
+                                             nullptr);
+
+    g_free(client_path);
+
+    if (!m_pSessionClient)
+        return;
+
+    m_nSessionClientSignalId = g_signal_connect(m_pSessionClient, "g-signal", 
G_CALLBACK(session_client_signal), this);
+}
+
 void GtkSalFrame::UpdateDarkMode()
 {
     g_autoptr (GVariant) value = nullptr;
@@ -1610,6 +1767,9 @@ void GtkSalFrame::Init( SalFrame* pParent, 
SalFrameStyleFlags nStyle )
 
         // Listen to portal settings for e.g. prefer dark theme
         ListenPortalSettings();
+
+        // Listen to session manager for e.g. query-end
+        ListenSessionManager();
     }
 }
 
@@ -2470,7 +2630,7 @@ void GtkSalFrame::ShowFullScreen( bool bFullScreen, 
sal_Int32 nScreen )
     }
 }
 
-void GtkSalFrame::StartPresentation( bool bStart )
+void GtkSalFrame::SessionManagerInhibit(bool bStart, ApplicationInhibitFlags 
eType, std::u16string_view sReason, const char* application_id)
 {
     guint nWindow(0);
     std::optional<Display*> aDisplay;
@@ -2481,9 +2641,13 @@ void GtkSalFrame::StartPresentation( bool bStart )
         aDisplay = gdk_x11_display_get_xdisplay(getGdkDisplay());
     }
 
-    m_SessionManagerInhibitor.inhibit(bStart, u"presentation",
-                                      APPLICATION_INHIBIT_IDLE,
-                                      nWindow, aDisplay);
+    m_SessionManagerInhibitor.inhibit(bStart, sReason, eType,
+                                      nWindow, aDisplay, application_id);
+}
+
+void GtkSalFrame::StartPresentation( bool bStart )
+{
+    SessionManagerInhibit(bStart, APPLICATION_INHIBIT_IDLE, u"presentation", 
nullptr);
 }
 
 void GtkSalFrame::SetAlwaysOnTop( bool bOnTop )

Reply via email to