From aa6169cea3b45fc9ac6299f5400f29aada8f2860 Mon Sep 17 00:00:00 2001 From: Xavier Claessens Date: Thu, 2 May 2013 18:05:50 +0200 Subject: [PATCH 3/4] McdSlacker: Use org.gnome.SessionManager DBus service MCE is a long dead Nokia closed source service for Maemo. This rewrites McdSlacker using GDBus and the much more useful GNOME SessionManager service. That interface is implemented in both gnome-shell and unity desktops. --- src/mcd-slacker.c | 219 ++++++++------------------ src/mcd-slacker.h | 2 +- tests/twisted/account-manager/device-idle.py | 81 ++++++---- 3 files changed, 117 insertions(+), 185 deletions(-) diff --git a/src/mcd-slacker.c b/src/mcd-slacker.c index 76e34f7..5609ef4 100644 --- a/src/mcd-slacker.c +++ b/src/mcd-slacker.c @@ -1,7 +1,8 @@ /* - * slacker.c - Maemo device state monitor + * slacker.c - Idleness monitor * Copyright ©2010 Collabora Ltd. * Copyright ©2008-2010 Nokia Corporation + * Copyright ©2013 Intel Corporation * * Derived from code in e-book-backend-tp.c in eds-backend-telepathy; thanks! * @@ -23,39 +24,12 @@ #include "mcd-slacker.h" -#include -#include -#include - #include -#ifdef HAVE_MCE -#include -#else /* HAVE_MCE */ - -/* Use some dummy interfaces etc. for the test suite. - * - * In a perfect world of sweetness and light these would not be enabled for the - * real build, but we do not live in a perfect world of sweetness and light: we - * live below a dark cloud of bitter ash, the charred remains of a defunct - * economy and cygnine clothing. - */ -#define MCE_SERVICE "org.freedesktop.Telepathy.MissionControl.Tests.MCE" - -#define MCE_SIGNAL_IF "org.freedesktop.Telepathy.MissionControl.Tests.MCE" -#define MCE_INACTIVITY_SIG "InactivityChanged" - -#define MCE_REQUEST_IF "org.freedesktop.Telepathy.MissionControl.Tests.MCE" -#define MCE_REQUEST_PATH "/org/freedesktop/Telepathy/MissionControl/Tests/MCE" -#define MCE_INACTIVITY_STATUS_GET "GetInactivity" - -#endif /* HAVE_MCE */ - #include "mcd-debug.h" struct _McdSlackerPrivate { - DBusGConnection *bus; - DBusGProxy *mce_request_proxy; + GDBusProxy *proxy; gboolean is_inactive; }; @@ -69,6 +43,22 @@ enum { static guint signals[N_SIGNALS]; +/* GNOME Session Manager interface description: + * https://git.gnome.org/browse/gnome-session/tree/gnome-session/org.gnome.SessionManager.Presence.xml + */ +enum { + STATUS_AVAILABLE = 0, + STATUS_INVISIBLE, + STATUS_BUSY, + STATUS_IDLE +}; + +#define SERVICE_NAME "org.gnome.SessionManager" +#define SERVICE_OBJECT_PATH "/org/gnome/SessionManager/Presence" +#define SERVICE_INTERFACE "org.gnome.SessionManager.Presence" +#define SERVICE_PROP_NAME "status" +#define SERVICE_SIG_NAME "StatusChanged" + /** * mcd_slacker_is_inactive: * @self: do some work! @@ -86,118 +76,74 @@ mcd_slacker_is_inactive (McdSlacker *self) } static void -slacker_inactivity_changed ( - McdSlacker *self, - gboolean is_inactive) +status_changed (McdSlacker *self, + GVariant *prop) { - McdSlackerPrivate *priv = self->priv; - gboolean old = priv->is_inactive; + gboolean old = self->priv->is_inactive; - priv->is_inactive = is_inactive; - - if (!!old != !!is_inactive) + if (g_variant_classify (prop) != G_VARIANT_CLASS_UINT32) { - DEBUG ("device became %s", (is_inactive ? "inactive" : "active")); - g_signal_emit (self, signals[SIG_INACTIVITY_CHANGED], 0, is_inactive); + WARNING ("Status property is of type %s and we expected an uint32", + g_variant_get_type_string (prop)); + return; } -} - -static GQuark mce_signal_interface_quark = 0; -static GQuark mce_inactivity_signal_quark = 0; - -#define INACTIVITY_MATCH_RULE \ - "type='signal',interface='" MCE_SIGNAL_IF "',member='" MCE_INACTIVITY_SIG "'" - -static DBusHandlerResult -slacker_message_filter ( - DBusConnection *connection, - DBusMessage *message, - gpointer user_data) -{ - McdSlacker *self = MCD_SLACKER (user_data); - GQuark interface, member; - const gchar *interface_name = NULL; - const gchar *member_name = NULL; - - if (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_SIGNAL) - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - - interface_name = dbus_message_get_interface (message); - if (interface_name == NULL) - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - member_name = dbus_message_get_member (message); - if (member_name == NULL) - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + self->priv->is_inactive = (g_variant_get_uint32 (prop) == STATUS_IDLE); - interface = g_quark_try_string (interface_name); - member = g_quark_try_string (member_name); - - if (interface == mce_signal_interface_quark && - member == mce_inactivity_signal_quark) + if (self->priv->is_inactive != old) { - gboolean is_inactive; - - if (dbus_message_get_args (message, NULL, DBUS_TYPE_BOOLEAN, &is_inactive, - DBUS_TYPE_INVALID)) - slacker_inactivity_changed (self, is_inactive); - else - DEBUG (MCE_INACTIVITY_SIG " without a boolean argument, ignoring"); + DEBUG ("device became %s", + self->priv->is_inactive ? "inactive" : "active"); + g_signal_emit (self, signals[SIG_INACTIVITY_CHANGED], 0, + self->priv->is_inactive); } - - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } static void -get_inactivity_status_cb ( - DBusGProxy *proxy, - DBusGProxyCall *call, +signal_cb (GDBusProxy *proxy, + gchar *sender_name, + gchar *signal_name, + GVariant *parameters, gpointer user_data) { - McdSlacker *self = MCD_SLACKER (user_data); - gboolean is_inactive; - GError *error = NULL; + McdSlacker *self = user_data; + GVariant *prop; - if (!dbus_g_proxy_end_call (proxy, call, &error /* ignore errors */, - G_TYPE_BOOLEAN, &is_inactive, G_TYPE_INVALID)) - { - DEBUG ("error getting inactivity status: %s", error->message); - g_error_free (error); - } - else - { - slacker_inactivity_changed (self, is_inactive); - } + if (tp_strdiff (signal_name, SERVICE_SIG_NAME)) + return; - tp_clear_object (&self->priv->mce_request_proxy); + prop = g_variant_get_child_value (parameters, 0); + status_changed (self, prop); + g_variant_unref (prop); } static void -slacker_add_filter (McdSlacker *self) +proxy_new_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) { - McdSlackerPrivate *priv = self->priv; - DBusConnection *c = dbus_g_connection_get_connection (self->priv->bus); + McdSlacker *self = user_data; + GVariant *prop; + GError *error = NULL; - dbus_connection_add_filter (c, slacker_message_filter, self, NULL); - dbus_bus_add_match (c, INACTIVITY_MATCH_RULE, NULL); + self->priv->proxy = g_dbus_proxy_new_finish (result, &error); + if (self->priv->proxy == NULL) + { + DEBUG ("Error while creating slacker proxy: %s", error->message); + goto out; + } - priv->mce_request_proxy = dbus_g_proxy_new_for_name (priv->bus, - MCE_SERVICE, MCE_REQUEST_PATH, MCE_REQUEST_IF); - dbus_g_proxy_begin_call (priv->mce_request_proxy, MCE_INACTIVITY_STATUS_GET, - get_inactivity_status_cb, self, NULL, G_TYPE_INVALID); -} + g_signal_connect (self->priv->proxy, "g-signal", + G_CALLBACK (signal_cb), self); -static void -slacker_remove_filter (McdSlacker *self) -{ - DBusConnection *c = dbus_g_connection_get_connection (self->priv->bus); + prop = g_dbus_proxy_get_cached_property (self->priv->proxy, SERVICE_PROP_NAME); + status_changed (self, prop); + g_variant_unref (prop); - dbus_connection_remove_filter (c, slacker_message_filter, self); - dbus_bus_remove_match (c, INACTIVITY_MATCH_RULE, NULL); +out: + g_object_unref (self); } -/* GObject boilerplate */ - static void mcd_slacker_init (McdSlacker *self) { @@ -234,37 +180,20 @@ static void mcd_slacker_constructed (GObject *object) { McdSlacker *self = MCD_SLACKER (object); - GError *error = NULL; - -#ifdef HAVE_MCE - self->priv->bus = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error); -#else - self->priv->bus = dbus_g_bus_get (DBUS_BUS_SESSION, &error); -#endif - if (self->priv->bus == NULL) - { - g_warning ("help! where did my system bus go? %s", error->message); - g_clear_error (&error); - } - else - { - slacker_add_filter (self); - } + g_dbus_proxy_new_for_bus (G_BUS_TYPE_SESSION, + G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, NULL, + SERVICE_NAME, SERVICE_OBJECT_PATH, SERVICE_INTERFACE, + NULL, + proxy_new_cb, g_object_ref (self)); } static void mcd_slacker_dispose (GObject *object) { McdSlacker *self = MCD_SLACKER (object); - McdSlackerPrivate *priv = self->priv; - - tp_clear_object (&priv->mce_request_proxy); /* this cancels pending calls */ - - if (priv->bus != NULL) - slacker_remove_filter (self); - tp_clear_pointer (&priv->bus, dbus_g_connection_unref); + g_clear_object (&self->priv->proxy); ((GObjectClass *) mcd_slacker_parent_class)->dispose (object); } @@ -285,21 +214,11 @@ mcd_slacker_class_init (McdSlackerClass *klass) * @self: what a slacker * @inactive: %TRUE if the device is inactive. * - * The ::inactivity-changed is emitted whenever MCE declares that the device - * has become active or inactive. Note that there is a lag (of around 30 - * seconds, at the time of writing) between the screen blanking and MCE - * declaring the device inactive. + * The ::inactivity-changed is emitted when session becomes idle. */ signals[SIG_INACTIVITY_CHANGED] = g_signal_new ("inactivity-changed", MCD_TYPE_SLACKER, G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_BOOLEAN); - - if (!mce_signal_interface_quark) - { - mce_signal_interface_quark = g_quark_from_static_string (MCE_SIGNAL_IF); - mce_inactivity_signal_quark = g_quark_from_static_string ( - MCE_INACTIVITY_SIG); - } } McdSlacker * diff --git a/src/mcd-slacker.h b/src/mcd-slacker.h index c944547..d2571e3 100644 --- a/src/mcd-slacker.h +++ b/src/mcd-slacker.h @@ -1,5 +1,5 @@ /* - * slacker.h - header for Maemo device state monitor + * slacker.h - header for McdSlacker * Copyright ©2010 Collabora Ltd. * * This library is free software; you can redistribute it and/or diff --git a/tests/twisted/account-manager/device-idle.py b/tests/twisted/account-manager/device-idle.py index 089a066..196b6a0 100644 --- a/tests/twisted/account-manager/device-idle.py +++ b/tests/twisted/account-manager/device-idle.py @@ -1,5 +1,6 @@ # Copyright (C) 2009 Nokia Corporation # Copyright (C) 2009 Collabora Ltd. +# Copyright (C) 2013 Intel Corporation # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -18,10 +19,6 @@ import config -if config.HAVE_MCE: - print "NOTE: built with real MCE support; skipping idleness test" - raise SystemExit(77) - import dbus.service from servicetest import EventPattern, tp_name_prefix, tp_path_prefix, \ @@ -30,34 +27,50 @@ from mctest import exec_test, create_fakecm_account, SimulatedConnection, \ enable_fakecm_account import constants as cs -# Fake MCE constants, cloned from mce-slacker.c -MCE_SERVICE = "org.freedesktop.Telepathy.MissionControl.Tests.MCE" - -MCE_SIGNAL_IF = "org.freedesktop.Telepathy.MissionControl.Tests.MCE" +# Fake SessionManager constants, cloned from mcd-slacker.c +STATUS_AVAILABLE = 0 +STATUS_INVISIBLE = 1 +STATUS_BUSY = 2 +STATUS_IDLE = 3 -MCE_REQUEST_IF = "org.freedesktop.Telepathy.MissionControl.Tests.MCE" -MCE_REQUEST_PATH = "/org/freedesktop/Telepathy/MissionControl/Tests/MCE" +SERVICE_NAME = "org.gnome.SessionManager" +SERVICE_OBJECT_PATH = "/org/gnome/SessionManager/Presence" +SERVICE_INTERFACE = "org.gnome.SessionManager.Presence" +SERVICE_PROP_NAME = "status" +SERVICE_SIG_NAME = "StatusChanged" -class SimulatedMCE(object): - def __init__(self, q, bus, inactive=False): +class SimulatedSession(object): + def __init__(self, q, bus, status=STATUS_AVAILABLE): self.bus = bus self.q = q - self.inactive = inactive - self.object_path = MCE_REQUEST_PATH - self._name_ref = dbus.service.BusName(MCE_SERVICE, bus) - - q.add_dbus_method_impl(self.GetInactivity, - path=self.object_path, interface=MCE_REQUEST_IF, - method='GetInactivity') - - - def GetInactivity(self, e): - self.q.dbus_return(e.message, self.inactive, signature='b') - - def InactivityChanged(self, new_value): - self.inactive = new_value - self.q.dbus_emit(self.object_path, MCE_SIGNAL_IF, "InactivityChanged", - self.inactive, signature="b") + self.status = status + self.object_path = SERVICE_OBJECT_PATH + self._name_ref = dbus.service.BusName(SERVICE_NAME, bus) + + self.q.add_dbus_method_impl(self.GetAll, + path=self.object_path, + interface=cs.PROPERTIES_IFACE, method='GetAll') + self.q.add_dbus_method_impl(self.Get, + path=self.object_path, + interface=cs.PROPERTIES_IFACE, method='Get') + + def GetAll(self, e): + ret = dbus.Dictionary({}, signature='sv') + ret[SERVICE_PROP_NAME] = dbus.UInt32(self.status) + self.q.dbus_return(e.message, ret, signature='a{sv}') + + def Get(self, e): + if e.args[0] == SERVICE_INTERFACE and e.args[1] == SERVICE_PROP_NAME: + self.q.dbus_return(e.message, dbus.UInt32(self.status), signature='v') + return + + self.q.dbus_raise(e.message, cs.NOT_IMPLEMENTED, \ + "Unknown property %s on interface %s" % (e.args[0], e.args[1])) + + def StatusChanged(self, new_value): + self.status = new_value + self.q.dbus_emit(self.object_path, SERVICE_INTERFACE, SERVICE_SIG_NAME, + dbus.UInt32(self.status), signature="u") def release_name(self): del self._name_ref @@ -87,7 +100,7 @@ def _disable_account(q, bus, mc, account, conn): path=conn.object_path, handled=True) def test(q, bus, mc): - mce = SimulatedMCE(q, bus, True) + service = SimulatedSession(q, bus, STATUS_IDLE) account1, conn1 = _create_and_enable( q, bus, mc, "first@example.com", True, @@ -100,10 +113,10 @@ def test(q, bus, mc): q.forbid_events(forbid_no_iface) - for enabled in [False, True, False]: - mce.InactivityChanged(enabled) + for status in [STATUS_AVAILABLE, STATUS_IDLE, STATUS_BUSY]: + service.StatusChanged(status) q.expect('dbus-method-call', method='SetPowerSaving', - args=[enabled], interface=cs.CONN_IFACE_POWER_SAVING, + args=[status == STATUS_IDLE], interface=cs.CONN_IFACE_POWER_SAVING, path=conn1.object_path) _disable_account(q, bus, mc, account1, conn1) @@ -117,13 +130,13 @@ def test(q, bus, mc): q.forbid_events(forbid_when_disconnected) - mce.InactivityChanged(True) + service.StatusChanged(STATUS_IDLE) sync_dbus(bus, q, account1) q.unforbid_events(forbid_when_disconnected) - mce.release_name() + service.release_name() if __name__ == '__main__': exec_test(test, {}) -- 1.7.10.4