summaryrefslogtreecommitdiff
path: root/src/gs-watcher-x11.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gs-watcher-x11.c')
-rw-r--r--src/gs-watcher-x11.c680
1 files changed, 680 insertions, 0 deletions
diff --git a/src/gs-watcher-x11.c b/src/gs-watcher-x11.c
new file mode 100644
index 0000000..1e3114e
--- /dev/null
+++ b/src/gs-watcher-x11.c
@@ -0,0 +1,680 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004-2006 William Jon McCann <[email protected]>
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#include "config.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include <errno.h>
+
+#include <string.h>
+#include <gdk/gdkx.h>
+
+#include <dbus/dbus.h>
+#include <dbus/dbus-glib.h>
+
+#include "gs-watcher.h"
+#include "gs-marshal.h"
+#include "gs-debug.h"
+
+static void gs_watcher_class_init (GSWatcherClass *klass);
+static void gs_watcher_init (GSWatcher *watcher);
+static void gs_watcher_finalize (GObject *object);
+
+static gboolean watchdog_timer (GSWatcher *watcher);
+
+#define GS_WATCHER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GS_TYPE_WATCHER, GSWatcherPrivate))
+
+struct GSWatcherPrivate
+{
+ /* settings */
+ guint enabled : 1;
+ guint delta_notice_timeout;
+
+ /* state */
+ guint active : 1;
+ guint idle : 1;
+ guint idle_notice : 1;
+
+ guint idle_id;
+ char *status_message;
+
+ DBusGProxy *presence_proxy;
+ guint watchdog_timer_id;
+};
+
+enum
+{
+ PROP_0,
+ PROP_STATUS_MESSAGE
+};
+
+enum
+{
+ IDLE_CHANGED,
+ IDLE_NOTICE_CHANGED,
+ LAST_SIGNAL
+};
+
+static guint signals [LAST_SIGNAL] = { 0, };
+
+G_DEFINE_TYPE (GSWatcher, gs_watcher, G_TYPE_OBJECT)
+
+static void
+remove_watchdog_timer (GSWatcher *watcher)
+{
+ if (watcher->priv->watchdog_timer_id != 0)
+ {
+ g_source_remove (watcher->priv->watchdog_timer_id);
+ watcher->priv->watchdog_timer_id = 0;
+ }
+}
+
+static void
+add_watchdog_timer (GSWatcher *watcher,
+ glong timeout)
+{
+ watcher->priv->watchdog_timer_id = g_timeout_add (timeout,
+ (GSourceFunc)watchdog_timer,
+ watcher);
+}
+
+static void
+gs_watcher_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GSWatcher *self;
+
+ self = GS_WATCHER (object);
+
+ switch (prop_id)
+ {
+ case PROP_STATUS_MESSAGE:
+ g_value_set_string (value, self->priv->status_message);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+set_status_text (GSWatcher *watcher,
+ const char *text)
+{
+ g_free (watcher->priv->status_message);
+
+ watcher->priv->status_message = g_strdup (text);
+ g_object_notify (G_OBJECT (watcher), "status-message");
+}
+
+static void
+gs_watcher_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GSWatcher *self;
+
+ self = GS_WATCHER (object);
+
+ switch (prop_id)
+ {
+ case PROP_STATUS_MESSAGE:
+ set_status_text (self, g_value_get_string (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gs_watcher_class_init (GSWatcherClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gs_watcher_finalize;
+ object_class->get_property = gs_watcher_get_property;
+ object_class->set_property = gs_watcher_set_property;
+
+ g_object_class_install_property (object_class,
+ PROP_STATUS_MESSAGE,
+ g_param_spec_string ("status-message",
+ NULL,
+ NULL,
+ NULL,
+ G_PARAM_READWRITE));
+
+ signals [IDLE_CHANGED] =
+ g_signal_new ("idle-changed",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GSWatcherClass, idle_changed),
+ NULL,
+ NULL,
+ gs_marshal_BOOLEAN__BOOLEAN,
+ G_TYPE_BOOLEAN,
+ 1, G_TYPE_BOOLEAN);
+ signals [IDLE_NOTICE_CHANGED] =
+ g_signal_new ("idle-notice-changed",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GSWatcherClass, idle_notice_changed),
+ NULL,
+ NULL,
+ gs_marshal_BOOLEAN__BOOLEAN,
+ G_TYPE_BOOLEAN,
+ 1, G_TYPE_BOOLEAN);
+
+ g_type_class_add_private (klass, sizeof (GSWatcherPrivate));
+}
+
+static gboolean
+_gs_watcher_set_session_idle_notice (GSWatcher *watcher,
+ gboolean in_effect)
+{
+ gboolean res;
+
+ res = FALSE;
+
+ if (in_effect != watcher->priv->idle_notice)
+ {
+
+ g_signal_emit (watcher, signals [IDLE_NOTICE_CHANGED], 0, in_effect, &res);
+ if (res)
+ {
+ gs_debug ("Changing idle notice state: %d", in_effect);
+
+ watcher->priv->idle_notice = in_effect;
+ }
+ else
+ {
+ gs_debug ("Idle notice signal not handled: %d", in_effect);
+ }
+ }
+
+ return res;
+}
+
+static gboolean
+_gs_watcher_set_session_idle (GSWatcher *watcher,
+ gboolean is_idle)
+{
+ gboolean res;
+
+ res = FALSE;
+
+ if (is_idle != watcher->priv->idle)
+ {
+
+ g_signal_emit (watcher, signals [IDLE_CHANGED], 0, is_idle, &res);
+ if (res)
+ {
+ gs_debug ("Changing idle state: %d", is_idle);
+
+ watcher->priv->idle = is_idle;
+ }
+ else
+ {
+ gs_debug ("Idle changed signal not handled: %d", is_idle);
+ }
+ }
+
+ return res;
+}
+
+gboolean
+gs_watcher_get_active (GSWatcher *watcher)
+{
+ gboolean active;
+
+ g_return_val_if_fail (GS_IS_WATCHER (watcher), FALSE);
+
+ active = watcher->priv->active;
+
+ return active;
+}
+
+static void
+_gs_watcher_reset_state (GSWatcher *watcher)
+{
+ watcher->priv->idle = FALSE;
+ watcher->priv->idle_notice = FALSE;
+}
+
+static gboolean
+_gs_watcher_set_active_internal (GSWatcher *watcher,
+ gboolean active)
+{
+ if (active != watcher->priv->active)
+ {
+ /* reset state */
+ _gs_watcher_reset_state (watcher);
+
+ watcher->priv->active = active;
+ }
+
+ return TRUE;
+}
+
+gboolean
+gs_watcher_set_active (GSWatcher *watcher,
+ gboolean active)
+{
+ g_return_val_if_fail (GS_IS_WATCHER (watcher), FALSE);
+
+ gs_debug ("turning watcher: %s", active ? "ON" : "OFF");
+
+ if (watcher->priv->active == active)
+ {
+ gs_debug ("Idle detection is already %s",
+ active ? "active" : "inactive");
+ return FALSE;
+ }
+
+ if (! watcher->priv->enabled)
+ {
+ gs_debug ("Idle detection is disabled, cannot activate");
+ return FALSE;
+ }
+
+ return _gs_watcher_set_active_internal (watcher, active);
+}
+
+gboolean
+gs_watcher_set_enabled (GSWatcher *watcher,
+ gboolean enabled)
+{
+ g_return_val_if_fail (GS_IS_WATCHER (watcher), FALSE);
+
+ if (watcher->priv->enabled != enabled)
+ {
+ gboolean is_active = gs_watcher_get_active (watcher);
+
+ watcher->priv->enabled = enabled;
+
+ /* if we are disabling the watcher and we are
+ active shut it down */
+ if (! enabled && is_active)
+ {
+ _gs_watcher_set_active_internal (watcher, FALSE);
+ }
+ }
+
+ return TRUE;
+}
+
+gboolean
+gs_watcher_get_enabled (GSWatcher *watcher)
+{
+ gboolean enabled;
+
+ g_return_val_if_fail (GS_IS_WATCHER (watcher), FALSE);
+
+ enabled = watcher->priv->enabled;
+
+ return enabled;
+}
+
+static gboolean
+on_idle_timeout (GSWatcher *watcher)
+{
+ gboolean res;
+
+ res = _gs_watcher_set_session_idle (watcher, TRUE);
+
+ _gs_watcher_set_session_idle_notice (watcher, FALSE);
+
+ /* try again if we failed i guess */
+ return !res;
+}
+
+static void
+set_status (GSWatcher *watcher,
+ guint status)
+{
+ gboolean res;
+ gboolean is_idle;
+
+ if (! watcher->priv->active)
+ {
+ gs_debug ("GSWatcher: not active, ignoring status changes");
+ return;
+ }
+
+ is_idle = (status == 3);
+
+ if (!is_idle && !watcher->priv->idle_notice)
+ {
+ /* no change in idleness */
+ return;
+ }
+
+ if (is_idle)
+ {
+ res = _gs_watcher_set_session_idle_notice (watcher, is_idle);
+ /* queue an activation */
+ if (watcher->priv->idle_id > 0)
+ {
+ g_source_remove (watcher->priv->idle_id);
+ }
+ watcher->priv->idle_id = g_timeout_add (watcher->priv->delta_notice_timeout,
+ (GSourceFunc)on_idle_timeout,
+ watcher);
+ }
+ else
+ {
+ /* cancel notice too */
+ if (watcher->priv->idle_id > 0)
+ {
+ g_source_remove (watcher->priv->idle_id);
+ }
+ res = _gs_watcher_set_session_idle (watcher, FALSE);
+ res = _gs_watcher_set_session_idle_notice (watcher, FALSE);
+ }
+}
+
+static void
+on_presence_status_changed (DBusGProxy *presence_proxy,
+ guint status,
+ GSWatcher *watcher)
+{
+ set_status (watcher, status);
+}
+
+static void
+on_presence_status_text_changed (DBusGProxy *presence_proxy,
+ const char *status_text,
+ GSWatcher *watcher)
+{
+ set_status_text (watcher, status_text);
+}
+
+static gboolean
+connect_presence_watcher (GSWatcher *watcher)
+{
+ DBusGConnection *bus;
+ GError *error;
+ gboolean ret;
+
+ ret = FALSE;
+
+ error = NULL;
+ bus = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
+ if (bus == NULL)
+ {
+ g_warning ("Unable to get session bus: %s", error->message);
+ g_error_free (error);
+ goto done;
+ }
+
+ error = NULL;
+ watcher->priv->presence_proxy = dbus_g_proxy_new_for_name_owner (bus,
+ "org.mate.SessionManager",
+ "/org/mate/SessionManager/Presence",
+ "org.mate.SessionManager.Presence",
+ &error);
+ if (watcher->priv->presence_proxy != NULL)
+ {
+ DBusGProxy *proxy;
+
+ dbus_g_proxy_add_signal (watcher->priv->presence_proxy,
+ "StatusChanged",
+ G_TYPE_UINT,
+ G_TYPE_INVALID);
+ dbus_g_proxy_connect_signal (watcher->priv->presence_proxy,
+ "StatusChanged",
+ G_CALLBACK (on_presence_status_changed),
+ watcher,
+ NULL);
+ dbus_g_proxy_add_signal (watcher->priv->presence_proxy,
+ "StatusTextChanged",
+ G_TYPE_STRING,
+ G_TYPE_INVALID);
+ dbus_g_proxy_connect_signal (watcher->priv->presence_proxy,
+ "StatusTextChanged",
+ G_CALLBACK (on_presence_status_text_changed),
+ watcher,
+ NULL);
+
+ proxy = dbus_g_proxy_new_from_proxy (watcher->priv->presence_proxy,
+ "org.freedesktop.DBus.Properties",
+ "/org/mate/SessionManager/Presence");
+ if (proxy != NULL)
+ {
+ guint status;
+ const char *status_text;
+ GValue value = { 0, };
+
+ status = 0;
+ status_text = NULL;
+
+ error = NULL;
+ dbus_g_proxy_call (proxy,
+ "Get",
+ &error,
+ G_TYPE_STRING, "org.mate.SessionManager.Presence",
+ G_TYPE_STRING, "status",
+ G_TYPE_INVALID,
+ G_TYPE_VALUE, &value,
+ G_TYPE_INVALID);
+
+ if (error != NULL)
+ {
+ g_warning ("Couldn't get presence status: %s", error->message);
+ g_error_free (error);
+ goto done;
+ }
+ else
+ {
+ status = g_value_get_uint (&value);
+ }
+
+ g_value_unset (&value);
+
+ error = NULL;
+ dbus_g_proxy_call (proxy,
+ "Get",
+ &error,
+ G_TYPE_STRING, "org.mate.SessionManager.Presence",
+ G_TYPE_STRING, "status-text",
+ G_TYPE_INVALID,
+ G_TYPE_VALUE, &value,
+ G_TYPE_INVALID);
+
+ if (error != NULL)
+ {
+ g_warning ("Couldn't get presence status text: %s", error->message);
+ g_error_free (error);
+ }
+ else
+ {
+ status_text = g_value_get_string (&value);
+ }
+
+ set_status (watcher, status);
+ set_status_text (watcher, status_text);
+ }
+ }
+ else
+ {
+ g_warning ("Failed to get session presence proxy: %s", error->message);
+ g_error_free (error);
+ goto done;
+ }
+
+ ret = TRUE;
+
+done:
+ return ret;
+}
+
+static void
+gs_watcher_init (GSWatcher *watcher)
+{
+ watcher->priv = GS_WATCHER_GET_PRIVATE (watcher);
+
+ watcher->priv->enabled = TRUE;
+ watcher->priv->active = FALSE;
+
+ connect_presence_watcher (watcher);
+
+ /* time before idle signal to send notice signal */
+ watcher->priv->delta_notice_timeout = 10000;
+
+ add_watchdog_timer (watcher, 600000);
+}
+
+static void
+gs_watcher_finalize (GObject *object)
+{
+ GSWatcher *watcher;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GS_IS_WATCHER (object));
+
+ watcher = GS_WATCHER (object);
+
+ g_return_if_fail (watcher->priv != NULL);
+
+ remove_watchdog_timer (watcher);
+
+ if (watcher->priv->idle_id > 0)
+ {
+ g_source_remove (watcher->priv->idle_id);
+ }
+
+ watcher->priv->active = FALSE;
+
+ if (watcher->priv->presence_proxy != NULL)
+ {
+ g_object_unref (watcher->priv->presence_proxy);
+ }
+
+ g_free (watcher->priv->status_message);
+
+ G_OBJECT_CLASS (gs_watcher_parent_class)->finalize (object);
+}
+
+/* Figuring out what the appropriate XSetScreenSaver() parameters are
+ (one wouldn't expect this to be rocket science.)
+*/
+static void
+disable_builtin_screensaver (GSWatcher *watcher,
+ gboolean unblank_screen)
+{
+ int current_server_timeout, current_server_interval;
+ int current_prefer_blank, current_allow_exp;
+ int desired_server_timeout, desired_server_interval;
+ int desired_prefer_blank, desired_allow_exp;
+
+ XGetScreenSaver (GDK_DISPLAY (),
+ &current_server_timeout,
+ &current_server_interval,
+ &current_prefer_blank,
+ &current_allow_exp);
+
+ desired_server_timeout = current_server_timeout;
+ desired_server_interval = current_server_interval;
+ desired_prefer_blank = current_prefer_blank;
+ desired_allow_exp = current_allow_exp;
+
+ desired_server_interval = 0;
+
+ /* I suspect (but am not sure) that DontAllowExposures might have
+ something to do with powering off the monitor as well, at least
+ on some systems that don't support XDPMS? Who know... */
+ desired_allow_exp = AllowExposures;
+
+ /* When we're not using an extension, set the server-side timeout to 0,
+ so that the server never gets involved with screen blanking, and we
+ do it all ourselves. (However, when we *are* using an extension,
+ we tell the server when to notify us, and rather than blanking the
+ screen, the server will send us an X event telling us to blank.)
+ */
+ desired_server_timeout = 0;
+
+ if (desired_server_timeout != current_server_timeout
+ || desired_server_interval != current_server_interval
+ || desired_prefer_blank != current_prefer_blank
+ || desired_allow_exp != current_allow_exp)
+ {
+
+ gs_debug ("disabling server builtin screensaver:"
+ " (xset s %d %d; xset s %s; xset s %s)",
+ desired_server_timeout,
+ desired_server_interval,
+ (desired_prefer_blank ? "blank" : "noblank"),
+ (desired_allow_exp ? "expose" : "noexpose"));
+
+ XSetScreenSaver (GDK_DISPLAY (),
+ desired_server_timeout,
+ desired_server_interval,
+ desired_prefer_blank,
+ desired_allow_exp);
+
+ XSync (GDK_DISPLAY (), FALSE);
+ }
+
+ if (unblank_screen)
+ {
+ /* Turn off the server builtin saver if it is now running. */
+ XForceScreenSaver (GDK_DISPLAY (), ScreenSaverReset);
+ }
+}
+
+
+/* This timer goes off every few minutes, whether the user is idle or not,
+ to try and clean up anything that has gone wrong.
+
+ It calls disable_builtin_screensaver() so that if xset has been used,
+ or some other program (like xlock) has messed with the XSetScreenSaver()
+ settings, they will be set back to sensible values (if a server extension
+ is in use, messing with xlock can cause the screensaver to never get a wakeup
+ event, and could cause monitor power-saving to occur, and all manner of
+ heinousness.)
+
+ */
+
+static gboolean
+watchdog_timer (GSWatcher *watcher)
+{
+
+ disable_builtin_screensaver (watcher, FALSE);
+
+ return TRUE;
+}
+
+GSWatcher *
+gs_watcher_new (void)
+{
+ GSWatcher *watcher;
+
+ watcher = g_object_new (GS_TYPE_WATCHER,
+ NULL);
+
+ return GS_WATCHER (watcher);
+}