diff options
author | Caolán McNamara <caolanm@redhat.com> | 2023-05-09 08:46:09 +0100 |
---|---|---|
committer | Caolán McNamara <caolanm@redhat.com> | 2023-05-09 20:42:49 +0200 |
commit | c04d0570b6c8c78e35c2c9b58bf37bb1218021f5 (patch) | |
tree | c80ae21c87958b7c61e3fb9ceb0b4a35808e7887 /vcl | |
parent | 5c73c3aa6875b1e69b2d58a47ba823faf78b8aa2 (diff) |
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 <caolanm@redhat.com>
Diffstat (limited to 'vcl')
-rw-r--r-- | vcl/Library_vcl.mk | 2 | ||||
-rw-r--r-- | vcl/inc/qt5/QtFrame.hxx | 2 | ||||
-rw-r--r-- | vcl/inc/strings.hrc | 3 | ||||
-rw-r--r-- | vcl/inc/unx/gtk/gtkframe.hxx | 9 | ||||
-rw-r--r-- | vcl/inc/unx/salframe.h | 2 | ||||
-rw-r--r-- | vcl/inc/unx/sessioninhibitor.hxx (renamed from vcl/inc/unx/screensaverinhibitor.hxx) | 3 | ||||
-rw-r--r-- | vcl/unx/generic/window/sessioninhibitor.cxx (renamed from vcl/unx/generic/window/screensaverinhibitor.cxx) | 19 | ||||
-rw-r--r-- | vcl/unx/gtk3/gtkframe.cxx | 174 |
8 files changed, 195 insertions, 19 deletions
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 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 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 ) |