/* * Copyright 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, 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 . * * Authors: * Charles Kerr */ #include #include #include #include #include #include #include #include namespace ayatana { namespace indicator { namespace datetime { /*** **** ***/ class Clock::Impl { public: Impl(Clock& owner): m_owner(owner), m_cancellable(g_cancellable_new()) { g_bus_get(G_BUS_TYPE_SYSTEM, m_cancellable, on_bus_ready, this); } ~Impl() { g_cancellable_cancel(m_cancellable); g_object_unref(m_cancellable); for(const auto& tag : m_watched_names) g_bus_unwatch_name(tag); } private: static void on_bus_ready(GObject * /*source_object*/, GAsyncResult * res, gpointer gself) { GError * error = NULL; GDBusConnection * bus; if ((bus = g_bus_get_finish(res, &error))) { auto self = static_cast(gself); auto tag = g_bus_watch_name_on_connection(bus, "org.freedesktop.login1", G_BUS_NAME_WATCHER_FLAGS_NONE, on_login1_appeared, on_login1_vanished, gself, nullptr); self->m_watched_names.insert(tag); tag = g_bus_watch_name_on_connection(bus, BUS_POWERD_NAME, G_BUS_NAME_WATCHER_FLAGS_NONE, on_powerd_appeared, on_powerd_vanished, gself, nullptr); self->m_watched_names.insert(tag); g_object_unref(bus); } else if (error != nullptr) { if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) g_warning("%s Couldn't get system bus: %s", G_STRLOC, error->message); g_error_free(error); } } void remember_subscription(const std::string & name, GDBusConnection * bus, guint tag) { g_object_ref(bus); auto deleter = [tag](GDBusConnection* bus){ g_dbus_connection_signal_unsubscribe(bus, tag); g_object_unref(G_OBJECT(bus)); }; m_subscriptions[name].push_back(std::shared_ptr(bus, deleter)); } /** *** DBus Chatter: org.freedesktop.login1 *** *** Fire Clock::minute_changed() signal on login1's PrepareForSleep signal **/ static void on_login1_appeared(GDBusConnection * bus, const gchar * name, const gchar * name_owner, gpointer gself) { auto tag = g_dbus_connection_signal_subscribe(bus, name_owner, "org.freedesktop.login1.Manager", // interface "PrepareForSleep", // signal name "/org/freedesktop/login1", // object path nullptr, // arg0 G_DBUS_SIGNAL_FLAGS_NONE, on_prepare_for_sleep, gself, nullptr); static_cast(gself)->remember_subscription(name, bus, tag); } static void on_login1_vanished(GDBusConnection * /*system_bus*/, const gchar * name, gpointer gself) { static_cast(gself)->m_subscriptions[name].clear(); } static void on_prepare_for_sleep(GDBusConnection* /*connection*/, const gchar* /*sender_name*/, const gchar* /*object_path*/, const gchar* /*interface_name*/, const gchar* /*signal_name*/, GVariant* /*parameters*/, gpointer gself) { g_debug("firing clock.minute_changed() due to PrepareForSleep"); static_cast(gself)->m_owner.minute_changed(); } /** *** DBus Chatter: com.canonical.powerd *** *** Fire Clock::minute_changed() signal when powerd says the system's *** has awoken from sleep -- the old timestamp is likely out-of-date **/ static void on_powerd_appeared(GDBusConnection * bus, const gchar * name, const gchar * name_owner, gpointer gself) { auto tag = g_dbus_connection_signal_subscribe(bus, name_owner, BUS_POWERD_INTERFACE, "SysPowerStateChange", BUS_POWERD_PATH, nullptr, // arg0 G_DBUS_SIGNAL_FLAGS_NONE, on_sys_power_state_change, gself, // user_data nullptr); // user_data closure static_cast(gself)->remember_subscription(name, bus, tag); } static void on_powerd_vanished(GDBusConnection * /*bus*/, const gchar * name, gpointer gself) { static_cast(gself)->m_subscriptions[name].clear(); } static void on_sys_power_state_change(GDBusConnection* /*connection*/, const gchar* /*sender_name*/, const gchar* /*object_path*/, const gchar* /*interface_name*/, const gchar* /*signal_name*/, GVariant* /*parameters*/, gpointer gself) { g_debug("firing clock.minute_changed() due to state change"); static_cast(gself)->m_owner.minute_changed(); } /*** **** ***/ Clock& m_owner; GCancellable * m_cancellable = nullptr; std::set m_watched_names; std::map>> m_subscriptions; }; /*** **** ***/ Clock::Clock(): m_impl(new Impl{*this}) { } Clock::~Clock() { } /*** **** ***/ } // namespace datetime } // namespace indicator } // namespace ayatana