diff options
Diffstat (limited to 'src/gs-listener-dbus.c')
-rw-r--r-- | src/gs-listener-dbus.c | 2272 |
1 files changed, 2272 insertions, 0 deletions
diff --git a/src/gs-listener-dbus.c b/src/gs-listener-dbus.c new file mode 100644 index 0000000..601689c --- /dev/null +++ b/src/gs-listener-dbus.c @@ -0,0 +1,2272 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2004-2006 William Jon McCann <[email protected]> + * + * 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 <string.h> + +#include <glib/gi18n.h> + +#include <dbus/dbus.h> +#include <dbus/dbus-glib.h> +#include <dbus/dbus-glib-lowlevel.h> + +#include "gs-listener-dbus.h" +#include "gs-marshal.h" +#include "gs-debug.h" + +/* this is for dbus < 0.3 */ +#if ((DBUS_VERSION_MAJOR == 0) && (DBUS_VERSION_MINOR < 30)) +#define dbus_bus_name_has_owner(connection, name, err) dbus_bus_service_exists(connection, name, err) +#define dbus_bus_request_name(connection, name, flags, err) dbus_bus_acquire_service(connection, name, flags, err) +#endif + +static void gs_listener_class_init (GSListenerClass *klass); +static void gs_listener_init (GSListener *listener); +static void gs_listener_finalize (GObject *object); + +static void gs_listener_unregister_handler (DBusConnection *connection, + void *data); + +static DBusHandlerResult gs_listener_message_handler (DBusConnection *connection, + DBusMessage *message, + void *user_data); + +#define GS_LISTENER_SERVICE "org.mate.ScreenSaver" +#define GS_LISTENER_PATH "/org/mate/ScreenSaver" +#define GS_LISTENER_INTERFACE "org.mate.ScreenSaver" + +#define HAL_DEVICE_INTERFACE "org.freedesktop.Hal.Device" + +#define CK_NAME "org.freedesktop.ConsoleKit" +#define CK_MANAGER_PATH "/org/freedesktop/ConsoleKit/Manager" +#define CK_MANAGER_INTERFACE "org.freedesktop.ConsoleKit.Manager" +#define CK_SESSION_INTERFACE "org.freedesktop.ConsoleKit.Session" + +#define SESSION_NAME "org.mate.SessionManager" +#define SESSION_PATH "/org/mate/SessionManager" +#define SESSION_INTERFACE "org.mate.SessionManager" + +#define TYPE_MISMATCH_ERROR GS_LISTENER_INTERFACE ".TypeMismatch" + +#define GS_LISTENER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GS_TYPE_LISTENER, GSListenerPrivate)) + +struct GSListenerPrivate +{ + DBusConnection *connection; + DBusConnection *system_connection; + + guint session_idle : 1; + guint active : 1; + guint activation_enabled : 1; + guint throttled : 1; + GHashTable *inhibitors; + GHashTable *throttlers; + time_t active_start; + time_t session_idle_start; + char *session_id; + + guint32 ck_throttle_cookie; +}; + +typedef struct +{ + int entry_type; + char *application; + char *reason; + char *connection; + guint32 cookie; + guint32 foreign_cookie; + GTimeVal since; +} GSListenerRefEntry; + +enum +{ + LOCK, + CYCLE, + QUIT, + SIMULATE_USER_ACTIVITY, + ACTIVE_CHANGED, + THROTTLE_CHANGED, + SHOW_MESSAGE, + LAST_SIGNAL +}; + +enum +{ + PROP_0, + PROP_ACTIVE, + PROP_SESSION_IDLE, + PROP_ACTIVATION_ENABLED, +}; + +enum +{ + REF_ENTRY_TYPE_INHIBIT, + REF_ENTRY_TYPE_THROTTLE +}; + +static DBusObjectPathVTable +gs_listener_vtable = { &gs_listener_unregister_handler, + &gs_listener_message_handler, + NULL, + NULL, + NULL, + NULL + }; + +static guint signals [LAST_SIGNAL] = { 0, }; + +G_DEFINE_TYPE (GSListener, gs_listener, G_TYPE_OBJECT) + +GQuark +gs_listener_error_quark (void) +{ + static GQuark quark = 0; + if (!quark) + { + quark = g_quark_from_static_string ("gs_listener_error"); + } + + return quark; +} + +static void +gs_listener_ref_entry_free (GSListenerRefEntry *entry) +{ + g_free (entry->connection); + g_free (entry->application); + g_free (entry->reason); + g_free (entry); + entry = NULL; +} + +static void +gs_listener_unregister_handler (DBusConnection *connection, + void *data) +{ +} + +static gboolean +send_dbus_message (DBusConnection *connection, + DBusMessage *message) +{ + gboolean is_connected; + gboolean sent; + + g_return_val_if_fail (message != NULL, FALSE); + + if (! connection) + { + gs_debug ("There is no valid connection to the message bus"); + return FALSE; + } + + is_connected = dbus_connection_get_is_connected (connection); + if (! is_connected) + { + gs_debug ("Not connected to the message bus"); + return FALSE; + } + + sent = dbus_connection_send (connection, message, NULL); + + return sent; +} + +static void +send_dbus_boolean_signal (GSListener *listener, + const char *name, + gboolean value) +{ + DBusMessage *message; + DBusMessageIter iter; + + g_return_if_fail (listener != NULL); + + message = dbus_message_new_signal (GS_LISTENER_PATH, + GS_LISTENER_SERVICE, + name); + + dbus_message_iter_init_append (message, &iter); + dbus_message_iter_append_basic (&iter, DBUS_TYPE_BOOLEAN, &value); + + if (! send_dbus_message (listener->priv->connection, message)) + { + gs_debug ("Could not send %s signal", name); + } + + dbus_message_unref (message); +} + +static void +gs_listener_send_signal_active_changed (GSListener *listener) +{ + g_return_if_fail (listener != NULL); + + gs_debug ("Sending the ActiveChanged(%s) signal on the session bus", + listener->priv->active ? "TRUE" : "FALSE"); + + send_dbus_boolean_signal (listener, "ActiveChanged", listener->priv->active); +} + +static const char * +get_name_for_entry_type (int entry_type) +{ + const char *name; + + switch (entry_type) + { + case REF_ENTRY_TYPE_INHIBIT: + name = "inhibitor"; + break; + case REF_ENTRY_TYPE_THROTTLE: + name = "throttler"; + break; + default: + g_assert_not_reached (); + break; + } + + return name; +} + +static GHashTable * +get_hash_for_entry_type (GSListener *listener, + int entry_type) +{ + GHashTable *hash; + + switch (entry_type) + { + case REF_ENTRY_TYPE_INHIBIT: + hash = listener->priv->inhibitors; + break; + case REF_ENTRY_TYPE_THROTTLE: + hash = listener->priv->throttlers; + break; + default: + g_assert_not_reached (); + break; + } + + return hash; +} + +static void +list_ref_entry (gpointer key, + gpointer value, + gpointer user_data) +{ + GSListenerRefEntry *entry; + + entry = (GSListenerRefEntry *)value; + + gs_debug ("%s: %s for reason: %s", + get_name_for_entry_type (entry->entry_type), + entry->application, + entry->reason); +} + +static gboolean +listener_ref_entry_is_present (GSListener *listener, + int entry_type) +{ + guint n_entries; + gboolean is_set; + GHashTable *hash; + + hash = get_hash_for_entry_type (listener, entry_type); + + /* if we aren't inhibited then activate */ + n_entries = 0; + if (hash != NULL) + { + n_entries = g_hash_table_size (hash); + + g_hash_table_foreach (hash, list_ref_entry, NULL); + } + + is_set = (n_entries > 0); + + return is_set; +} + +static gboolean +listener_check_activation (GSListener *listener) +{ + gboolean inhibited; + gboolean res; + + gs_debug ("Checking for activation"); + + if (! listener->priv->activation_enabled) + { + return TRUE; + } + + if (! listener->priv->session_idle) + { + return TRUE; + } + + /* if we aren't inhibited then activate */ + inhibited = listener_ref_entry_is_present (listener, REF_ENTRY_TYPE_INHIBIT); + + res = FALSE; + if (! inhibited) + { + gs_debug ("Trying to activate"); + res = gs_listener_set_active (listener, TRUE); + } + + return res; +} + +static void +gs_listener_set_throttle (GSListener *listener, + gboolean throttled) +{ + g_return_if_fail (GS_IS_LISTENER (listener)); + + if (listener->priv->throttled != throttled) + { + gs_debug ("Changing throttle status: %d", throttled); + + listener->priv->throttled = throttled; + + g_signal_emit (listener, signals [THROTTLE_CHANGED], 0, throttled); + } +} + +static gboolean +listener_check_throttle (GSListener *listener) +{ + gboolean throttled; + + gs_debug ("Checking for throttle"); + + throttled = listener_ref_entry_is_present (listener, REF_ENTRY_TYPE_THROTTLE); + + if (throttled != listener->priv->throttled) + { + gs_listener_set_throttle (listener, throttled); + } + + return TRUE; +} + +static gboolean +listener_set_session_idle_internal (GSListener *listener, + gboolean idle) +{ + listener->priv->session_idle = idle; + + if (idle) + { + listener->priv->session_idle_start = time (NULL); + } + else + { + listener->priv->session_idle_start = 0; + } + + return TRUE; +} + +static gboolean +listener_set_active_internal (GSListener *listener, + gboolean active) +{ + listener->priv->active = active; + + /* if idle not in sync with active, change it */ + if (listener->priv->session_idle != active) + { + listener_set_session_idle_internal (listener, active); + } + + if (active) + { + listener->priv->active_start = time (NULL); + } + else + { + listener->priv->active_start = 0; + } + + gs_listener_send_signal_active_changed (listener); + + return TRUE; +} + +gboolean +gs_listener_set_active (GSListener *listener, + gboolean active) +{ + gboolean res; + + g_return_val_if_fail (GS_IS_LISTENER (listener), FALSE); + + if (listener->priv->active == active) + { + gs_debug ("Trying to set active state when already: %s", + active ? "active" : "inactive"); + return FALSE; + } + + res = FALSE; + g_signal_emit (listener, signals [ACTIVE_CHANGED], 0, active, &res); + if (! res) + { + /* if the signal is not handled then we haven't changed state */ + gs_debug ("Active-changed signal not handled"); + + /* clear the idle state */ + if (active) + { + listener_set_session_idle_internal (listener, FALSE); + } + + return FALSE; + } + + listener_set_active_internal (listener, active); + + return TRUE; +} + +gboolean +gs_listener_set_session_idle (GSListener *listener, + gboolean idle) +{ + gboolean res; + + g_return_val_if_fail (GS_IS_LISTENER (listener), FALSE); + + gs_debug ("Setting session idle: %d", idle); + + if (listener->priv->session_idle == idle) + { + gs_debug ("Trying to set idle state when already %s", + idle ? "idle" : "not idle"); + return FALSE; + } + + if (idle) + { + gboolean inhibited; + + inhibited = listener_ref_entry_is_present (listener, REF_ENTRY_TYPE_INHIBIT); + + /* if we are inhibited then do nothing */ + if (inhibited) + { + return FALSE; + } + } + + listener->priv->session_idle = idle; + res = listener_check_activation (listener); + + /* if activation fails then don't set idle */ + if (res) + { + listener_set_session_idle_internal (listener, idle); + } + else + { + gs_debug ("Idle activation failed"); + listener->priv->session_idle = !idle; + } + + return res; +} + +gboolean +gs_listener_get_activation_enabled (GSListener *listener) +{ + g_return_val_if_fail (GS_IS_LISTENER (listener), FALSE); + + return listener->priv->activation_enabled; +} + +gboolean +gs_listener_is_inhibited (GSListener *listener) +{ + gboolean inhibited; + + g_return_val_if_fail (GS_IS_LISTENER (listener), FALSE); + + inhibited = listener_ref_entry_is_present (listener, REF_ENTRY_TYPE_INHIBIT); + + return inhibited; +} + +void +gs_listener_set_activation_enabled (GSListener *listener, + gboolean enabled) +{ + g_return_if_fail (GS_IS_LISTENER (listener)); + + if (listener->priv->activation_enabled != enabled) + { + listener->priv->activation_enabled = enabled; + } +} + +static dbus_bool_t +listener_property_set_bool (GSListener *listener, + guint prop_id, + dbus_bool_t value) +{ + dbus_bool_t ret; + + ret = FALSE; + + switch (prop_id) + { + case PROP_ACTIVE: + gs_listener_set_active (listener, value); + ret = TRUE; + break; + default: + break; + } + + return ret; +} + +static void +raise_error (DBusConnection *connection, + DBusMessage *in_reply_to, + const char *error_name, + char *format, ...) +{ + char buf[512]; + DBusMessage *reply; + + va_list args; + va_start (args, format); + vsnprintf (buf, sizeof (buf), format, args); + va_end (args); + + gs_debug (buf); + reply = dbus_message_new_error (in_reply_to, error_name, buf); + if (reply == NULL) + { + g_error ("No memory"); + } + if (! dbus_connection_send (connection, reply, NULL)) + { + g_error ("No memory"); + } + + dbus_message_unref (reply); +} + +static void +raise_syntax (DBusConnection *connection, + DBusMessage *in_reply_to, + const char *method_name) +{ + raise_error (connection, in_reply_to, + GS_LISTENER_SERVICE ".SyntaxError", + "There is a syntax error in the invocation of the method %s", + method_name); +} + +static guint32 +generate_cookie (void) +{ + guint32 cookie; + + cookie = (guint32)g_random_int_range (1, G_MAXINT32); + + return cookie; +} + +static guint32 +listener_generate_unique_key (GSListener *listener, + int entry_type) +{ + guint32 cookie; + GHashTable *hash; + + hash = get_hash_for_entry_type (listener, entry_type); + + do + { + cookie = generate_cookie (); + } + while (g_hash_table_lookup (hash, &cookie) != NULL); + + return cookie; +} + +static void +listener_ref_entry_check (GSListener *listener, + int entry_type) +{ + switch (entry_type) + { + case REF_ENTRY_TYPE_INHIBIT: + listener_check_activation (listener); + break; + case REF_ENTRY_TYPE_THROTTLE: + listener_check_throttle (listener); + break; + default: + g_assert_not_reached (); + break; + } +} + +static void +add_session_inhibit (GSListener *listener, + GSListenerRefEntry *entry) +{ + DBusMessage *message; + DBusMessage *reply; + DBusMessageIter iter; + DBusMessageIter reply_iter; + DBusError error; + guint xid; + guint flags; + + g_return_if_fail (listener != NULL); + + dbus_error_init (&error); + + message = dbus_message_new_method_call (SESSION_NAME, + SESSION_PATH, + SESSION_INTERFACE, + "Inhibit"); + if (message == NULL) + { + gs_debug ("Couldn't allocate the dbus message"); + return; + } + + dbus_message_iter_init_append (message, &iter); + xid = 0; + flags = 8; + dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &entry->application); + dbus_message_iter_append_basic (&iter, DBUS_TYPE_UINT32, &xid); + dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &entry->reason); + dbus_message_iter_append_basic (&iter, DBUS_TYPE_UINT32, &flags); + + /* FIXME: use async? */ + reply = dbus_connection_send_with_reply_and_block (listener->priv->connection, + message, + -1, + &error); + dbus_message_unref (message); + + if (dbus_error_is_set (&error)) + { + gs_debug ("%s raised:\n %s\n\n", error.name, error.message); + dbus_error_free (&error); + return; + } + + dbus_message_iter_init (reply, &reply_iter); + dbus_message_iter_get_basic (&reply_iter, &entry->foreign_cookie); + + dbus_message_unref (reply); +} + +static void +remove_session_inhibit (GSListener *listener, + GSListenerRefEntry *entry) +{ + DBusMessage *message; + DBusMessage *reply; + DBusMessageIter iter; + DBusError error; + + g_return_if_fail (listener != NULL); + + if (entry->foreign_cookie == 0) + { + gs_debug ("Can't remove inhibitor from session: Session cookie not set"); + return; + } + + dbus_error_init (&error); + + message = dbus_message_new_method_call (SESSION_NAME, + SESSION_PATH, + SESSION_INTERFACE, + "Uninhibit"); + if (message == NULL) + { + gs_debug ("Couldn't allocate the dbus message"); + return; + } + + dbus_message_iter_init_append (message, &iter); + dbus_message_iter_append_basic (&iter, DBUS_TYPE_UINT32, &entry->foreign_cookie); + + /* FIXME: use async? */ + reply = dbus_connection_send_with_reply_and_block (listener->priv->connection, + message, + -1, + &error); + dbus_message_unref (message); + + if (dbus_error_is_set (&error)) + { + gs_debug ("%s raised:\n %s\n\n", error.name, error.message); + dbus_error_free (&error); + return; + } + + dbus_message_unref (reply); +} + +static void +listener_add_ref_entry (GSListener *listener, + int entry_type, + GSListenerRefEntry *entry) +{ + GHashTable *hash; + + gs_debug ("adding %s from %s for reason '%s' on connection %s", + get_name_for_entry_type (entry_type), + entry->application, + entry->reason, + entry->connection); + + hash = get_hash_for_entry_type (listener, entry_type); + g_hash_table_insert (hash, &entry->cookie, entry); + + if (entry_type == REF_ENTRY_TYPE_INHIBIT) + { + /* proxy inhibit over to mate session */ + add_session_inhibit (listener, entry); + } + + listener_ref_entry_check (listener, entry_type); +} + +static gboolean +listener_remove_ref_entry (GSListener *listener, + int entry_type, + guint32 cookie) +{ + GHashTable *hash; + gboolean removed; + GSListenerRefEntry *entry; + + removed = FALSE; + + hash = get_hash_for_entry_type (listener, entry_type); + + entry = g_hash_table_lookup (hash, &cookie); + if (entry == NULL) + { + goto out; + } + + gs_debug ("removing %s from %s for reason '%s' on connection %s", + get_name_for_entry_type (entry_type), + entry->application, + entry->reason, + entry->connection); + + if (entry_type == REF_ENTRY_TYPE_INHIBIT) + { + /* remove inhibit from mate session */ + remove_session_inhibit (listener, entry); + } + + removed = g_hash_table_remove (hash, &cookie); +out: + if (removed) + { + listener_ref_entry_check (listener, entry_type); + } + else + { + gs_debug ("Cookie %u was not in the list!", cookie); + } + + return removed; +} + +#if GLIB_CHECK_VERSION(2,12,0) +#define _g_time_val_to_iso8601(t) g_time_val_to_iso8601(t) +#else +/* copied from GLib */ +static gchar * +_g_time_val_to_iso8601 (GTimeVal *time_) +{ + gchar *retval; + + g_return_val_if_fail (time_->tv_usec >= 0 && time_->tv_usec < G_USEC_PER_SEC, NULL); + +#define ISO_8601_LEN 21 +#define ISO_8601_FORMAT "%Y-%m-%dT%H:%M:%SZ" + retval = g_new0 (gchar, ISO_8601_LEN + 1); + + strftime (retval, ISO_8601_LEN, + ISO_8601_FORMAT, + gmtime (&(time_->tv_sec))); + + return retval; +} +#endif + +static void +accumulate_ref_entry (gpointer key, + GSListenerRefEntry *entry, + DBusMessageIter *iter) +{ + char *description; + char *time; + + time = _g_time_val_to_iso8601 (&entry->since); + + description = g_strdup_printf ("Application=\"%s\"; Since=\"%s\"; Reason=\"%s\";", + entry->application, + time, + entry->reason); + + dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &description); + + g_free (description); + g_free (time); +} + +static DBusHandlerResult +listener_dbus_get_ref_entries (GSListener *listener, + int entry_type, + DBusConnection *connection, + DBusMessage *message) +{ + DBusMessage *reply; + GHashTable *hash; + DBusMessageIter iter; + DBusMessageIter iter_array; + + hash = get_hash_for_entry_type (listener, entry_type); + + reply = dbus_message_new_method_return (message); + if (reply == NULL) + { + g_error ("No memory"); + } + + dbus_message_iter_init_append (reply, &iter); + dbus_message_iter_open_container (&iter, + DBUS_TYPE_ARRAY, + DBUS_TYPE_STRING_AS_STRING, + &iter_array); + + if (hash != NULL) + { + g_hash_table_foreach (hash, + (GHFunc)accumulate_ref_entry, + &iter_array); + } + + dbus_message_iter_close_container (&iter, &iter_array); + + if (! dbus_connection_send (connection, reply, NULL)) + { + g_error ("No memory"); + } + + dbus_message_unref (reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +static void +listener_add_ck_ref_entry (GSListener *listener, + int entry_type, + DBusConnection *connection, + DBusMessage *message, + guint32 *cookiep) +{ + GSListenerRefEntry *entry; + + entry = g_new0 (GSListenerRefEntry, 1); + entry->entry_type = entry_type; + entry->connection = g_strdup (dbus_message_get_sender (message)); + entry->cookie = listener_generate_unique_key (listener, entry_type); + entry->application = g_strdup ("ConsoleKit"); + entry->reason = g_strdup ("Session is not active"); + g_get_current_time (&entry->since); + + /* takes ownership of entry */ + listener_add_ref_entry (listener, entry_type, entry); + + if (cookiep != NULL) + { + *cookiep = entry->cookie; + } +} + +static void +listener_remove_ck_ref_entry (GSListener *listener, + int entry_type, + guint32 cookie) +{ + listener_remove_ref_entry (listener, entry_type, cookie); +} + +static DBusHandlerResult +listener_dbus_add_ref_entry (GSListener *listener, + int entry_type, + DBusConnection *connection, + DBusMessage *message) +{ + DBusMessage *reply; + DBusError error; + const char *application; + const char *reason; + GSListenerRefEntry *entry; + DBusMessageIter iter; + + dbus_error_init (&error); + if (! dbus_message_get_args (message, &error, + DBUS_TYPE_STRING, &application, + DBUS_TYPE_STRING, &reason, + DBUS_TYPE_INVALID)) + { + if (entry_type == REF_ENTRY_TYPE_INHIBIT) + { + raise_syntax (connection, message, "Inhibit"); + } + else if (entry_type == REF_ENTRY_TYPE_THROTTLE) + { + raise_syntax (connection, message, "Throttle"); + } + else + { + g_assert_not_reached (); + } + + return DBUS_HANDLER_RESULT_HANDLED; + } + + reply = dbus_message_new_method_return (message); + if (reply == NULL) + { + g_error ("No memory"); + } + + entry = g_new0 (GSListenerRefEntry, 1); + entry->entry_type = entry_type; + entry->connection = g_strdup (dbus_message_get_sender (message)); + entry->cookie = listener_generate_unique_key (listener, entry_type); + entry->application = g_strdup (application); + entry->reason = g_strdup (reason); + g_get_current_time (&entry->since); + + listener_add_ref_entry (listener, entry_type, entry); + + dbus_message_iter_init_append (reply, &iter); + dbus_message_iter_append_basic (&iter, DBUS_TYPE_UINT32, &entry->cookie); + + if (! dbus_connection_send (connection, reply, NULL)) + { + g_error ("No memory"); + } + + dbus_message_unref (reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +static DBusHandlerResult +listener_dbus_remove_ref_entry (GSListener *listener, + int entry_type, + DBusConnection *connection, + DBusMessage *message) +{ + DBusMessage *reply; + DBusError error; + const char *sender; + guint32 cookie; + + dbus_error_init (&error); + if (! dbus_message_get_args (message, &error, + DBUS_TYPE_UINT32, &cookie, + DBUS_TYPE_INVALID)) + { + if (entry_type == REF_ENTRY_TYPE_INHIBIT) + { + raise_syntax (connection, message, "UnInhibit"); + } + else if (entry_type == REF_ENTRY_TYPE_THROTTLE) + { + raise_syntax (connection, message, "UnThrottle"); + } + else + { + g_assert_not_reached (); + } + + return DBUS_HANDLER_RESULT_HANDLED; + } + + reply = dbus_message_new_method_return (message); + if (reply == NULL) + g_error ("No memory"); + + /* FIXME: check sender is from same connection as entry */ + sender = dbus_message_get_sender (message); + + listener_remove_ref_entry (listener, entry_type, cookie); + + /* FIXME: Pointless? */ + if (! dbus_connection_send (connection, reply, NULL)) + { + g_error ("No memory"); + } + + dbus_message_unref (reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +static gboolean +listener_ref_entry_remove_for_connection (GSListener *listener, + int entry_type, + const char *connection) +{ + gboolean removed; + GHashTable *hash; + GHashTableIter iter; + GSListenerRefEntry *entry; + + if (connection == NULL) + return FALSE; + + hash = get_hash_for_entry_type (listener, entry_type); + + removed = FALSE; + g_hash_table_iter_init (&iter, hash); + while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&entry)) + { + if (entry->connection != NULL && + strcmp (connection, entry->connection) == 0) + { + gs_debug ("removing %s from %s for reason '%s' on connection %s", + get_name_for_entry_type (entry->entry_type), + entry->application, + entry->reason, + entry->connection); + + if (entry->entry_type == REF_ENTRY_TYPE_INHIBIT) + { + /* remove inhibit from mate session */ + remove_session_inhibit (listener, entry); + } + + g_hash_table_iter_remove (&iter); + removed = TRUE; + } + } + + return removed; +} + +static void +listener_service_deleted (GSListener *listener, + DBusMessage *message) +{ + const char *old_service_name; + const char *new_service_name; + gboolean removed; + + if (! dbus_message_get_args (message, NULL, + DBUS_TYPE_STRING, &old_service_name, + DBUS_TYPE_STRING, &new_service_name, + DBUS_TYPE_INVALID)) + { + g_error ("Invalid NameOwnerChanged signal from bus!"); + return; + } + + gs_debug ("DBUS service deleted: %s", new_service_name); + + removed = listener_ref_entry_remove_for_connection (listener, REF_ENTRY_TYPE_THROTTLE, new_service_name); + if (removed) + { + listener_ref_entry_check (listener, REF_ENTRY_TYPE_THROTTLE); + } + + removed = listener_ref_entry_remove_for_connection (listener, REF_ENTRY_TYPE_INHIBIT, new_service_name); + if (removed) + { + listener_ref_entry_check (listener, REF_ENTRY_TYPE_INHIBIT); + } + +} + +static void +raise_property_type_error (DBusConnection *connection, + DBusMessage *in_reply_to, + const char *device_id) +{ + char buf [512]; + DBusMessage *reply; + + snprintf (buf, 511, + "Type mismatch setting property with id %s", + device_id); + gs_debug (buf); + + reply = dbus_message_new_error (in_reply_to, + TYPE_MISMATCH_ERROR, + buf); + if (reply == NULL) + { + g_error ("No memory"); + } + if (! dbus_connection_send (connection, reply, NULL)) + { + g_error ("No memory"); + } + + dbus_message_unref (reply); +} + +static DBusHandlerResult +listener_set_property (GSListener *listener, + DBusConnection *connection, + DBusMessage *message, + guint prop_id) +{ + const char *path; + int type; + gboolean rc; + DBusMessageIter iter; + DBusMessage *reply; + + path = dbus_message_get_path (message); + + dbus_message_iter_init (message, &iter); + type = dbus_message_iter_get_arg_type (&iter); + rc = FALSE; + + switch (type) + { + case DBUS_TYPE_BOOLEAN: + { + dbus_bool_t v; + dbus_message_iter_get_basic (&iter, &v); + rc = listener_property_set_bool (listener, prop_id, v); + break; + } + default: + gs_debug ("Unsupported property type %d", type); + break; + } + + if (! rc) + { + raise_property_type_error (connection, message, path); + return DBUS_HANDLER_RESULT_HANDLED; + } + + reply = dbus_message_new_method_return (message); + + if (reply == NULL) + { + g_error ("No memory"); + } + + if (! dbus_connection_send (connection, reply, NULL)) + { + g_error ("No memory"); + } + + dbus_message_unref (reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +static DBusHandlerResult +listener_get_property (GSListener *listener, + DBusConnection *connection, + DBusMessage *message, + guint prop_id) +{ + DBusMessageIter iter; + DBusMessage *reply; + + reply = dbus_message_new_method_return (message); + + dbus_message_iter_init_append (reply, &iter); + + if (reply == NULL) + g_error ("No memory"); + + switch (prop_id) + { + case PROP_ACTIVE: + { + dbus_bool_t b; + b = listener->priv->active; + dbus_message_iter_append_basic (&iter, DBUS_TYPE_BOOLEAN, &b); + } + break; + default: + gs_debug ("Unsupported property id %u", prop_id); + break; + } + + if (! dbus_connection_send (connection, reply, NULL)) + { + g_error ("No memory"); + } + + dbus_message_unref (reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +static DBusHandlerResult +listener_get_active_time (GSListener *listener, + DBusConnection *connection, + DBusMessage *message) +{ + DBusMessageIter iter; + DBusMessage *reply; + dbus_uint32_t secs; + + reply = dbus_message_new_method_return (message); + + dbus_message_iter_init_append (reply, &iter); + + if (reply == NULL) + { + g_error ("No memory"); + } + + if (listener->priv->active) + { + time_t now = time (NULL); + + if (now < listener->priv->active_start) + { + /* shouldn't happen */ + gs_debug ("Active start time is in the future"); + secs = 0; + } + else if (listener->priv->active_start <= 0) + { + /* shouldn't happen */ + gs_debug ("Active start time was not set"); + secs = 0; + } + else + { + secs = now - listener->priv->active_start; + } + } + else + { + secs = 0; + } + + gs_debug ("Returning screensaver active for %u seconds", secs); + dbus_message_iter_append_basic (&iter, DBUS_TYPE_UINT32, &secs); + + if (! dbus_connection_send (connection, reply, NULL)) + { + g_error ("No memory"); + } + + dbus_message_unref (reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +static DBusHandlerResult +listener_show_message (GSListener *listener, + DBusConnection *connection, + DBusMessage *message) +{ + DBusMessageIter iter; + DBusMessage *reply; + DBusError error; + + reply = dbus_message_new_method_return (message); + + dbus_message_iter_init_append (reply, &iter); + + if (reply == NULL) + { + g_error ("No memory"); + } + + if (listener->priv->active) + { + char *summary; + char *body; + char *icon; + + /* if we're not active we ignore the request */ + + dbus_error_init (&error); + if (! dbus_message_get_args (message, &error, + DBUS_TYPE_STRING, &summary, + DBUS_TYPE_STRING, &body, + DBUS_TYPE_STRING, &icon, + DBUS_TYPE_INVALID)) + { + raise_syntax (connection, message, "ShowMessage"); + return DBUS_HANDLER_RESULT_HANDLED; + } + + g_signal_emit (listener, signals [SHOW_MESSAGE], 0, summary, body, icon); + } + + if (! dbus_connection_send (connection, reply, NULL)) + { + g_error ("No memory"); + } + + dbus_message_unref (reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +static DBusHandlerResult +do_introspect (DBusConnection *connection, + DBusMessage *message, + dbus_bool_t local_interface) +{ + DBusMessage *reply; + GString *xml; + char *xml_string; + + /* standard header */ + xml = g_string_new ("<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\"\n" + "\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n" + "<node>\n" + " <interface name=\"org.freedesktop.DBus.Introspectable\">\n" + " <method name=\"Introspect\">\n" + " <arg name=\"data\" direction=\"out\" type=\"s\"/>\n" + " </method>\n" + " </interface>\n"); + + /* ScreenSaver interface */ + xml = g_string_append (xml, + " <interface name=\"org.mate.ScreenSaver\">\n" + " <method name=\"Lock\">\n" + " </method>\n" + " <method name=\"Cycle\">\n" + " </method>\n" + " <method name=\"SimulateUserActivity\">\n" + " </method>\n" + " <method name=\"Inhibit\">\n" + " <arg name=\"application_name\" direction=\"in\" type=\"s\"/>\n" + " <arg name=\"reason\" direction=\"in\" type=\"s\"/>\n" + " <arg name=\"cookie\" direction=\"out\" type=\"u\"/>\n" + " </method>\n" + " <method name=\"UnInhibit\">\n" + " <arg name=\"cookie\" direction=\"in\" type=\"u\"/>\n" + " </method>\n" + " <method name=\"GetInhibitors\">\n" + " <arg name=\"list\" direction=\"out\" type=\"as\"/>\n" + " </method>\n" + " <method name=\"Throttle\">\n" + " <arg name=\"application_name\" direction=\"in\" type=\"s\"/>\n" + " <arg name=\"reason\" direction=\"in\" type=\"s\"/>\n" + " <arg name=\"cookie\" direction=\"out\" type=\"u\"/>\n" + " </method>\n" + " <method name=\"UnThrottle\">\n" + " <arg name=\"cookie\" direction=\"in\" type=\"u\"/>\n" + " </method>\n" + " <method name=\"GetActive\">\n" + " <arg name=\"value\" direction=\"out\" type=\"b\"/>\n" + " </method>\n" + " <method name=\"GetActiveTime\">\n" + " <arg name=\"seconds\" direction=\"out\" type=\"u\"/>\n" + " </method>\n" + " <method name=\"SetActive\">\n" + " <arg name=\"value\" direction=\"in\" type=\"b\"/>\n" + " </method>\n" + " <method name=\"ShowMessage\">\n" + " <arg name=\"summary\" direction=\"in\" type=\"s\"/>\n" + " <arg name=\"body\" direction=\"in\" type=\"s\"/>\n" + " <arg name=\"icon\" direction=\"in\" type=\"s\"/>\n" + " </method>\n" + " <signal name=\"ActiveChanged\">\n" + " <arg name=\"new_value\" type=\"b\"/>\n" + " </signal>\n" + " </interface>\n"); + + reply = dbus_message_new_method_return (message); + + xml = g_string_append (xml, "</node>\n"); + xml_string = g_string_free (xml, FALSE); + + dbus_message_append_args (reply, + DBUS_TYPE_STRING, &xml_string, + DBUS_TYPE_INVALID); + + g_free (xml_string); + + if (reply == NULL) + { + g_error ("No memory"); + } + + if (! dbus_connection_send (connection, reply, NULL)) + { + g_error ("No memory"); + } + + dbus_message_unref (reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +static DBusHandlerResult +listener_dbus_handle_session_message (DBusConnection *connection, + DBusMessage *message, + void *user_data, + dbus_bool_t local_interface) +{ + GSListener *listener = GS_LISTENER (user_data); + +#if 0 + g_message ("obj_path=%s interface=%s method=%s destination=%s", + dbus_message_get_path (message), + dbus_message_get_interface (message), + dbus_message_get_member (message), + dbus_message_get_destination (message)); +#endif + + g_return_val_if_fail (connection != NULL, DBUS_HANDLER_RESULT_NOT_YET_HANDLED); + g_return_val_if_fail (message != NULL, DBUS_HANDLER_RESULT_NOT_YET_HANDLED); + + if (dbus_message_is_method_call (message, GS_LISTENER_SERVICE, "Lock")) + { + g_signal_emit (listener, signals [LOCK], 0); + return DBUS_HANDLER_RESULT_HANDLED; + } + if (dbus_message_is_method_call (message, GS_LISTENER_SERVICE, "Quit")) + { + g_signal_emit (listener, signals [QUIT], 0); + return DBUS_HANDLER_RESULT_HANDLED; + } + if (dbus_message_is_method_call (message, GS_LISTENER_SERVICE, "Cycle")) + { + g_signal_emit (listener, signals [CYCLE], 0); + return DBUS_HANDLER_RESULT_HANDLED; + } + if (dbus_message_is_method_call (message, GS_LISTENER_SERVICE, "Inhibit")) + { + return listener_dbus_add_ref_entry (listener, REF_ENTRY_TYPE_INHIBIT, connection, message); + } + if (dbus_message_is_method_call (message, GS_LISTENER_SERVICE, "UnInhibit")) + { + return listener_dbus_remove_ref_entry (listener, REF_ENTRY_TYPE_INHIBIT, connection, message); + } + if (dbus_message_is_method_call (message, GS_LISTENER_SERVICE, "GetInhibitors")) + { + return listener_dbus_get_ref_entries (listener, REF_ENTRY_TYPE_INHIBIT, connection, message); + } + if (dbus_message_is_method_call (message, GS_LISTENER_SERVICE, "Throttle")) + { + return listener_dbus_add_ref_entry (listener, REF_ENTRY_TYPE_THROTTLE, connection, message); + } + if (dbus_message_is_method_call (message, GS_LISTENER_SERVICE, "UnThrottle")) + { + return listener_dbus_remove_ref_entry (listener, REF_ENTRY_TYPE_THROTTLE, connection, message); + } + if (dbus_message_is_method_call (message, GS_LISTENER_SERVICE, "SetActive")) + { + return listener_set_property (listener, connection, message, PROP_ACTIVE); + } + if (dbus_message_is_method_call (message, GS_LISTENER_SERVICE, "GetActive")) + { + return listener_get_property (listener, connection, message, PROP_ACTIVE); + } + if (dbus_message_is_method_call (message, GS_LISTENER_SERVICE, "GetActiveTime")) + { + return listener_get_active_time (listener, connection, message); + } + if (dbus_message_is_method_call (message, GS_LISTENER_SERVICE, "ShowMessage")) + { + return listener_show_message (listener, connection, message); + } + if (dbus_message_is_method_call (message, GS_LISTENER_SERVICE, "SimulateUserActivity")) + { + g_signal_emit (listener, signals [SIMULATE_USER_ACTIVITY], 0); + return DBUS_HANDLER_RESULT_HANDLED; + } + if (dbus_message_is_method_call (message, "org.freedesktop.DBus.Introspectable", "Introspect")) + { + return do_introspect (connection, message, local_interface); + } + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +static gboolean +_listener_message_path_is_our_session (GSListener *listener, + DBusMessage *message) +{ + const char *ssid; + gboolean ours; + + ours = FALSE; + + ssid = dbus_message_get_path (message); + if (ssid != NULL + && listener->priv->session_id != NULL + && strcmp (ssid, listener->priv->session_id) == 0) + { + ours = TRUE; + } + + return ours; +} + +static DBusHandlerResult +listener_dbus_handle_system_message (DBusConnection *connection, + DBusMessage *message, + void *user_data, + dbus_bool_t local_interface) +{ + GSListener *listener = GS_LISTENER (user_data); + + g_return_val_if_fail (connection != NULL, DBUS_HANDLER_RESULT_NOT_YET_HANDLED); + g_return_val_if_fail (message != NULL, DBUS_HANDLER_RESULT_NOT_YET_HANDLED); + +#if 1 + gs_debug ("obj_path=%s interface=%s method=%s destination=%s", + dbus_message_get_path (message), + dbus_message_get_interface (message), + dbus_message_get_member (message), + dbus_message_get_destination (message)); +#endif + + if (dbus_message_is_signal (message, HAL_DEVICE_INTERFACE, "Condition")) + { + DBusError error; + const char *event; + const char *keyname; + + dbus_error_init (&error); + if (dbus_message_get_args (message, &error, + DBUS_TYPE_STRING, &event, + DBUS_TYPE_STRING, &keyname, + DBUS_TYPE_INVALID)) + { + if ((event && strcmp (event, "ButtonPressed") == 0) && + (keyname && strcmp (keyname, "coffee") == 0)) + { + gs_debug ("Coffee key was pressed - locking"); + g_signal_emit (listener, signals [LOCK], 0); + } + } + + if (dbus_error_is_set (&error)) + { + dbus_error_free (&error); + } + + return DBUS_HANDLER_RESULT_HANDLED; + } + else if (dbus_message_is_signal (message, CK_SESSION_INTERFACE, "Unlock")) + { + if (_listener_message_path_is_our_session (listener, message)) + { + gs_debug ("Console kit requested session unlock"); + gs_listener_set_active (listener, FALSE); + } + + return DBUS_HANDLER_RESULT_HANDLED; + } + else if (dbus_message_is_signal (message, CK_SESSION_INTERFACE, "Lock")) + { + if (_listener_message_path_is_our_session (listener, message)) + { + gs_debug ("ConsoleKit requested session lock"); + g_signal_emit (listener, signals [LOCK], 0); + } + + return DBUS_HANDLER_RESULT_HANDLED; + } + else if (dbus_message_is_signal (message, CK_SESSION_INTERFACE, "ActiveChanged")) + { + /* NB that `ActiveChanged' refers to the active + * session in ConsoleKit terminology - ie which + * session is currently displayed on the screen. + * mate-screensaver uses `active' to mean `is the + * screensaver active' (ie, is the screen locked) but + * that's not what we're referring to here. + */ + + if (_listener_message_path_is_our_session (listener, message)) + { + DBusError error; + dbus_bool_t new_active; + + dbus_error_init (&error); + if (dbus_message_get_args (message, &error, + DBUS_TYPE_BOOLEAN, &new_active, + DBUS_TYPE_INVALID)) + { + gs_debug ("ConsoleKit notified ActiveChanged %d", new_active); + + /* when we aren't active add an implicit throttle from CK + * when we become active remove the throttle and poke the lock */ + if (new_active) + { + if (listener->priv->ck_throttle_cookie != 0) + { + listener_remove_ck_ref_entry (listener, + REF_ENTRY_TYPE_THROTTLE, + listener->priv->ck_throttle_cookie); + listener->priv->ck_throttle_cookie = 0; + } + + g_signal_emit (listener, signals [SIMULATE_USER_ACTIVITY], 0); + } + else + { + if (listener->priv->ck_throttle_cookie != 0) + { + g_warning ("ConsoleKit throttle already set"); + listener_remove_ck_ref_entry (listener, + REF_ENTRY_TYPE_THROTTLE, + listener->priv->ck_throttle_cookie); + listener->priv->ck_throttle_cookie = 0; + } + + listener_add_ck_ref_entry (listener, + REF_ENTRY_TYPE_THROTTLE, + connection, + message, + &listener->priv->ck_throttle_cookie); + } + } + + if (dbus_error_is_set (&error)) + { + dbus_error_free (&error); + } + } + + return DBUS_HANDLER_RESULT_HANDLED; + } + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +static DBusHandlerResult +gs_listener_message_handler (DBusConnection *connection, + DBusMessage *message, + void *user_data) +{ + g_return_val_if_fail (connection != NULL, DBUS_HANDLER_RESULT_NOT_YET_HANDLED); + g_return_val_if_fail (message != NULL, DBUS_HANDLER_RESULT_NOT_YET_HANDLED); + +#if 0 + g_message ("obj_path=%s interface=%s method=%s destination=%s", + dbus_message_get_path (message), + dbus_message_get_interface (message), + dbus_message_get_member (message), + dbus_message_get_destination (message)); +#endif + + if (dbus_message_is_method_call (message, "org.freedesktop.DBus", "AddMatch")) + { + DBusMessage *reply; + + reply = dbus_message_new_method_return (message); + + if (reply == NULL) + { + g_error ("No memory"); + } + + if (! dbus_connection_send (connection, reply, NULL)) + { + g_error ("No memory"); + } + + dbus_message_unref (reply); + + return DBUS_HANDLER_RESULT_HANDLED; + } + else if (dbus_message_is_signal (message, DBUS_INTERFACE_LOCAL, "Disconnected") && + strcmp (dbus_message_get_path (message), DBUS_PATH_LOCAL) == 0) + { + dbus_connection_unref (connection); + + return DBUS_HANDLER_RESULT_HANDLED; + } + else + { + return listener_dbus_handle_session_message (connection, message, user_data, TRUE); + } +} + +static gboolean +gs_listener_dbus_init (GSListener *listener) +{ + DBusError error; + + dbus_error_init (&error); + + if (listener->priv->connection == NULL) + { + listener->priv->connection = dbus_bus_get (DBUS_BUS_SESSION, &error); + if (listener->priv->connection == NULL) + { + if (dbus_error_is_set (&error)) + { + gs_debug ("couldn't connect to session bus: %s", + error.message); + dbus_error_free (&error); + } + return FALSE; + } + + dbus_connection_setup_with_g_main (listener->priv->connection, NULL); + dbus_connection_set_exit_on_disconnect (listener->priv->connection, FALSE); + } + + if (listener->priv->system_connection == NULL) + { + listener->priv->system_connection = dbus_bus_get (DBUS_BUS_SYSTEM, &error); + if (listener->priv->system_connection == NULL) + { + if (dbus_error_is_set (&error)) + { + gs_debug ("couldn't connect to system bus: %s", + error.message); + dbus_error_free (&error); + } + return FALSE; + } + + dbus_connection_setup_with_g_main (listener->priv->system_connection, NULL); + dbus_connection_set_exit_on_disconnect (listener->priv->system_connection, FALSE); + } + + return TRUE; +} + +static gboolean +reinit_dbus (GSListener *listener) +{ + gboolean initialized; + gboolean try_again; + + initialized = gs_listener_dbus_init (listener); + + /* if we didn't initialize then try again */ + /* FIXME: Should we keep trying forever? If we fail more than + once or twice then the session bus may have died. The + problem is that if it is restarted it will likely have a + different bus address and we won't be able to find it */ + try_again = !initialized; + + return try_again; +} + +static DBusHandlerResult +listener_dbus_filter_function (DBusConnection *connection, + DBusMessage *message, + void *user_data) +{ + GSListener *listener = GS_LISTENER (user_data); + const char *path; + + path = dbus_message_get_path (message); + + /* + g_message ("obj_path=%s interface=%s method=%s", + dbus_message_get_path (message), + dbus_message_get_interface (message), + dbus_message_get_member (message)); + */ + + if (dbus_message_is_signal (message, DBUS_INTERFACE_LOCAL, "Disconnected") + && strcmp (path, DBUS_PATH_LOCAL) == 0) + { + + g_message ("Got disconnected from the session message bus; " + "retrying to reconnect every 10 seconds"); + + dbus_connection_unref (connection); + listener->priv->connection = NULL; + + g_timeout_add (10000, (GSourceFunc)reinit_dbus, listener); + } + else if (dbus_message_is_signal (message, + DBUS_INTERFACE_DBUS, + "NameOwnerChanged")) + { + + if (listener->priv->inhibitors != NULL) + { + listener_service_deleted (listener, message); + } + } + else + { + return listener_dbus_handle_session_message (connection, message, user_data, FALSE); + } + + return DBUS_HANDLER_RESULT_HANDLED; +} + +static DBusHandlerResult +listener_dbus_system_filter_function (DBusConnection *connection, + DBusMessage *message, + void *user_data) +{ + GSListener *listener = GS_LISTENER (user_data); + const char *path; + + path = dbus_message_get_path (message); + + if (dbus_message_is_signal (message, DBUS_INTERFACE_LOCAL, "Disconnected") + && strcmp (path, DBUS_PATH_LOCAL) == 0) + { + + g_message ("Got disconnected from the system message bus; " + "retrying to reconnect every 10 seconds"); + + dbus_connection_unref (connection); + listener->priv->system_connection = NULL; + + g_timeout_add (10000, (GSourceFunc)reinit_dbus, listener); + } + else + { + return listener_dbus_handle_system_message (connection, message, user_data, FALSE); + } + + return DBUS_HANDLER_RESULT_HANDLED; +} + +static void +gs_listener_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GSListener *self; + + self = GS_LISTENER (object); + + switch (prop_id) + { + case PROP_ACTIVE: + gs_listener_set_active (self, g_value_get_boolean (value)); + break; + case PROP_SESSION_IDLE: + gs_listener_set_session_idle (self, g_value_get_boolean (value)); + break; + case PROP_ACTIVATION_ENABLED: + gs_listener_set_activation_enabled (self, g_value_get_boolean (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gs_listener_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GSListener *self; + + self = GS_LISTENER (object); + + switch (prop_id) + { + case PROP_ACTIVE: + g_value_set_boolean (value, self->priv->active); + break; + case PROP_SESSION_IDLE: + g_value_set_boolean (value, self->priv->session_idle); + break; + case PROP_ACTIVATION_ENABLED: + g_value_set_boolean (value, self->priv->activation_enabled); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gs_listener_class_init (GSListenerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = gs_listener_finalize; + object_class->get_property = gs_listener_get_property; + object_class->set_property = gs_listener_set_property; + + signals [LOCK] = + g_signal_new ("lock", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GSListenerClass, lock), + NULL, + NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + signals [QUIT] = + g_signal_new ("quit", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GSListenerClass, quit), + NULL, + NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + signals [CYCLE] = + g_signal_new ("cycle", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GSListenerClass, cycle), + NULL, + NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + signals [SIMULATE_USER_ACTIVITY] = + g_signal_new ("simulate-user-activity", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GSListenerClass, simulate_user_activity), + NULL, + NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + signals [ACTIVE_CHANGED] = + g_signal_new ("active-changed", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GSListenerClass, active_changed), + NULL, + NULL, + gs_marshal_BOOLEAN__BOOLEAN, + G_TYPE_BOOLEAN, + 1, + G_TYPE_BOOLEAN); + signals [THROTTLE_CHANGED] = + g_signal_new ("throttle-changed", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GSListenerClass, throttle_changed), + NULL, + NULL, + g_cclosure_marshal_VOID__BOOLEAN, + G_TYPE_NONE, + 1, + G_TYPE_BOOLEAN); + signals [SHOW_MESSAGE] = + g_signal_new ("show-message", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GSListenerClass, show_message), + NULL, + NULL, + gs_marshal_VOID__STRING_STRING_STRING, + G_TYPE_NONE, + 3, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_STRING); + + g_object_class_install_property (object_class, + PROP_ACTIVE, + g_param_spec_boolean ("active", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE)); + g_object_class_install_property (object_class, + PROP_ACTIVATION_ENABLED, + g_param_spec_boolean ("activation-enabled", + NULL, + NULL, + TRUE, + G_PARAM_READWRITE)); + + g_type_class_add_private (klass, sizeof (GSListenerPrivate)); +} + + +static gboolean +screensaver_is_running (DBusConnection *connection) +{ + DBusError error; + gboolean exists; + + g_return_val_if_fail (connection != NULL, FALSE); + + dbus_error_init (&error); + exists = dbus_bus_name_has_owner (connection, GS_LISTENER_SERVICE, &error); + if (dbus_error_is_set (&error)) + { + dbus_error_free (&error); + } + + return exists; +} + +gboolean +gs_listener_acquire (GSListener *listener, + GError **error) +{ + gboolean acquired; + DBusError buserror; + gboolean is_connected; + + g_return_val_if_fail (listener != NULL, FALSE); + + if (! listener->priv->connection) + { + g_set_error (error, + GS_LISTENER_ERROR, + GS_LISTENER_ERROR_ACQUISITION_FAILURE, + "%s", + _("failed to register with the message bus")); + return FALSE; + } + + is_connected = dbus_connection_get_is_connected (listener->priv->connection); + if (! is_connected) + { + g_set_error (error, + GS_LISTENER_ERROR, + GS_LISTENER_ERROR_ACQUISITION_FAILURE, + "%s", + _("not connected to the message bus")); + return FALSE; + } + + if (screensaver_is_running (listener->priv->connection)) + { + g_set_error (error, + GS_LISTENER_ERROR, + GS_LISTENER_ERROR_ACQUISITION_FAILURE, + "%s", + _("screensaver already running in this session")); + return FALSE; + } + + dbus_error_init (&buserror); + + if (dbus_connection_register_object_path (listener->priv->connection, + GS_LISTENER_PATH, + &gs_listener_vtable, + listener) == FALSE) + { + g_critical ("out of memory registering object path"); + return FALSE; + } + + acquired = dbus_bus_request_name (listener->priv->connection, + GS_LISTENER_SERVICE, + 0, &buserror) != -1; + if (dbus_error_is_set (&buserror)) + { + g_set_error (error, + GS_LISTENER_ERROR, + GS_LISTENER_ERROR_ACQUISITION_FAILURE, + "%s", + buserror.message); + } + + dbus_error_free (&buserror); + + dbus_connection_add_filter (listener->priv->connection, listener_dbus_filter_function, listener, NULL); + + dbus_bus_add_match (listener->priv->connection, + "type='signal'" + ",interface='"DBUS_INTERFACE_DBUS"'" + ",sender='"DBUS_SERVICE_DBUS"'" + ",member='NameOwnerChanged'", + NULL); + + if (listener->priv->system_connection != NULL) + { + dbus_connection_add_filter (listener->priv->system_connection, + listener_dbus_system_filter_function, + listener, + NULL); + + dbus_bus_add_match (listener->priv->system_connection, + "type='signal'" + ",interface='"HAL_DEVICE_INTERFACE"'" + ",member='Condition'", + NULL); + dbus_bus_add_match (listener->priv->system_connection, + "type='signal'" + ",interface='"CK_SESSION_INTERFACE"'" + ",member='Unlock'", + NULL); + dbus_bus_add_match (listener->priv->system_connection, + "type='signal'" + ",interface='"CK_SESSION_INTERFACE"'" + ",member='Lock'", + NULL); + dbus_bus_add_match (listener->priv->system_connection, + "type='signal'" + ",interface='"CK_SESSION_INTERFACE"'" + ",member='ActiveChanged'", + NULL); + } + + return acquired; +} + +static char * +query_session_id (GSListener *listener) +{ + DBusMessage *message; + DBusMessage *reply; + DBusError error; + DBusMessageIter reply_iter; + char *ssid; + + if (listener->priv->system_connection == NULL) + { + gs_debug ("No connection to the system bus"); + return NULL; + } + + ssid = NULL; + + dbus_error_init (&error); + + message = dbus_message_new_method_call (CK_NAME, CK_MANAGER_PATH, CK_MANAGER_INTERFACE, "GetCurrentSession"); + if (message == NULL) + { + gs_debug ("Couldn't allocate the dbus message"); + return NULL; + } + + /* FIXME: use async? */ + reply = dbus_connection_send_with_reply_and_block (listener->priv->system_connection, + message, + -1, &error); + dbus_message_unref (message); + + if (dbus_error_is_set (&error)) + { + gs_debug ("%s raised:\n %s\n\n", error.name, error.message); + dbus_error_free (&error); + return NULL; + } + + dbus_message_iter_init (reply, &reply_iter); + dbus_message_iter_get_basic (&reply_iter, &ssid); + + dbus_message_unref (reply); + + return g_strdup (ssid); +} + +static void +init_session_id (GSListener *listener) +{ + g_free (listener->priv->session_id); + listener->priv->session_id = query_session_id (listener); + gs_debug ("Got session-id: %s", listener->priv->session_id); +} + +static void +gs_listener_init (GSListener *listener) +{ + listener->priv = GS_LISTENER_GET_PRIVATE (listener); + + gs_listener_dbus_init (listener); + + init_session_id (listener); + + listener->priv->inhibitors = g_hash_table_new_full (g_int_hash, + g_int_equal, + NULL, + (GDestroyNotify)gs_listener_ref_entry_free); + listener->priv->throttlers = g_hash_table_new_full (g_int_hash, + g_int_equal, + NULL, + (GDestroyNotify)gs_listener_ref_entry_free); +} + +static void +gs_listener_finalize (GObject *object) +{ + GSListener *listener; + + g_return_if_fail (object != NULL); + g_return_if_fail (GS_IS_LISTENER (object)); + + listener = GS_LISTENER (object); + + g_return_if_fail (listener->priv != NULL); + + if (listener->priv->inhibitors) + { + g_hash_table_destroy (listener->priv->inhibitors); + } + + if (listener->priv->throttlers) + { + g_hash_table_destroy (listener->priv->throttlers); + } + + g_free (listener->priv->session_id); + + G_OBJECT_CLASS (gs_listener_parent_class)->finalize (object); +} + +GSListener * +gs_listener_new (void) +{ + GSListener *listener; + + listener = g_object_new (GS_TYPE_LISTENER, NULL); + + return GS_LISTENER (listener); +} |