From 2206a15709b43f27e553ded0c29e3dd369d1377d Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 23 Jul 2014 17:59:22 -0500 Subject: when an alarm is being shown, inhibit sleep and force the screen on. --- src/snap.cpp | 189 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 187 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/snap.cpp b/src/snap.cpp index 45eb14e..a601cae 100644 --- a/src/snap.cpp +++ b/src/snap.cpp @@ -45,6 +45,8 @@ namespace datetime { namespace { +static constexpr char const * APP_NAME = {"indicator-datetime-service"}; + /** * Plays a sound, possibly looping. */ @@ -214,13 +216,29 @@ public: Popup(const Appointment& appointment, const SoundBuilder& sound_builder): m_appointment(appointment), m_interactive(get_interactive()), - m_sound_builder(sound_builder) + m_sound_builder(sound_builder), + m_cancellable(g_cancellable_new()) { + g_bus_get (G_BUS_TYPE_SYSTEM, m_cancellable, on_system_bus_ready, this); + show(); } ~Popup() { + if (m_cancellable != nullptr) + { + g_cancellable_cancel (m_cancellable); + g_clear_object (&m_cancellable); + } + + if (m_system_bus != nullptr) + { + unforce_awake (); + unforce_screen (); + g_clear_object (&m_system_bus); + } + if (m_nn != nullptr) { notify_notification_clear_actions(m_nn); @@ -361,6 +379,169 @@ private: **** ***/ + static void on_system_bus_ready (GObject *, GAsyncResult *res, gpointer gself) + { + GError * error; + GDBusConnection * system_bus; + + error = nullptr; + system_bus = g_bus_get_finish (res, &error); + if (error != nullptr) + { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("Unable to get bus: %s", error->message); + + g_error_free (error); + } + else if (system_bus != nullptr) + { + auto self = static_cast(gself); + + self->m_system_bus = G_DBUS_CONNECTION (g_object_ref (system_bus)); + + // ask powerd to keep the system awake + static constexpr int32_t POWERD_SYS_STATE_ACTIVE = 1; + g_dbus_connection_call (system_bus, + "com.canonical.powerd", + "/com/canonical/powerd", + "com.canonical.powerd", + "requestSysState", + g_variant_new("(si)", APP_NAME, POWERD_SYS_STATE_ACTIVE), + G_VARIANT_TYPE("(s)"), + G_DBUS_CALL_FLAGS_NONE, + -1, + self->m_cancellable, + on_force_awake_response, + self); + + // ask unity-system-compositor to turn on the screen + g_dbus_connection_call (system_bus, + "com.canonical.Unity.Screen", + "/com/canonical/Unity/Screen", + "com.canonical.Unity.Screen", + "keepDisplayOn", + nullptr, + G_VARIANT_TYPE("(i)"), + G_DBUS_CALL_FLAGS_NONE, + -1, + self->m_cancellable, + on_force_screen_response, + self); + + g_object_unref (system_bus); + } + } + + static void on_force_awake_response (GObject * connection, + GAsyncResult * res, + gpointer gself) + { + GError * error; + GVariant * args; + + error = nullptr; + args = g_dbus_connection_call_finish (G_DBUS_CONNECTION(connection), res, &error); + if (error != nullptr) + { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("Unable to inhibit sleep: %s", error->message); + + g_error_free (error); + } + else + { + auto self = static_cast(gself); + + g_message ("%s response is '%s'", G_STRFUNC, g_variant_print (args, true)); + + g_clear_pointer (&self->m_awake_cookie, g_free); + g_variant_get (args, "(s)", &self->m_awake_cookie); + g_message ("m_awake_cookie is now '%s'", self->m_awake_cookie); + + g_variant_unref (args); + } + } + + static void on_force_screen_response (GObject * connection, + GAsyncResult * res, + gpointer gself) + { + GError * error; + GVariant * args; + + error = nullptr; + args = g_dbus_connection_call_finish (G_DBUS_CONNECTION(connection), res, &error); + if (error != nullptr) + { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("Unable to turn on the screen: %s", error->message); + + g_error_free (error); + } + else + { + auto self = static_cast(gself); + + g_message ("%s response is '%s'", G_STRFUNC, g_variant_print (args, true)); + + g_clear_pointer (&self->m_screen_cookie, g_free); + g_variant_get (args, "(i)", &self->m_screen_cookie); + g_message ("m_screen_cookie is now '%d'", self->m_screen_cookie); + + g_variant_unref (args); + } + } + + void unforce_awake () + { + g_return_if_fail (G_IS_DBUS_CONNECTION(m_system_bus)); + + if (m_awake_cookie != nullptr) + { + g_dbus_connection_call (m_system_bus, + "com.canonical.powerd", + "/com/canonical/powerd", + "com.canonical.powerd", + "clearSysState", + g_variant_new("(s)", m_awake_cookie), + nullptr, + G_DBUS_CALL_FLAGS_NONE, + -1, + nullptr, + nullptr, + nullptr); + + g_clear_pointer (&m_awake_cookie, g_free); + } + } + + void unforce_screen () + { + g_return_if_fail (G_IS_DBUS_CONNECTION(m_system_bus)); + + if (m_screen_cookie != 0) + { + g_dbus_connection_call (m_system_bus, + "com.canonical.Unity.Screen", + "/com/canonical/Unity/Screen", + "com.canonical.Unity.Screen", + "removeDisplayOnRequest", + g_variant_new("(i)", m_screen_cookie), + nullptr, + G_DBUS_CALL_FLAGS_NONE, + -1, + nullptr, + nullptr, + nullptr); + + m_screen_cookie = 0; + } + } + + /*** + **** + ***/ + typedef Popup Self; const Appointment m_appointment; @@ -370,6 +551,10 @@ private: core::Signal m_response; Response m_response_value = RESPONSE_CLOSE; NotifyNotification* m_nn = nullptr; + GCancellable * m_cancellable = nullptr; + GDBusConnection * m_system_bus = nullptr; + char * m_awake_cookie = nullptr; + int32_t m_screen_cookie = 0; static constexpr char const * HINT_SNAP {"x-canonical-snap-decisions"}; static constexpr char const * HINT_TINT {"x-canonical-private-button-tint"}; @@ -426,7 +611,7 @@ Snap::Snap(const std::shared_ptr& clock, m_clock(clock), m_settings(settings) { - if (!n_existing_snaps++ && !notify_init("indicator-datetime-service")) + if (!n_existing_snaps++ && !notify_init(APP_NAME)) g_critical("libnotify initialization failed"); } -- cgit v1.2.3 From 5235e6046a6a1c924cd2d39edbef0fd8de2a21f8 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 23 Jul 2014 18:58:42 -0500 Subject: handle Screen.keepDisplayOn() returning a cookie whose value is '0' --- src/snap.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/snap.cpp b/src/snap.cpp index a601cae..cf9cbd8 100644 --- a/src/snap.cpp +++ b/src/snap.cpp @@ -30,6 +30,7 @@ #include #include +#include #include // std::call_once() #include #include @@ -484,7 +485,7 @@ private: g_message ("%s response is '%s'", G_STRFUNC, g_variant_print (args, true)); - g_clear_pointer (&self->m_screen_cookie, g_free); + self->m_screen_cookie = NO_SCREEN_COOKIE; g_variant_get (args, "(i)", &self->m_screen_cookie); g_message ("m_screen_cookie is now '%d'", self->m_screen_cookie); @@ -498,6 +499,7 @@ private: if (m_awake_cookie != nullptr) { +g_message ("%s calling clearSysState %s", G_STRFUNC, m_awake_cookie); g_dbus_connection_call (m_system_bus, "com.canonical.powerd", "/com/canonical/powerd", @@ -519,8 +521,9 @@ private: { g_return_if_fail (G_IS_DBUS_CONNECTION(m_system_bus)); - if (m_screen_cookie != 0) + if (m_screen_cookie != NO_SCREEN_COOKIE) { +g_message ("%s calling removeDisplayOnRequest %d", G_STRFUNC, (int)m_screen_cookie); g_dbus_connection_call (m_system_bus, "com.canonical.Unity.Screen", "/com/canonical/Unity/Screen", @@ -534,7 +537,7 @@ private: nullptr, nullptr); - m_screen_cookie = 0; + m_screen_cookie = NO_SCREEN_COOKIE; } } @@ -554,7 +557,9 @@ private: GCancellable * m_cancellable = nullptr; GDBusConnection * m_system_bus = nullptr; char * m_awake_cookie = nullptr; - int32_t m_screen_cookie = 0; + int32_t m_screen_cookie = NO_SCREEN_COOKIE; + + static constexpr int32_t NO_SCREEN_COOKIE { std::numeric_limits::min() }; static constexpr char const * HINT_SNAP {"x-canonical-snap-decisions"}; static constexpr char const * HINT_TINT {"x-canonical-private-button-tint"}; -- cgit v1.2.3 From e94fa94e3d8d3d7a9630d3e6fa94b78525ae4345 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 23 Jul 2014 19:12:14 -0500 Subject: remove tracer g_messages() that were used during development --- src/snap.cpp | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/snap.cpp b/src/snap.cpp index cf9cbd8..5971c68 100644 --- a/src/snap.cpp +++ b/src/snap.cpp @@ -453,11 +453,9 @@ private: { auto self = static_cast(gself); - g_message ("%s response is '%s'", G_STRFUNC, g_variant_print (args, true)); - g_clear_pointer (&self->m_awake_cookie, g_free); g_variant_get (args, "(s)", &self->m_awake_cookie); - g_message ("m_awake_cookie is now '%s'", self->m_awake_cookie); + g_debug ("m_awake_cookie is now '%s'", self->m_awake_cookie); g_variant_unref (args); } @@ -483,11 +481,9 @@ private: { auto self = static_cast(gself); - g_message ("%s response is '%s'", G_STRFUNC, g_variant_print (args, true)); - self->m_screen_cookie = NO_SCREEN_COOKIE; g_variant_get (args, "(i)", &self->m_screen_cookie); - g_message ("m_screen_cookie is now '%d'", self->m_screen_cookie); + g_debug ("m_screen_cookie is now '%d'", self->m_screen_cookie); g_variant_unref (args); } @@ -499,7 +495,6 @@ private: if (m_awake_cookie != nullptr) { -g_message ("%s calling clearSysState %s", G_STRFUNC, m_awake_cookie); g_dbus_connection_call (m_system_bus, "com.canonical.powerd", "/com/canonical/powerd", @@ -523,7 +518,6 @@ g_message ("%s calling clearSysState %s", G_STRFUNC, m_awake_cookie); if (m_screen_cookie != NO_SCREEN_COOKIE) { -g_message ("%s calling removeDisplayOnRequest %d", G_STRFUNC, (int)m_screen_cookie); g_dbus_connection_call (m_system_bus, "com.canonical.Unity.Screen", "/com/canonical/Unity/Screen", @@ -650,8 +644,8 @@ void Snap::operator()(const Appointment& appointment, dismiss, popup](Popup::Response response){ - // we can't delete the Popup inside its response() signal handler, - // so push that to an idle func + // we can't delete the Popup inside its response() signal handler + // because core::signal deadlocks, so push that to an idle func g_idle_add([](gpointer gdata){ delete static_cast(gdata); return G_SOURCE_REMOVE; -- cgit v1.2.3 From 909ccbc93d9fa21075cf5001887e69159e621f5d Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 23 Jul 2014 22:56:36 -0500 Subject: move the powerd and screen bus name, path, and interface strings into dbus-shared --- src/exporter.cpp | 8 ++++---- src/snap.cpp | 25 +++++++++++++------------ 2 files changed, 17 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/exporter.cpp b/src/exporter.cpp index e2b60f2..88aee2f 100644 --- a/src/exporter.cpp +++ b/src/exporter.cpp @@ -72,7 +72,7 @@ public: m_actions = actions; m_menus = menus; m_own_id = g_bus_own_name(G_BUS_TYPE_SESSION, - BUS_NAME, + BUS_DATETIME_NAME, G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT, on_bus_acquired, nullptr, @@ -166,12 +166,12 @@ private: GError * error = nullptr; g_dbus_interface_skeleton_export(G_DBUS_INTERFACE_SKELETON(m_alarm_props), m_bus, - BUS_PATH"/AlarmProperties", + BUS_DATETIME_PATH"/AlarmProperties", &error); // export the actions const auto id = g_dbus_connection_export_action_group(m_bus, - BUS_PATH, + BUS_DATETIME_PATH, m_actions->action_group(), &error); if (id) @@ -187,7 +187,7 @@ private: // export the menus for(auto& menu : m_menus) { - const auto path = std::string(BUS_PATH) + "/" + menu->name(); + const auto path = std::string(BUS_DATETIME_PATH) + "/" + menu->name(); const auto id = g_dbus_connection_export_menu_model(m_bus, path.c_str(), menu->menu_model(), &error); if (id) { diff --git a/src/snap.cpp b/src/snap.cpp index 5971c68..566dfae 100644 --- a/src/snap.cpp +++ b/src/snap.cpp @@ -18,6 +18,7 @@ */ #include +#include #include #include @@ -403,9 +404,9 @@ private: // ask powerd to keep the system awake static constexpr int32_t POWERD_SYS_STATE_ACTIVE = 1; g_dbus_connection_call (system_bus, - "com.canonical.powerd", - "/com/canonical/powerd", - "com.canonical.powerd", + BUS_POWERD_NAME, + BUS_POWERD_PATH, + BUS_POWERD_INTERFACE, "requestSysState", g_variant_new("(si)", APP_NAME, POWERD_SYS_STATE_ACTIVE), G_VARIANT_TYPE("(s)"), @@ -417,9 +418,9 @@ private: // ask unity-system-compositor to turn on the screen g_dbus_connection_call (system_bus, - "com.canonical.Unity.Screen", - "/com/canonical/Unity/Screen", - "com.canonical.Unity.Screen", + BUS_SCREEN_NAME, + BUS_SCREEN_PATH, + BUS_SCREEN_INTERFACE, "keepDisplayOn", nullptr, G_VARIANT_TYPE("(i)"), @@ -496,9 +497,9 @@ private: if (m_awake_cookie != nullptr) { g_dbus_connection_call (m_system_bus, - "com.canonical.powerd", - "/com/canonical/powerd", - "com.canonical.powerd", + BUS_POWERD_NAME, + BUS_POWERD_PATH, + BUS_POWERD_INTERFACE, "clearSysState", g_variant_new("(s)", m_awake_cookie), nullptr, @@ -519,9 +520,9 @@ private: if (m_screen_cookie != NO_SCREEN_COOKIE) { g_dbus_connection_call (m_system_bus, - "com.canonical.Unity.Screen", - "/com/canonical/Unity/Screen", - "com.canonical.Unity.Screen", + BUS_SCREEN_NAME, + BUS_SCREEN_PATH, + BUS_SCREEN_INTERFACE, "removeDisplayOnRequest", g_variant_new("(i)", m_screen_cookie), nullptr, -- cgit v1.2.3 From b9b3cfea551b5c7e12e6bb9904cffe2873b328c7 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 24 Jul 2014 01:18:10 -0500 Subject: when a Snap object is destructed, delete any active Popups that it owns. This cleaner shutdown doesn't have any effect in production, but is needed to shut down the bus cleanly in the tests. --- src/snap.cpp | 94 +++++++++++++++++++++++++++++++++--------------------------- 1 file changed, 51 insertions(+), 43 deletions(-) (limited to 'src') diff --git a/src/snap.cpp b/src/snap.cpp index 566dfae..c21a398 100644 --- a/src/snap.cpp +++ b/src/snap.cpp @@ -207,11 +207,52 @@ private: bool m_looping = true; }; +/** +*** libnotify -- snap decisions +**/ + +std::string get_alarm_uri(const Appointment& appointment, + const std::shared_ptr& settings) +{ + const char* FALLBACK {"/usr/share/sounds/ubuntu/ringtones/Suru arpeggio.ogg"}; + + const std::string candidates[] = { appointment.audio_url, + settings->alarm_sound.get(), + FALLBACK }; + + std::string uri; + + for(const auto& candidate : candidates) + { + if (gst_uri_is_valid (candidate.c_str())) + { + uri = candidate; + break; + } + else if (g_file_test(candidate.c_str(), G_FILE_TEST_EXISTS)) + { + gchar* tmp = gst_filename_to_uri(candidate.c_str(), nullptr); + if (tmp != nullptr) + { + uri = tmp; + g_free (tmp); + break; + } + } + } + + return uri; +} + +int32_t n_existing_snaps = 0; + +} // unnamed namespace + /** * A popup notification (with optional sound) * that emits a Response signal when done. */ -class Popup +class Snap::Popup { public: @@ -561,47 +602,6 @@ private: static constexpr char const * HINT_TIMEOUT {"x-canonical-snap-decisions-timeout"}; }; -/** -*** libnotify -- snap decisions -**/ - -std::string get_alarm_uri(const Appointment& appointment, - const std::shared_ptr& settings) -{ - const char* FALLBACK {"/usr/share/sounds/ubuntu/ringtones/Suru arpeggio.ogg"}; - - const std::string candidates[] = { appointment.audio_url, - settings->alarm_sound.get(), - FALLBACK }; - - std::string uri; - - for(const auto& candidate : candidates) - { - if (gst_uri_is_valid (candidate.c_str())) - { - uri = candidate; - break; - } - else if (g_file_test(candidate.c_str(), G_FILE_TEST_EXISTS)) - { - gchar* tmp = gst_filename_to_uri(candidate.c_str(), nullptr); - if (tmp != nullptr) - { - uri = tmp; - g_free (tmp); - break; - } - } - } - - return uri; -} - -int32_t n_existing_snaps = 0; - -} // unnamed namespace - /*** **** ***/ @@ -617,6 +617,9 @@ Snap::Snap(const std::shared_ptr& clock, Snap::~Snap() { + for (auto popup : m_pending) + delete popup; + if (!--n_existing_snaps) notify_uninit(); } @@ -638,12 +641,17 @@ void Snap::operator()(const Appointment& appointment, sound_builder.set_clock(m_clock); sound_builder.set_duration_minutes(m_settings->alarm_duration.get()); auto popup = new Popup(appointment, sound_builder); + + m_pending.insert(popup); // listen for it to finish... - popup->response().connect([appointment, + popup->response().connect([this, + appointment, show, dismiss, popup](Popup::Response response){ + + m_pending.erase(popup); // we can't delete the Popup inside its response() signal handler // because core::signal deadlocks, so push that to an idle func -- cgit v1.2.3