diff options
-rw-r--r-- | include/datetime/clock.h | 9 | ||||
-rw-r--r-- | src/clock.cpp | 138 | ||||
-rw-r--r-- | tests/test-clock.cpp | 50 |
3 files changed, 132 insertions, 65 deletions
diff --git a/include/datetime/clock.h b/include/datetime/clock.h index 1d488d1..0b2a543 100644 --- a/include/datetime/clock.h +++ b/include/datetime/clock.h @@ -55,12 +55,9 @@ protected: void maybe_emit (const DateTime& a, const DateTime& b); private: - static void on_system_bus_ready(GObject*, GAsyncResult*, gpointer); - static void on_prepare_for_sleep(GDBusConnection*, const gchar*, const gchar*, const gchar*, const gchar*, GVariant*, gpointer); - - GCancellable * m_cancellable = nullptr; - GDBusConnection * m_system_bus = nullptr; - unsigned int m_sleep_subscription_id = 0; + class Impl; + friend class Impl; + std::unique_ptr<Impl> m_impl; // we've got raw pointers and GSignal tags in here, so disable copying Clock(const Clock&) =delete; diff --git a/src/clock.cpp b/src/clock.cpp index f41a0cc..748174b 100644 --- a/src/clock.cpp +++ b/src/clock.cpp @@ -22,6 +22,11 @@ #include <glib.h> #include <gio/gio.h> +#include <map> +#include <set> +#include <string> +#include <vector> + namespace unity { namespace indicator { namespace datetime { @@ -30,58 +35,109 @@ namespace datetime { **** ***/ -Clock::Clock(): - m_cancellable(g_cancellable_new()) +class Clock::Impl { - g_bus_get(G_BUS_TYPE_SYSTEM, m_cancellable, on_system_bus_ready, this); -} +public: -Clock::~Clock() -{ - g_cancellable_cancel(m_cancellable); - g_clear_object(&m_cancellable); + Impl(Clock& owner): + m_owner(owner) + { + auto tag = g_bus_watch_name(G_BUS_TYPE_SYSTEM, + "org.freedesktop.login1", + G_BUS_NAME_WATCHER_FLAGS_NONE, + on_login1_appeared, + on_login1_vanished, + this, nullptr); + m_watched_names.insert(tag); + } - if (m_sleep_subscription_id) - g_dbus_connection_signal_unsubscribe(m_system_bus, m_sleep_subscription_id); - g_clear_object(&m_system_bus); -} + ~Impl() + { + for(const auto& tag : m_watched_names) + g_bus_unwatch_name(tag); + } -void -Clock::on_system_bus_ready(GObject*, GAsyncResult * res, gpointer gself) -{ - GDBusConnection * system_bus; +private: + + void remember_subscription(const std::string& name, + GDBusConnection* bus, + guint tag) + { + auto subscription = std::shared_ptr<GDBusConnection>( + G_DBUS_CONNECTION(g_object_ref(bus)), + [tag](GDBusConnection* bus){ + g_dbus_connection_signal_unsubscribe(bus, tag); + g_object_unref(G_OBJECT(bus)); + } + ); - if ((system_bus = g_bus_get_finish(res, nullptr))) + m_subscriptions[name].push_back(subscription); + } + + /** + *** 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 self = static_cast<Clock*>(gself); - - self->m_system_bus = system_bus; - - self->m_sleep_subscription_id = g_dbus_connection_signal_subscribe( - system_bus, - nullptr, - "org.freedesktop.login1.Manager", // interface - "PrepareForSleep", // signal name - "/org/freedesktop/login1", // object path - nullptr, // arg0 - G_DBUS_SIGNAL_FLAGS_NONE, - on_prepare_for_sleep, - self, - nullptr); + 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<Impl*>(gself)->remember_subscription(name, bus, tag); } + + static void on_login1_vanished(GDBusConnection * /*system_bus*/, + const gchar * name, + gpointer gself) + { + static_cast<Impl*>(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) + { + static_cast<Impl*>(gself)->m_owner.minute_changed(); + } + + /*** + **** + ***/ + + Clock& m_owner; + std::set<guint> m_watched_names; + std::map<std::string,std::vector<std::shared_ptr<GDBusConnection>>> m_subscriptions; +}; + +/*** +**** +***/ + +Clock::Clock(): + m_impl(new Impl{*this}) +{ } -void -Clock::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) +Clock::~Clock() { - static_cast<Clock*>(gself)->minute_changed(); } /*** diff --git a/tests/test-clock.cpp b/tests/test-clock.cpp index a4924b3..82f8cb0 100644 --- a/tests/test-clock.cpp +++ b/tests/test-clock.cpp @@ -32,18 +32,6 @@ class ClockFixture: public TestDBusFixture { private: typedef TestDBusFixture super; - - public: - void emitPrepareForSleep() - { - g_dbus_connection_emit_signal(g_bus_get_sync(G_BUS_TYPE_SYSTEM, nullptr, nullptr), - nullptr, - "/org/freedesktop/login1", // object path - "org.freedesktop.login1.Manager", // interface - "PrepareForSleep", // signal name - g_variant_new("(b)", FALSE), - nullptr); - } }; TEST_F(ClockFixture, MinuteChangedSignalShouldTriggerOncePerMinute) @@ -113,6 +101,27 @@ TEST_F(ClockFixture, TimezoneChangeTriggersSkew) g_time_zone_unref(tz_la); } +/*** +**** +***/ + +namespace +{ + void on_login1_name_acquired(GDBusConnection * connection, + const gchar * /*name*/, + gpointer /*user_data*/) + { + g_dbus_connection_emit_signal(connection, + nullptr, + "/org/freedesktop/login1", // object path + "org.freedesktop.login1.Manager", // interface + "PrepareForSleep", // signal name + g_variant_new("(b)", FALSE), + nullptr); + } +} + + /** * Confirm that a "PrepareForSleep" event wil trigger a skew event */ @@ -121,7 +130,7 @@ TEST_F(ClockFixture, SleepTriggersSkew) std::shared_ptr<Timezones> zones(new Timezones); zones->timezone.set("America/New_York"); LiveClock clock(zones); - wait_msec(500); // wait for the bus to set up + wait_msec(250); // wait for the bus to set up bool skewed = false; clock.minute_changed.connect([&skewed, this](){ @@ -130,11 +139,16 @@ TEST_F(ClockFixture, SleepTriggersSkew) return G_SOURCE_REMOVE; }); - g_idle_add([](gpointer gself){ - static_cast<ClockFixture*>(gself)->emitPrepareForSleep(); - return G_SOURCE_REMOVE; - }, this); - + auto name_tag = g_bus_own_name(G_BUS_TYPE_SYSTEM, + "org.freedesktop.login1", + G_BUS_NAME_OWNER_FLAGS_NONE, + nullptr /* bus acquired */, + on_login1_name_acquired, + nullptr /* name lost */, + nullptr /* user_data */, + nullptr /* user_data closure */); g_main_loop_run(loop); EXPECT_TRUE(skewed); + + g_bus_unown_name(name_tag); } |