pidgin/plugins/idle-xscreensaver/idle-xscreensaver.c

changeset 42459
e0a24938f214
child 43179
9d11a64f19f4
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/plugins/idle-xscreensaver/idle-xscreensaver.c	Tue Oct 31 02:58:02 2023 -0500
@@ -0,0 +1,226 @@
+/*
+ * Pidgin - Internet Messenger
+ * Copyright (C) Pidgin Developers <devel@pidgin.im>
+ *
+ * Pidgin is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * 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, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+
+#include <gtk/gtk.h>
+#include <gdk/x11/gdkx.h>
+
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+
+#include <gplugin.h>
+#include <gplugin-native.h>
+
+#include <purple.h>
+
+#include <pidgin.h>
+
+#define PIDGIN_IDLE_XSCREENSAVER_DOMAIN (g_quark_from_static_string("idle-xscreensaver"))
+#define PIDGIN_IDLE_XSCREENSAVER_IDLE_SOURCE ("pidgin-xscreensaver")
+
+/******************************************************************************
+ * Globals
+ *****************************************************************************/
+static GDateTime *idle_at = NULL;
+
+static Atom XA_BLANK = 0;
+static Atom XA_SCREENSAVER_STATUS = 0;
+
+/******************************************************************************
+ * Callbacks
+ *****************************************************************************/
+static gboolean
+pidgin_idle_xscreensaver_xevent_cb(GdkX11Display *gdk_display, gpointer xevent,
+                                   G_GNUC_UNUSED gpointer data)
+{
+	XEvent *event = NULL;
+
+	if(xevent == NULL) {
+		return FALSE;
+	}
+
+	event = xevent;
+
+	if(event->type == PropertyNotify &&
+	   event->xproperty.state == PropertyNewValue &&
+	   event->xproperty.atom == XA_SCREENSAVER_STATUS)
+	{
+		Atom type;
+		Display *x11_display = NULL;
+		Window root_window;
+		int result = 0;
+		int format = 0;
+		gulong n_items = 0;
+		gulong bytes_after = 0;
+		guchar *x_data_ptr = NULL;
+
+		x11_display = gdk_x11_display_get_xdisplay(gdk_display);
+		root_window = RootWindow(x11_display, 0);
+
+		/* We don't use format or bytes_after, but if you pass NULL we
+		 * segfault, so we need them to avoid that.
+		 */
+		result = XGetWindowProperty(x11_display, root_window,
+		                            XA_SCREENSAVER_STATUS, 0, 999, False,
+		                            XA_INTEGER, &type, &format, &n_items,
+		                            &bytes_after, &x_data_ptr);
+		if(result == Success && type != 0 && x_data_ptr != NULL) {
+			gulong *x_data = (gulong *)x_data_ptr;
+			gboolean need_to_set = FALSE;
+			const char *state = NULL;
+
+			if(type != XA_INTEGER || n_items < 3) {
+				g_clear_pointer(&x_data, XFree);
+
+				g_message("malformed status, ignoring");
+
+				return FALSE;
+			}
+
+			if(x_data[0] == XA_BLANK && idle_at == NULL) {
+				state = "blank";
+				idle_at = g_date_time_new_from_unix_local((gint64)x_data[1]);
+				need_to_set = TRUE;
+			} else if(x_data[0] == 0) {
+				need_to_set = TRUE;
+				state = "unblank";
+				g_clear_pointer(&idle_at, g_date_time_unref);
+			}
+
+			if(need_to_set) {
+				PurpleIdleManager *manager = NULL;
+
+				manager = purple_idle_manager_get_default();
+				purple_idle_manager_set_source(manager,
+				                               PIDGIN_IDLE_XSCREENSAVER_IDLE_SOURCE,
+				                               idle_at);
+			}
+
+			if(state != NULL) {
+				g_message("state changed to %s", state);
+			}
+
+			g_clear_pointer(&x_data, XFree);
+		}
+	}
+
+	return FALSE;
+}
+
+/******************************************************************************
+ * Plugin Exports
+ *****************************************************************************/
+static GPluginPluginInfo *
+pidgin_idle_xscreensaver_query(G_GNUC_UNUSED GError **error) {
+	const gchar * const authors[] = {
+		"Pidgin Developers <devel@pidgin.im>",
+		NULL
+	};
+	const char *description = N_("Reads the idle status from XScreenSaver. "
+		"If you aren't using XScreenSaver directly this will not do anything "
+		"useful for you.");
+
+	return purple_plugin_info_new(
+		"id", "pidgin-idle-xscreensaver",
+		"abi-version", PURPLE_ABI_VERSION,
+		"name", N_("XScreenSaver idle reporting"),
+		"version", DISPLAY_VERSION,
+		"category", N_("Presence"),
+		"summary", N_("Read idle state from XScreenSaver"),
+		"description", description,
+		"authors", authors,
+		"website", PURPLE_WEBSITE,
+		NULL);
+}
+
+static gboolean
+pidgin_idle_xscreensaver_load(GPluginPlugin *plugin, GError **error) {
+	PurpleIdleManager *manager = NULL;
+	GdkDisplay *gdk_display = NULL;
+	Display *x11_display = NULL;
+	Window root_window;
+	XWindowAttributes attributes;
+
+	/* Remove any previous idle source we had to make sure we're in a known
+	 * state.
+	 */
+	manager = purple_idle_manager_get_default();
+	purple_idle_manager_set_source(manager,
+	                               PIDGIN_IDLE_XSCREENSAVER_IDLE_SOURCE,
+	                               NULL);
+
+	gdk_display = gdk_display_get_default();
+	if(gdk_display == NULL) {
+		g_set_error_literal(error, PIDGIN_IDLE_XSCREENSAVER_DOMAIN, 0,
+		                    _("failed to find display"));
+
+		return FALSE;
+	}
+
+	if(!GDK_IS_X11_DISPLAY(gdk_display)) {
+		g_set_error_literal(error, PIDGIN_IDLE_XSCREENSAVER_DOMAIN, 0,
+		                    _("display is not an x11 display"));
+
+		return FALSE;
+	}
+
+	/* Set our input mask. */
+	x11_display = gdk_x11_display_get_xdisplay(gdk_display);
+	root_window = RootWindow(x11_display, 0);
+	XGetWindowAttributes(x11_display, root_window, &attributes);
+	XSelectInput(x11_display, root_window,
+	             attributes.your_event_mask | PropertyChangeMask);
+
+	/* Grab our atoms. */
+	XA_BLANK = gdk_x11_get_xatom_by_name_for_display(GDK_X11_DISPLAY(gdk_display),
+	                                                 "BLANK");
+	XA_SCREENSAVER_STATUS = gdk_x11_get_xatom_by_name_for_display(GDK_X11_DISPLAY(gdk_display),
+	                                                              "_SCREENSAVER_STATUS");
+
+	/* Connect our signal. */
+	g_signal_connect_object(gdk_display, "xevent",
+	                        G_CALLBACK(pidgin_idle_xscreensaver_xevent_cb),
+	                        plugin, 0);
+
+	return TRUE;
+}
+
+static gboolean
+pidgin_idle_xscreensaver_unload(GPluginPlugin *plugin,
+                                G_GNUC_UNUSED gboolean shutdown,
+                                G_GNUC_UNUSED GError **error)
+{
+	GdkDisplay *display = NULL;
+
+	g_clear_pointer(&idle_at, g_date_time_unref);
+
+	display = gdk_display_get_default();
+	g_signal_handlers_disconnect_by_func(display,
+	                                     pidgin_idle_xscreensaver_xevent_cb,
+	                                     plugin);
+
+	return TRUE;
+}
+
+GPLUGIN_NATIVE_PLUGIN_DECLARE(pidgin_idle_xscreensaver)

mercurial