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>
diff --git a/include/sal/log-areas.dox b/include/sal/log-areas.dox
index 15bcf5a..51b005e 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 3453747..a4f04ad 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 9eca753..a7c1fbc 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 b927d36..09abe53 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 83beea0..c2e95f2 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 1a83a7f..890bcdb 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 d8a177d..c14c325 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 6cfa3e2..5385cde 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 b1cfcb3..300df9f 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 b6b4cd5..ea13a70 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 )