From 16fd4a85a7cadc263bab2de0d5d9062801e6f1ea Mon Sep 17 00:00:00 2001
From: Akira TAGOH <akira@tagoh.org>
Date: Wed, 6 Mar 2024 22:21:36 +0900
Subject: [PATCH] Support Plasma desktop 5+ for logging out

Fixes #3
---
 libimchooseui/Makefile.am             |   1 +
 libimchooseui/eggsmclient-dbus-kde5.c | 203 ++++++++++++++++++++++++++
 libimchooseui/eggsmclient-private.h   |   2 +
 libimchooseui/eggsmclient.c           |   4 +-
 libimchooseui/imchooseui.c            |  20 ++-
 5 files changed, 224 insertions(+), 6 deletions(-)
 create mode 100644 libimchooseui/eggsmclient-dbus-kde5.c

diff --git a/libimchooseui/Makefile.am b/libimchooseui/Makefile.am
index 959fe91..523ace9 100644
--- a/libimchooseui/Makefile.am
+++ b/libimchooseui/Makefile.am
@@ -75,6 +75,7 @@ libeggsmclient_la_SOURCES =					\
 	eggsmclient-private.h					\
 	eggsmclient-dbus-gnome.c				\
 	eggsmclient-dbus-kde.c					\
+	eggsmclient-dbus-kde5.c					\
 	eggsmclient-dbus-xfce.c					\
 	eggsmclient-xsmp.c					\
 	$(NULL)
diff --git a/libimchooseui/eggsmclient-dbus-kde5.c b/libimchooseui/eggsmclient-dbus-kde5.c
new file mode 100644
index 0000000..1ed130d
--- /dev/null
+++ b/libimchooseui/eggsmclient-dbus-kde5.c
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2008,2011 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth
+ * Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "config.h"
+
+#include "eggsmclient.h"
+#include "eggsmclient-private.h"
+
+#include "eggdesktopfile.h"
+
+#include <gio/gio.h>
+
+#define KSM_DBUS_NAME      "org.kde.Shutdown"
+#define KSM_DBUS_PATH      "/Shutdown"
+#define KSM_DBUS_INTERFACE "org.kde.Shutdown"
+
+#define EGG_TYPE_SM_CLIENT_KDBUS5            (egg_sm_client_dbus_kde5_get_type ())
+#define EGG_SM_CLIENT_KDBUS5(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_SM_CLIENT_KDBUS5, EggSMClientKDBus5))
+#define EGG_SM_CLIENT_KDBUS5_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), EGG_TYPE_SM_CLIENT_KDBUS5, EggSMClientKDBus5Class))
+#define EGG_IS_SM_CLIENT_KDBUS5(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_SM_CLIENT_KDBUS5))
+#define EGG_IS_SM_CLIENT_KDBUS5_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EGG_TYPE_SM_CLIENT_KDBUS5))
+#define EGG_SM_CLIENT_KDBUS5_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), EGG_TYPE_SM_CLIENT_KDBUS5, EggSMClientKDBus5Class))
+
+typedef struct _EggSMClientKDBus5        EggSMClientKDBus5;
+typedef struct _EggSMClientKDBus5Class   EggSMClientKDBus5Class;
+
+struct _EggSMClientKDBus5
+{
+	EggSMClient parent;
+
+	GDBusProxy *sm_proxy;
+};
+
+struct _EggSMClientKDBus5Class
+{
+	EggSMClientClass parent_class;
+};
+
+G_DEFINE_TYPE (EggSMClientKDBus5, egg_sm_client_dbus_kde5, EGG_TYPE_SM_CLIENT)
+
+	static const gchar introspection_xml[] =
+	"<node name='/Shutdown'>"
+	"  <interface name='org.kde.Shutdown'>"
+	"    <method name='logout' />"
+	"  </interface>"
+	"</node>";
+
+static GDBusInterfaceInfo *
+sm_client_kdbus5_get_interface_info(void)
+{
+	static gsize has_info = 0;
+	static GDBusInterfaceInfo *info = NULL;
+
+	if (g_once_init_enter(&has_info)) {
+		GError *err = NULL;
+		GDBusNodeInfo *introspection_data = g_dbus_node_info_new_for_xml(introspection_xml,
+										 &err);
+
+		if (err) {
+			g_warning("%s", err->message);
+			return NULL;
+		}
+		info = g_dbus_interface_info_ref(introspection_data->interfaces[0]);
+		g_dbus_node_info_unref(introspection_data);
+
+		g_once_init_leave(&has_info, 1);
+	}
+
+	return info;
+}
+
+static gboolean
+sm_client_dbus_kde5_end_session(EggSMClient         *client,
+				EggSMClientEndStyle  style,
+				gboolean             request_confirmation)
+{
+	EggSMClientKDBus5 *dbus = EGG_SM_CLIENT_KDBUS5 (client);
+	GVariant *value;
+	GError *err = NULL;
+
+	switch (style) {
+	    case EGG_SM_CLIENT_END_SESSION_DEFAULT:
+	    case EGG_SM_CLIENT_LOGOUT:
+		    value = g_dbus_proxy_call_sync(dbus->sm_proxy,
+						   "logout",
+						   NULL,
+						   G_DBUS_CALL_FLAGS_NONE,
+						   -1,
+						   NULL,
+						   &err);
+		    break;
+	    case EGG_SM_CLIENT_REBOOT:
+		    value = g_dbus_proxy_call_sync(dbus->sm_proxy,
+						   "logoutAndReboot",
+						   NULL,
+						   G_DBUS_CALL_FLAGS_NONE,
+						   -1,
+						   NULL,
+						   &err);
+		    break;
+	    case EGG_SM_CLIENT_SHUTDOWN:
+		    value = g_dbus_proxy_call_sync(dbus->sm_proxy,
+						   "logoutAndShutdown",
+						   NULL,
+						   G_DBUS_CALL_FLAGS_NONE,
+						   -1,
+						   NULL,
+						   &err);
+		    break;
+	}
+	if (value)
+		g_variant_unref(value);
+	if (err) {
+		g_warning("%s", err->message);
+		g_error_free(err);
+
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static void
+egg_sm_client_dbus_kde5_init(EggSMClientKDBus5 *dbus)
+{
+}
+
+static void
+egg_sm_client_dbus_kde5_class_init(EggSMClientKDBus5Class *klass)
+{
+	EggSMClientClass *sm_client_class = EGG_SM_CLIENT_CLASS (klass);
+
+	sm_client_class->end_session = sm_client_dbus_kde5_end_session;
+}
+
+EggSMClient *
+egg_sm_client_dbus_kde5_new(void)
+{
+	GDBusProxy *proxy;
+	GDBusConnection *connection;
+	EggSMClientKDBus5 *dbus;
+	GError *err = NULL;
+	GVariant *value = NULL;
+
+	proxy = g_dbus_proxy_new_for_bus_sync(G_BUS_TYPE_SESSION,
+					      G_DBUS_PROXY_FLAGS_NONE,
+					      sm_client_kdbus5_get_interface_info(),
+					      KSM_DBUS_NAME,
+					      KSM_DBUS_PATH,
+					      KSM_DBUS_INTERFACE,
+					      NULL,
+					      &err);
+	if (err) {
+		g_warning("%s", err->message);
+		g_error_free(err);
+		return NULL;
+	}
+	connection = g_dbus_proxy_get_connection(proxy);
+	value = g_dbus_connection_call_sync(connection,
+					    KSM_DBUS_NAME,
+					    KSM_DBUS_PATH,
+					    "org.freedesktop.DBus.Peer",
+					    "Ping",
+					    NULL,
+					    NULL,
+					    G_DBUS_CALL_FLAGS_NO_AUTO_START,
+					    -1,
+					    NULL,
+					    &err);
+	if (err || !value) {
+		goto bail;
+	}
+
+	dbus = g_object_new(EGG_TYPE_SM_CLIENT_KDBUS5, NULL);
+	dbus->sm_proxy = proxy;
+
+	return EGG_SM_CLIENT (dbus);
+  bail:
+	if (value)
+		g_variant_unref(value);
+	if (err)
+		g_error_free(err);
+	if (proxy)
+		g_object_unref(proxy);
+
+	return NULL;
+}
diff --git a/libimchooseui/eggsmclient-private.h b/libimchooseui/eggsmclient-private.h
index 6a2edbc..973c9a7 100644
--- a/libimchooseui/eggsmclient-private.h
+++ b/libimchooseui/eggsmclient-private.h
@@ -46,6 +46,8 @@ GType        egg_sm_client_dbus_gnome_get_type (void);
 EggSMClient *egg_sm_client_dbus_gnome_new      (void);
 GType        egg_sm_client_dbus_kde_get_type   (void);
 EggSMClient *egg_sm_client_dbus_kde_new        (void);
+GType        egg_sm_client_dbus_kde5_get_type  (void);
+EggSMClient *egg_sm_client_dbus_kde5_new       (void);
 GType        egg_sm_client_dbus_xfce_get_type  (void);
 EggSMClient *egg_sm_client_dbus_xfce_new       (void);
 # endif
diff --git a/libimchooseui/eggsmclient.c b/libimchooseui/eggsmclient.c
index bb8efd1..fbdc02c 100644
--- a/libimchooseui/eggsmclient.c
+++ b/libimchooseui/eggsmclient.c
@@ -112,7 +112,7 @@ egg_sm_client_class_init (EggSMClientClass *klass)
    * handling this signal; if the user has requested that the session
    * be saved when logging out, then ::save_state will be emitted
    * separately.
-   * 
+   *
    * If the application agrees to quit, it should then wait for either
    * the ::quit_cancelled or ::quit signals to be emitted.
    **/
@@ -342,6 +342,8 @@ egg_sm_client_get (void)
 	   */
 #ifdef EGG_SM_CLIENT_BACKEND_DBUS
 	  global_client = egg_sm_client_dbus_gnome_new ();
+	  if (!global_client)
+	    global_client = egg_sm_client_dbus_kde5_new ();
 	  if (!global_client)
 	    global_client = egg_sm_client_dbus_kde_new ();
 	  if (!global_client)
diff --git a/libimchooseui/imchooseui.c b/libimchooseui/imchooseui.c
index af3b68c..504e043 100644
--- a/libimchooseui/imchooseui.c
+++ b/libimchooseui/imchooseui.c
@@ -1,21 +1,21 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/* 
+/*
  * imchooseui.c
  * Copyright (C) 2007-2021 Red Hat, Inc. All rights reserved.
- * 
+ *
  * Authors:
  *   Akira TAGOH  <tagoh@redhat.com>
- * 
+ *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
- * 
+ *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
- * 
+ *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth
@@ -443,7 +443,17 @@ _imchoose_ui_switch_im_start(IMChooseUI  *ui,
 			     const gchar *imname)
 {
 	IMChooseUIPrivate *priv = imchoose_ui_get_instance_private(ui);
+	GError *err = NULL;
 
+	if (!imsettings_client_is_action_needed(priv->client, imname, NULL, &err))
+	{
+		g_warning("%s", err->message);
+		g_signal_emit(ui, signals[SIG_SHOW_ERROR], 0,
+			      _("Failed to switch Input Method"),
+			      _("Input Method doesn't support im-chooser on this desktop session"));
+		g_error_free(err);
+		return;
+	}
 	_imchoose_ui_show_progress(ui, _("Switching Input Method - %s"), imname);
 	g_free(priv->current_im);
 	priv->current_im = g_strdup(imname);
-- 
2.43.0

