From fcd77b806a8826d5f694f78c63943d0f768ef6ec Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Sat, 26 Jul 2014 23:35:38 -0500 Subject: refactor the Notifications / sound / awake code --- include/CMakeLists.txt | 1 + include/datetime/dbus-shared.h | 14 +--- include/datetime/snap.h | 12 ++-- include/notifications/CMakeLists.txt | 2 + include/notifications/awake.h | 55 ++++++++++++++++ include/notifications/dbus-shared.h | 32 ++++++++++ include/notifications/notifications.h | 116 ++++++++++++++++++++++++++++++++++ include/notifications/sound.h | 60 ++++++++++++++++++ 8 files changed, 274 insertions(+), 18 deletions(-) create mode 100644 include/notifications/CMakeLists.txt create mode 100644 include/notifications/awake.h create mode 100644 include/notifications/dbus-shared.h create mode 100644 include/notifications/notifications.h create mode 100644 include/notifications/sound.h (limited to 'include') diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt index 486e9c7..15a7c33 100644 --- a/include/CMakeLists.txt +++ b/include/CMakeLists.txt @@ -1 +1,2 @@ add_subdirectory(datetime) +add_subdirectory(notifications) diff --git a/include/datetime/dbus-shared.h b/include/datetime/dbus-shared.h index 4b71ce5..db10c1d 100644 --- a/include/datetime/dbus-shared.h +++ b/include/datetime/dbus-shared.h @@ -18,18 +18,10 @@ * Charles Kerr */ -#ifndef _DBUS_SHARED_H_ -#define _DBUS_SHARED_H_ +#ifndef _INDICATOR_DATETIME_DBUS_SHARED_H_ +#define _INDICATOR_DATETIME_DBUS_SHARED_H_ #define BUS_DATETIME_NAME "com.canonical.indicator.datetime" #define BUS_DATETIME_PATH "/com/canonical/indicator/datetime" -#define BUS_SCREEN_NAME "com.canonical.Unity.Screen" -#define BUS_SCREEN_PATH "/com/canonical/Unity/Screen" -#define BUS_SCREEN_INTERFACE "com.canonical.Unity.Screen" - -#define BUS_POWERD_NAME "com.canonical.powerd" -#define BUS_POWERD_PATH "/com/canonical/powerd" -#define BUS_POWERD_INTERFACE "com.canonical.powerd" - -#endif /* _DBUS_SHARED_H_ */ +#endif /* _INDICATOR_DATETIME_DBUS_SHARED_H_ */ diff --git a/include/datetime/snap.h b/include/datetime/snap.h index 1c90496..78d9f65 100644 --- a/include/datetime/snap.h +++ b/include/datetime/snap.h @@ -21,9 +21,10 @@ #define INDICATOR_DATETIME_SNAP_H #include -#include #include +#include + #include #include #include @@ -38,7 +39,7 @@ namespace datetime { class Snap { public: - Snap(const std::shared_ptr& clock, + Snap(const std::shared_ptr& engine, const std::shared_ptr& settings); virtual ~Snap(); @@ -48,12 +49,9 @@ public: appointment_func dismiss); private: - const std::shared_ptr m_clock; + const std::shared_ptr m_engine; const std::shared_ptr m_settings; - - class Popup; - friend class Popup; - std::set m_pending; + std::set m_notifications; }; } // namespace datetime diff --git a/include/notifications/CMakeLists.txt b/include/notifications/CMakeLists.txt new file mode 100644 index 0000000..139597f --- /dev/null +++ b/include/notifications/CMakeLists.txt @@ -0,0 +1,2 @@ + + diff --git a/include/notifications/awake.h b/include/notifications/awake.h new file mode 100644 index 0000000..fd812c1 --- /dev/null +++ b/include/notifications/awake.h @@ -0,0 +1,55 @@ +/* + * Copyright 2014 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 + */ + +#ifndef UNITY_INDICATOR_NOTIFICATIONS_AWAKE_H +#define UNITY_INDICATOR_NOTIFICATIONS_AWAKE_H + +#include + +namespace unity { +namespace indicator { +namespace notifications { + +/*** +**** +***/ + +/** + * A class that forces the screen display on and inhibits sleep + */ +class Awake +{ +public: + Awake(const std::string& app_name); + ~Awake(); + +private: + class Impl; + std::unique_ptr impl; +}; + +/*** +**** +***/ + +} // namespace notifications +} // namespace indicator +} // namespace unity + +#endif // UNITY_INDICATOR_NOTIFICATIONS_AWAKE_H diff --git a/include/notifications/dbus-shared.h b/include/notifications/dbus-shared.h new file mode 100644 index 0000000..7738cb7 --- /dev/null +++ b/include/notifications/dbus-shared.h @@ -0,0 +1,32 @@ +/* + * 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: + * Ted Gould + * Charles Kerr + */ + +#ifndef UNITY_INDICATOR_NOTIFICATIONS_DBUS_SHARED_H +#define UNITY_INDICATOR_NOTIFICATIONS_DBUS_SHARED_H + +#define BUS_SCREEN_NAME "com.canonical.Unity.Screen" +#define BUS_SCREEN_PATH "/com/canonical/Unity/Screen" +#define BUS_SCREEN_INTERFACE "com.canonical.Unity.Screen" + +#define BUS_POWERD_NAME "com.canonical.powerd" +#define BUS_POWERD_PATH "/com/canonical/powerd" +#define BUS_POWERD_INTERFACE "com.canonical.powerd" + +#endif /* INDICATOR_NOTIFICATIONS_DBUS_SHARED_H */ diff --git a/include/notifications/notifications.h b/include/notifications/notifications.h new file mode 100644 index 0000000..b4c88b4 --- /dev/null +++ b/include/notifications/notifications.h @@ -0,0 +1,116 @@ +/* + * Copyright 2014 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 + */ + +#ifndef UNITY_INDICATOR_NOTIFICATIONS_NOTIFICATIONS_H +#define UNITY_INDICATOR_NOTIFICATIONS_NOTIFICATIONS_H + +#include +#include +#include +#include + +namespace unity { +namespace indicator { +namespace notifications { + +class Engine; + +/** + * Helper class for showing notifications. + * + * Populate the builder, with the relevant properites, + * then pass it to Engine::show(). + * + * @see Engine::show(Builder) + */ +class Builder +{ +public: + Builder(); + ~Builder(); + + void set_title (const std::string& title); + void set_body (const std::string& body); + void set_icon_name (const std::string& icon_name); + + /* Set an interval, after which the notification will automatically + be closed. If not set, the notification server's default timeout + is used. */ + void set_timeout (const std::chrono::seconds& duration); + + /* Add a notification hint. + These keys may be dependent on the notification server. */ + void add_hint (const std::string& name); + static constexpr char const * HINT_SNAP {"x-canonical-snap-decisions"}; + static constexpr char const * HINT_TINT {"x-canonical-private-button-tint"}; + + /* Add an action button. + This may fail if the Engine doesn't support actions. + @see Engine::supports_actions() */ + void add_action (const std::string& action, const std::string& label); + + /** Sets the closed callback. This will be called exactly once. */ + void set_closed_callback (std::function); + +private: + friend class Engine; + class Impl; + std::unique_ptr impl; +}; + +/** + * Manages Notifications and the connection to the notification server. + * + * When this class is destroyed, any remaining notifications it created + * will be closed and their closed() callbacks will be invoked. + */ +class Engine +{ +public: + Engine(const std::string& app_name); + ~Engine(); + + /** @see Builder::set_action() */ + bool supports_actions() const; + + /** Show a notification. + @return nonzero on success, zero on failure. */ + int show(const Builder& builder); + + /** Close a notification. + @param key the int returned by show() + @return true if the notification was closed. */ + bool close(int key); + + /** Close all remaining notifications. + *@return true if all closed successfully. */ + bool close_all(); + + const std::string& app_name() const; + +private: + class Impl; + std::unique_ptr impl; +}; + +} // namespace notifications +} // namespace indicator +} // namespace unity + +#endif // UNITY_INDICATOR_NOTIFICATIONS_NOTIFICATIONS_H diff --git a/include/notifications/sound.h b/include/notifications/sound.h new file mode 100644 index 0000000..f5f549c --- /dev/null +++ b/include/notifications/sound.h @@ -0,0 +1,60 @@ +/* + * Copyright 2014 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 + */ + +#ifndef UNITY_INDICATOR_NOTIFICATIONS_SOUND_H +#define UNITY_INDICATOR_NOTIFICATIONS_SOUND_H + +#include +#include + +namespace unity { +namespace indicator { +namespace notifications { + +/*** +**** +***/ + +/** + * Plays a sound, possibly looping. + * + * @param uri the file to play + * @param volume the volume at which to play the sound, [0..100] + * @param loop if true, loop the sound for the lifespan of the object + */ +class Sound +{ +public: + Sound(const std::string& uri, unsigned int volume, bool loop); + ~Sound(); + +private: + class Impl; + std::unique_ptr impl; +}; + +/*** +**** +***/ + +} // namespace notifications +} // namespace indicator +} // namespace unity + +#endif // UNITY_INDICATOR_NOTIFICATIONS_SOUND_H -- cgit v1.2.3 From 7271b2139a5c600a2c3cdb4e552e05ddb0f374dd Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Sun, 27 Jul 2014 10:51:54 -0500 Subject: make close return void instead of bool, because after all what more can you do if the call fails? What's the point? --- include/notifications/notifications.h | 4 ++-- src/notifications.cpp | 26 ++++++++------------------ 2 files changed, 10 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/include/notifications/notifications.h b/include/notifications/notifications.h index b4c88b4..43442d3 100644 --- a/include/notifications/notifications.h +++ b/include/notifications/notifications.h @@ -96,11 +96,11 @@ public: /** Close a notification. @param key the int returned by show() @return true if the notification was closed. */ - bool close(int key); + void close(int key); /** Close all remaining notifications. *@return true if all closed successfully. */ - bool close_all(); + void close_all(); const std::string& app_name() const; diff --git a/src/notifications.cpp b/src/notifications.cpp index 486a910..6d993df 100644 --- a/src/notifications.cpp +++ b/src/notifications.cpp @@ -154,42 +154,32 @@ public: return m_caps.count("actions") != 0; } - bool close_all () + void close_all () { - bool all_closed = true; - // close() removes the item from m_notifications, // so increment the iterator before it gets invalidated for (auto it=m_notifications.begin(), e=m_notifications.end(); it!=e; ) { const int key = it->first; ++it; - if (!close (key)) - all_closed = false; + close (key); } - - return all_closed; } - bool close (int key) + void close (int key) { - bool is_closed = true; - // if we've got this one... auto it = m_notifications.find(key); if (it != m_notifications.end()) { GError * error = nullptr; - is_closed = notify_notification_close (it->second.nn.get(), &error); - if (!is_closed) + if (!notify_notification_close (it->second.nn.get(), &error)) { g_warning ("Unable to close notification %d: %s", key, error->message); g_error_free (error); } on_closed (key); } - - return is_closed; } int show (const Builder& builder) @@ -339,16 +329,16 @@ Engine::show(const Builder& builder) return impl->show(builder); } -bool +void Engine::close_all() { - return impl->close_all(); + impl->close_all(); } -bool +void Engine::close(int key) { - return impl->close(key); + impl->close(key); } const std::string& -- cgit v1.2.3 From b0936139bfef6fe169b5c17be4b2dafa3c2e2c3a Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Sun, 27 Jul 2014 11:01:30 -0500 Subject: copyediting: comments, use anonymous namespace --- include/notifications/notifications.h | 5 +++-- src/awake.cpp | 14 ++++++++++---- src/notifications.cpp | 27 +++++++++++++++++---------- src/snap.cpp | 11 ++++++++--- src/sound.cpp | 25 ++++--------------------- 5 files changed, 42 insertions(+), 40 deletions(-) (limited to 'include') diff --git a/include/notifications/notifications.h b/include/notifications/notifications.h index 43442d3..c2e2d85 100644 --- a/include/notifications/notifications.h +++ b/include/notifications/notifications.h @@ -34,8 +34,7 @@ class Engine; /** * Helper class for showing notifications. * - * Populate the builder, with the relevant properites, - * then pass it to Engine::show(). + * Populate the builder, then pass it to Engine::show(). * * @see Engine::show(Builder) */ @@ -46,7 +45,9 @@ public: ~Builder(); void set_title (const std::string& title); + void set_body (const std::string& body); + void set_icon_name (const std::string& icon_name); /* Set an interval, after which the notification will automatically diff --git a/src/awake.cpp b/src/awake.cpp index 19826ae..57358ab 100644 --- a/src/awake.cpp +++ b/src/awake.cpp @@ -58,7 +58,9 @@ public: private: - static void on_system_bus_ready (GObject *, GAsyncResult *res, gpointer gself) + static void on_system_bus_ready (GObject *, + GAsyncResult *res, + gpointer gself) { GError * error; GDBusConnection * system_bus; @@ -119,7 +121,9 @@ private: GVariant * args; error = nullptr; - args = g_dbus_connection_call_finish (G_DBUS_CONNECTION(connection), res, &error); + 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) && @@ -150,7 +154,9 @@ private: GVariant * args; error = nullptr; - args = g_dbus_connection_call_finish (G_DBUS_CONNECTION(connection), res, &error); + 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) && @@ -233,7 +239,7 @@ private: ***/ Awake::Awake(const std::string& app_name): - impl(new Impl (app_name)) + impl(new Impl (app_name)) { } diff --git a/src/notifications.cpp b/src/notifications.cpp index 6d993df..bad4b1a 100644 --- a/src/notifications.cpp +++ b/src/notifications.cpp @@ -168,10 +168,11 @@ public: void close (int key) { - // if we've got this one... auto it = m_notifications.find(key); if (it != m_notifications.end()) { + // tell the server to close, call the close() callback, + // and immediately forget about the nn. GError * error = nullptr; if (!notify_notification_close (it->second.nn.get(), &error)) { @@ -187,9 +188,9 @@ public: int ret = -1; const auto& info = *builder.impl; - auto * nn = notify_notification_new (info.m_title.c_str(), - info.m_body.c_str(), - info.m_icon_name.c_str()); + auto nn = notify_notification_new (info.m_title.c_str(), + info.m_body.c_str(), + info.m_icon_name.c_str()); if (info.m_duration.count() != 0) { @@ -243,7 +244,9 @@ public: } else { - g_critical ("Unable to show notification for '%s': %s", info.m_title.c_str(), error->message); + g_critical ("Unable to show notification for '%s': %s", + info.m_title.c_str(), + error->message); g_error_free (error); g_object_unref (nn); } @@ -266,8 +269,8 @@ private: static void on_notification_closed (NotifyNotification * nn, gpointer gself) { const GQuark q = notification_key_quark(); - const int key = GPOINTER_TO_INT(g_object_get_qdata(G_OBJECT(nn), q)); - static_cast(gself)->on_closed (key); + const gpointer gkey = g_object_get_qdata(G_OBJECT(nn), q); + static_cast(gself)->on_closed(GPOINTER_TO_INT(gkey)); } void on_closed (int key) @@ -281,8 +284,8 @@ private: { std::string action; - const auto q = notification_action_quark(); - const auto p = g_object_get_qdata (G_OBJECT(nn), q); + const GQuark q = notification_action_quark(); + const gpointer p = g_object_get_qdata(G_OBJECT(nn), q); if (p != nullptr) action = static_cast(p); @@ -298,7 +301,11 @@ private: ***/ const std::string m_app_name; + + // key-to-data std::map m_notifications; + + // server capabilities std::set m_caps; static constexpr char const * HINT_TIMEOUT {"x-canonical-snap-decisions-timeout"}; @@ -320,7 +327,7 @@ Engine::~Engine() bool Engine::supports_actions() const { - return impl->supports_actions(); + return true;//impl->supports_actions(); } int diff --git a/src/snap.cpp b/src/snap.cpp index d99e5ef..e9df256 100644 --- a/src/snap.cpp +++ b/src/snap.cpp @@ -39,8 +39,11 @@ namespace datetime { **** ***/ -static std::string get_alarm_uri(const Appointment& appointment, - const std::shared_ptr& settings) +namespace // unnamed namespace +{ + +std::string get_alarm_uri(const Appointment& appointment, + const std::shared_ptr& settings) { const char* FALLBACK {"/usr/share/sounds/ubuntu/ringtones/Suru arpeggio.ogg"}; @@ -72,6 +75,8 @@ static std::string get_alarm_uri(const Appointment& appointment, return uri; } +} // unnamed namespace + /*** **** ***/ @@ -102,7 +107,7 @@ void Snap::operator()(const Appointment& appointment, // force the system to stay awake auto awake = std::make_shared(m_engine->app_name()); - // create the sound..., + // create the sound... const auto uri = get_alarm_uri(appointment, m_settings); const auto volume = m_settings->alarm_volume.get(); const bool loop = m_engine->supports_actions(); diff --git a/src/sound.cpp b/src/sound.cpp index 052b168..7658658 100644 --- a/src/sound.cpp +++ b/src/sound.cpp @@ -63,13 +63,14 @@ public: gst_object_unref(bus); g_debug("Playing '%s'", m_uri.c_str()); - play(); + g_object_set(G_OBJECT (m_play), "uri", m_uri.c_str(), + "volume", get_volume(), + nullptr); + gst_element_set_state (m_play, GST_STATE_PLAYING); } ~Impl() { - stop(); - g_source_remove(m_watch_source); if (m_play != nullptr) @@ -81,24 +82,6 @@ public: private: - void stop() - { - if (m_play != nullptr) - { - gst_element_set_state (m_play, GST_STATE_PAUSED); - } - } - - void play() - { - g_return_if_fail(m_play != nullptr); - - g_object_set(G_OBJECT (m_play), "uri", m_uri.c_str(), - "volume", get_volume(), - nullptr); - gst_element_set_state (m_play, GST_STATE_PLAYING); - } - // convert settings range [1..100] to gst playbin's range is [0...1.0] gdouble get_volume() const { -- cgit v1.2.3 From d6b290fda978379fb07285aaddfeb31686735667 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 30 Jul 2014 14:54:37 -0500 Subject: move Snap's guts into an Impl class --- include/datetime/snap.h | 6 +- src/snap.cpp | 185 +++++++++++++++++++++++++++--------------------- 2 files changed, 107 insertions(+), 84 deletions(-) (limited to 'include') diff --git a/include/datetime/snap.h b/include/datetime/snap.h index 78d9f65..ef5c868 100644 --- a/include/datetime/snap.h +++ b/include/datetime/snap.h @@ -27,7 +27,6 @@ #include #include -#include namespace unity { namespace indicator { @@ -49,9 +48,8 @@ public: appointment_func dismiss); private: - const std::shared_ptr m_engine; - const std::shared_ptr m_settings; - std::set m_notifications; + class Impl; + std::unique_ptr impl; }; } // namespace datetime diff --git a/src/snap.cpp b/src/snap.cpp index e9df256..0eb176c 100644 --- a/src/snap.cpp +++ b/src/snap.cpp @@ -27,6 +27,7 @@ #include #include +#include #include namespace uin = unity::indicator::notifications; @@ -39,114 +40,138 @@ namespace datetime { **** ***/ -namespace // unnamed namespace +class Snap::Impl { +public: -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 }; + Impl(const std::shared_ptr& engine, + const std::shared_ptr& settings): + m_engine(engine), + m_settings(settings) + { + } - std::string uri; + ~Impl() + { + for (const auto& key : m_notifications) + m_engine->close (key); + } - for(const auto& candidate : candidates) + void operator()(const Appointment& appointment, + appointment_func show, + appointment_func dismiss) { - if (gst_uri_is_valid (candidate.c_str())) + if (!appointment.has_alarms) { - uri = candidate; - break; + dismiss(appointment); + return; + } + + // force the system to stay awake + auto awake = std::make_shared(m_engine->app_name()); + + // create the sound... + const auto uri = get_alarm_uri(appointment, m_settings); + const auto volume = m_settings->alarm_volume.get(); + const bool loop = m_engine->supports_actions(); + auto sound = std::make_shared(uri, volume, loop); + + // show a notification... + const auto minutes = std::chrono::minutes(m_settings->alarm_duration.get()); + const bool interactive = m_engine->supports_actions(); + uin::Builder b; + b.set_body (appointment.summary); + b.set_icon_name ("alarm-clock"); + b.add_hint (uin::Builder::HINT_SNAP); + b.add_hint (uin::Builder::HINT_TINT); + const auto timestr = appointment.begin.format (_("%a, %X")); + auto title = g_strdup_printf (_("Alarm %s"), timestr.c_str()); + b.set_title (title); + g_free (title); + b.set_timeout (std::chrono::duration_cast(minutes)); + if (interactive) { + b.add_action ("show", _("Show")); + b.add_action ("dismiss", _("Dismiss")); } - else if (g_file_test(candidate.c_str(), G_FILE_TEST_EXISTS)) + + // add the 'sound' and 'awake' objects to the capture so that + // they stay alive until the closed callback is called; i.e., + // for the lifespan of the notficiation + b.set_closed_callback([appointment, show, dismiss, sound, awake] + (const std::string& action){ + if (action == "show") + show(appointment); + else + dismiss(appointment); + }); + + const auto key = m_engine->show(b); + if (key) + m_notifications.insert (key); + else + show(appointment); + } + +private: + + std::string get_alarm_uri(const Appointment& appointment, + const std::shared_ptr& settings) const + { + 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) { - gchar* tmp = gst_filename_to_uri(candidate.c_str(), nullptr); - if (tmp != nullptr) + if (gst_uri_is_valid (candidate.c_str())) { - uri = tmp; - g_free (tmp); + 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; -} + return uri; + } -} // unnamed namespace + const std::shared_ptr m_engine; + const std::shared_ptr m_settings; + std::set m_notifications; +}; /*** **** ***/ -Snap::Snap(const std::shared_ptr& engine, +Snap::Snap(const std::shared_ptr& engine, const std::shared_ptr& settings): - m_engine(engine), - m_settings(settings) + impl(new Impl(engine, settings)) { } Snap::~Snap() { - for (const auto& key : m_notifications) - m_engine->close (key); } -void Snap::operator()(const Appointment& appointment, - appointment_func show, - appointment_func dismiss) +void +Snap::operator()(const Appointment& appointment, + appointment_func show, + appointment_func dismiss) { - if (!appointment.has_alarms) - { - dismiss(appointment); - return; - } - - // force the system to stay awake - auto awake = std::make_shared(m_engine->app_name()); - - // create the sound... - const auto uri = get_alarm_uri(appointment, m_settings); - const auto volume = m_settings->alarm_volume.get(); - const bool loop = m_engine->supports_actions(); - auto sound = std::make_shared(uri, volume, loop); - - // show a notification... - const auto minutes = std::chrono::minutes(m_settings->alarm_duration.get()); - const bool interactive = m_engine->supports_actions(); - uin::Builder b; - b.set_body (appointment.summary); - b.set_icon_name ("alarm-clock"); - b.add_hint (uin::Builder::HINT_SNAP); - b.add_hint (uin::Builder::HINT_TINT); - const auto timestr = appointment.begin.format (_("%a, %X")); - auto title = g_strdup_printf (_("Alarm %s"), timestr.c_str()); - b.set_title (title); - g_free (title); - b.set_timeout (std::chrono::duration_cast(minutes)); - if (interactive) { - b.add_action ("show", _("Show")); - b.add_action ("dismiss", _("Dismiss")); - } - - // add the 'sound' and 'awake' objects to the capture so that - // they stay alive until the closed callback is called; i.e., - // for the lifespan of the notficiation - b.set_closed_callback([appointment, show, dismiss, sound, awake] - (const std::string& action){ - if (action == "show") - show(appointment); - else - dismiss(appointment); - }); - - const auto key = m_engine->show(b); - if (key) - m_notifications.insert (key); - else - show(appointment); + (*impl)(appointment, show, dismiss); } /*** -- cgit v1.2.3 From 559d185dd7d51e56fbd8246970ef520d3edd18ae Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 30 Jul 2014 16:01:03 -0500 Subject: initial draft of haptic feedback when alarms play --- CMakeLists.txt | 3 +- debian/control | 1 + include/notifications/haptic.h | 57 ++++++++++++++++++++++++++ src/CMakeLists.txt | 1 + src/haptic.cpp | 92 ++++++++++++++++++++++++++++++++++++++++++ src/notifications.cpp | 2 +- src/snap.cpp | 8 +++- 7 files changed, 160 insertions(+), 4 deletions(-) create mode 100644 include/notifications/haptic.h create mode 100644 src/haptic.cpp (limited to 'include') diff --git a/CMakeLists.txt b/CMakeLists.txt index 9b4987e..3bc7e81 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,7 +41,8 @@ pkg_check_modules (SERVICE_DEPS REQUIRED gstreamer-1.0>=1.2 libnotify>=0.7.6 url-dispatcher-1>=1 - properties-cpp>=0.0.1) + properties-cpp>=0.0.1 + ubuntu-platform-api>=2.2.0) include_directories (SYSTEM ${SERVICE_DEPS_INCLUDE_DIRS}) CHECK_INCLUDE_FILE(ubuntu/hardware/alarm.h HAVE_UBUNTU_HW_ALARM_H) diff --git a/debian/control b/debian/control index 77e9241..dab9096 100644 --- a/debian/control +++ b/debian/control @@ -21,6 +21,7 @@ Build-Depends: cmake, libproperties-cpp-dev, libubuntu-platform-hardware-api-headers [armhf i386 amd64], libubuntu-platform-hardware-api-dev [armhf i386 amd64], + libubuntu-application-api-dev [armhf i386 amd64], libdbustest1-dev, locales, Standards-Version: 3.9.3 diff --git a/include/notifications/haptic.h b/include/notifications/haptic.h new file mode 100644 index 0000000..3036485 --- /dev/null +++ b/include/notifications/haptic.h @@ -0,0 +1,57 @@ +/* + * Copyright 2014 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 + */ + +#ifndef UNITY_INDICATOR_NOTIFICATIONS_HAPTIC_H +#define UNITY_INDICATOR_NOTIFICATIONS_HAPTIC_H + +#include + +namespace unity { +namespace indicator { +namespace notifications { + +/*** +**** +***/ + +/** + * A class that forces the screen display on and inhibits sleep + */ +class Haptic +{ +public: + enum Mode { VIBRATE_DEFAULT }; + + Haptic(const Mode& mode = VIBRATE_DEFAULT); + ~Haptic(); + +private: + class Impl; + std::unique_ptr impl; +}; + +/*** +**** +***/ + +} // namespace notifications +} // namespace indicator +} // namespace unity + +#endif // UNITY_INDICATOR_NOTIFICATIONS_HAPTIC_H diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 754d537..a466a48 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -20,6 +20,7 @@ set (SERVICE_CXX_SOURCES exporter.cpp formatter.cpp formatter-desktop.cpp + haptic.cpp locations.cpp locations-settings.cpp menu.cpp diff --git a/src/haptic.cpp b/src/haptic.cpp new file mode 100644 index 0000000..c5a20df --- /dev/null +++ b/src/haptic.cpp @@ -0,0 +1,92 @@ +/* + * Copyright 2014 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 + +namespace unity { +namespace indicator { +namespace notifications { + +/*** +**** +***/ + +class Haptic::Impl +{ +public: + + Impl(const Mode& mode): + m_mode(mode), + m_sensor(ua_sensors_haptic_new()) + { + if (m_sensor == nullptr) + g_warning ("Haptic device unavailable"); + else + m_tag = g_timeout_add_seconds (1, on_timeout, this); + } + + ~Impl() + { + if (m_tag) + g_source_remove(m_tag); + } + +private: + + static gboolean on_timeout (gpointer gself) + { + static_cast(gself)->vibrate_now(); + return G_SOURCE_CONTINUE; + } + + void vibrate_now() + { + const uint32_t msec = 1500; + ua_sensors_haptic_vibrate_once (m_sensor, msec); + } + + const Mode m_mode; + UASensorsHaptic * m_sensor = nullptr; + guint m_tag = 0; +}; + +/*** +**** +***/ + +Haptic::Haptic(const Mode& mode): + impl(new Impl (mode)) +{ +} + +Haptic::~Haptic() +{ +} + +/*** +**** +***/ + +} // namespace datetime +} // namespace indicator +} // namespace unity diff --git a/src/notifications.cpp b/src/notifications.cpp index da7351b..c66f634 100644 --- a/src/notifications.cpp +++ b/src/notifications.cpp @@ -327,7 +327,7 @@ Engine::~Engine() bool Engine::supports_actions() const { - return impl->supports_actions(); + return true; //impl->supports_actions(); } int diff --git a/src/snap.cpp b/src/snap.cpp index 0eb176c..f3e0f20 100644 --- a/src/snap.cpp +++ b/src/snap.cpp @@ -20,6 +20,7 @@ #include #include +#include #include #include @@ -76,6 +77,9 @@ public: const bool loop = m_engine->supports_actions(); auto sound = std::make_shared(uri, volume, loop); + // create the haptic feedback... + auto haptic = std::make_shared(); + // show a notification... const auto minutes = std::chrono::minutes(m_settings->alarm_duration.get()); const bool interactive = m_engine->supports_actions(); @@ -94,10 +98,10 @@ public: b.add_action ("dismiss", _("Dismiss")); } - // add the 'sound' and 'awake' objects to the capture so that + // add 'sound', 'haptic', and 'awake' objects to the capture so // they stay alive until the closed callback is called; i.e., // for the lifespan of the notficiation - b.set_closed_callback([appointment, show, dismiss, sound, awake] + b.set_closed_callback([appointment, show, dismiss, sound, awake, haptic] (const std::string& action){ if (action == "show") show(appointment); -- cgit v1.2.3 From 5d6108ee4440881b08b419a1c454b1db57e1d6ce Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 30 Jul 2014 17:03:08 -0500 Subject: fix doxygen comments --- include/notifications/notifications.h | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/notifications/notifications.h b/include/notifications/notifications.h index c2e2d85..d876a57 100644 --- a/include/notifications/notifications.h +++ b/include/notifications/notifications.h @@ -91,16 +91,14 @@ public: bool supports_actions() const; /** Show a notification. - @return nonzero on success, zero on failure. */ + @return zero on failure, or a key that can be passed to close() */ int show(const Builder& builder); /** Close a notification. - @param key the int returned by show() - @return true if the notification was closed. */ + @param key the int returned by show() */ void close(int key); - /** Close all remaining notifications. - *@return true if all closed successfully. */ + /** Close all remaining notifications. */ void close_all(); const std::string& app_name() const; -- cgit v1.2.3 From 9d32289feda6d72e53bcf7e77e2cbef09f88165e Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 31 Jul 2014 10:33:14 -0500 Subject: in indicator::noficiations::Haptic, better naming of the Mode enum --- include/notifications/haptic.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/notifications/haptic.h b/include/notifications/haptic.h index 3036485..d82d1c9 100644 --- a/include/notifications/haptic.h +++ b/include/notifications/haptic.h @@ -36,9 +36,12 @@ namespace notifications { class Haptic { public: - enum Mode { VIBRATE_DEFAULT }; + enum Mode + { + MODE_PULSE + }; - Haptic(const Mode& mode = VIBRATE_DEFAULT); + Haptic(const Mode& mode = MODE_PULSE); ~Haptic(); private: -- cgit v1.2.3 From af1b645de9b8116ead5ad72583f51afe28350818 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 31 Jul 2014 15:22:47 -0500 Subject: drop the ubuntu-application-api middleman and call usensorsd directly: ua_sensors_haptic_new() crashes on desktop and ua_sensors_haptic_vibrate_once() makes blocking dbus calls. --- CMakeLists.txt | 3 +- debian/control | 1 - include/notifications/dbus-shared.h | 4 ++ src/haptic.cpp | 80 ++++++++++++++++++++++++++----------- 4 files changed, 62 insertions(+), 26 deletions(-) (limited to 'include') diff --git a/CMakeLists.txt b/CMakeLists.txt index 3bc7e81..9b4987e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,8 +41,7 @@ pkg_check_modules (SERVICE_DEPS REQUIRED gstreamer-1.0>=1.2 libnotify>=0.7.6 url-dispatcher-1>=1 - properties-cpp>=0.0.1 - ubuntu-platform-api>=2.2.0) + properties-cpp>=0.0.1) include_directories (SYSTEM ${SERVICE_DEPS_INCLUDE_DIRS}) CHECK_INCLUDE_FILE(ubuntu/hardware/alarm.h HAVE_UBUNTU_HW_ALARM_H) diff --git a/debian/control b/debian/control index 15b981a..9e7133e 100644 --- a/debian/control +++ b/debian/control @@ -24,7 +24,6 @@ Build-Depends: cmake, libproperties-cpp-dev, libubuntu-platform-hardware-api-headers [armhf i386 amd64], libubuntu-platform-hardware-api-dev [armhf i386 amd64], - libubuntu-application-api-dev [armhf i386 amd64], libdbustest1-dev, locales, Standards-Version: 3.9.3 diff --git a/include/notifications/dbus-shared.h b/include/notifications/dbus-shared.h index 7738cb7..af714e7 100644 --- a/include/notifications/dbus-shared.h +++ b/include/notifications/dbus-shared.h @@ -29,4 +29,8 @@ #define BUS_POWERD_PATH "/com/canonical/powerd" #define BUS_POWERD_INTERFACE "com.canonical.powerd" +#define BUS_HAPTIC_NAME "com.canonical.usensord" +#define BUS_HAPTIC_PATH "/com/canonical/usensord/haptic" +#define BUS_HAPTIC_INTERFACE "com.canonical.usensord.haptic" + #endif /* INDICATOR_NOTIFICATIONS_DBUS_SHARED_H */ diff --git a/src/haptic.cpp b/src/haptic.cpp index 90c75f7..2b1ee21 100644 --- a/src/haptic.cpp +++ b/src/haptic.cpp @@ -17,11 +17,10 @@ * Charles Kerr */ +#include #include -#include - -#include +#include namespace unity { namespace indicator { @@ -37,34 +36,58 @@ public: Impl(const Mode& mode): m_mode(mode), - m_sensor(ua_sensors_haptic_new()) + m_cancellable(g_cancellable_new()) { - if (m_sensor == nullptr) - { - g_warning ("Haptic device unavailable"); - } - else - { - /* We only support one vibrate mode for now: an on/off pulse at - one-second intervals. So, set a timer to go off every 2 seconds - that vibrates for one second. */ - ua_sensors_haptic_enable(m_sensor); - m_tag = g_timeout_add_seconds (2, on_timeout, this); - on_timeout (this); - } + g_bus_get (G_BUS_TYPE_SESSION, m_cancellable, on_bus_ready, this); } ~Impl() { - if (m_sensor != nullptr) - ua_sensors_haptic_disable(m_sensor); - if (m_tag) g_source_remove(m_tag); + + g_cancellable_cancel (m_cancellable); + g_object_unref (m_cancellable); + + g_clear_object (&m_bus); } private: + static void on_bus_ready (GObject*, GAsyncResult* res, gpointer gself) + { + GError * error; + GDBusConnection * bus; + + error = nullptr; + 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 (bus != nullptr) + { + auto self = static_cast(gself); + + self->m_bus = G_DBUS_CONNECTION (g_object_ref (bus)); + self->start_vibrating(); + + g_object_unref (bus); + } + } + + void start_vibrating() + { + /* We only support one vibrate mode for now: an on/off pulse at + one-second intervals. So set a looping 2-second timer that asks + the phone to vibrate for one second. */ + m_tag = g_timeout_add_seconds (2, on_timeout, this); + on_timeout (this); + } + static gboolean on_timeout (gpointer gself) { static_cast(gself)->vibrate_now(); @@ -73,12 +96,23 @@ private: void vibrate_now() { - const uint32_t msec = 1000; - (void) ua_sensors_haptic_vibrate_once (m_sensor, msec); + g_dbus_connection_call (m_bus, + BUS_HAPTIC_NAME, + BUS_HAPTIC_PATH, + BUS_HAPTIC_INTERFACE, + "Vibrate", + g_variant_new("(u)", 1000u), + nullptr, + G_DBUS_CALL_FLAGS_NONE, + -1, + m_cancellable, + nullptr, + nullptr); } const Mode m_mode; - UASensorsHaptic * m_sensor = nullptr; + GCancellable * m_cancellable = nullptr; + GDBusConnection * m_bus = nullptr; guint m_tag = 0; }; -- cgit v1.2.3 From 4148750ea93e67e5d9aaa15ebc8e3c7a5a5554f1 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 31 Jul 2014 16:48:00 -0500 Subject: configurable haptic mode, part 1 of 3: add haptic feedback mode to the GSettings schema and to our Settings object --- data/com.canonical.indicator.datetime.gschema.xml.in | 8 ++++++++ include/datetime/settings-live.h | 1 + include/datetime/settings-shared.h | 1 + include/datetime/settings.h | 1 + src/settings-live.cpp | 14 ++++++++++++++ tests/test-settings.cpp | 1 + 6 files changed, 26 insertions(+) (limited to 'include') diff --git a/data/com.canonical.indicator.datetime.gschema.xml.in b/data/com.canonical.indicator.datetime.gschema.xml.in index 62b42c1..4e4acd8 100644 --- a/data/com.canonical.indicator.datetime.gschema.xml.in +++ b/data/com.canonical.indicator.datetime.gschema.xml.in @@ -123,6 +123,14 @@ Some timezones can be known by many different cities or names. This setting describes how the current zone prefers to be named. Format is "TIMEZONE NAME" (e.g. "America/New_York Boston" to name the New_York zone Boston). + + 'pulse' + <_summary>What kind of haptic feedback, if any, to trigger with an alarm. + <_description> + What kind of haptic feedback, if any, to trigger with an alarm. + Two modes are currently supported: 'pulse', 'none'. + + '/usr/share/sounds/ubuntu/ringtones/Suru arpeggio.ogg' <_summary>The alarm's default sound file. diff --git a/include/datetime/settings-live.h b/include/datetime/settings-live.h index 4db2d40..4850e69 100644 --- a/include/datetime/settings-live.h +++ b/include/datetime/settings-live.h @@ -58,6 +58,7 @@ private: void update_alarm_sound(); void update_alarm_volume(); void update_alarm_duration(); + void update_alarm_haptic(); GSettings* m_settings; diff --git a/include/datetime/settings-shared.h b/include/datetime/settings-shared.h index 23d2e1c..a211821 100644 --- a/include/datetime/settings-shared.h +++ b/include/datetime/settings-shared.h @@ -48,5 +48,6 @@ TimeFormatMode; #define SETTINGS_ALARM_SOUND_S "alarm-default-sound" #define SETTINGS_ALARM_VOLUME_S "alarm-default-volume" #define SETTINGS_ALARM_DURATION_S "alarm-duration-minutes" +#define SETTINGS_ALARM_HAPTIC_S "alarm-haptic-feedback" #endif // INDICATOR_DATETIME_SETTINGS_SHARED diff --git a/include/datetime/settings.h b/include/datetime/settings.h index e5f885e..c6fe13b 100644 --- a/include/datetime/settings.h +++ b/include/datetime/settings.h @@ -57,6 +57,7 @@ public: core::Property time_format_mode; core::Property timezone_name; core::Property alarm_sound; + core::Property alarm_haptic; core::Property alarm_volume; core::Property alarm_duration; }; diff --git a/src/settings-live.cpp b/src/settings-live.cpp index 71bbd96..a8338ed 100644 --- a/src/settings-live.cpp +++ b/src/settings-live.cpp @@ -55,6 +55,7 @@ LiveSettings::LiveSettings(): update_alarm_sound(); update_alarm_volume(); update_alarm_duration(); + update_alarm_haptic(); // now listen for clients to change the properties s.t. we can sync update GSettings @@ -130,6 +131,10 @@ LiveSettings::LiveSettings(): alarm_duration.changed().connect([this](unsigned int value){ g_settings_set_uint(m_settings, SETTINGS_ALARM_DURATION_S, value); }); + + alarm_haptic.changed().connect([this](const std::string& value){ + g_settings_set_string(m_settings, SETTINGS_ALARM_HAPTIC_S, value.c_str()); + }); } /*** @@ -237,6 +242,13 @@ void LiveSettings::update_alarm_duration() alarm_duration.set(g_settings_get_uint(m_settings, SETTINGS_ALARM_DURATION_S)); } +void LiveSettings::update_alarm_haptic() +{ + auto val = g_settings_get_string(m_settings, SETTINGS_ALARM_HAPTIC_S); + alarm_haptic.set(val); + g_free(val); +} + /*** **** ***/ @@ -284,6 +296,8 @@ void LiveSettings::update_key(const std::string& key) update_alarm_volume(); else if (key == SETTINGS_ALARM_DURATION_S) update_alarm_duration(); + else if (key == SETTINGS_ALARM_HAPTIC_S) + update_alarm_haptic(); } /*** diff --git a/tests/test-settings.cpp b/tests/test-settings.cpp index 44a0252..4fb0a08 100644 --- a/tests/test-settings.cpp +++ b/tests/test-settings.cpp @@ -159,6 +159,7 @@ TEST_F(SettingsFixture, StringProperties) TestStringProperty(m_settings->custom_time_format, SETTINGS_CUSTOM_TIME_FORMAT_S); TestStringProperty(m_settings->timezone_name, SETTINGS_TIMEZONE_NAME_S); TestStringProperty(m_settings->alarm_sound, SETTINGS_ALARM_SOUND_S); + TestStringProperty(m_settings->alarm_haptic, SETTINGS_ALARM_HAPTIC_S); } TEST_F(SettingsFixture, TimeFormatMode) -- cgit v1.2.3 From d129112ad9ce04fad979af95b5b5cd4fd87f5b2b Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 31 Jul 2014 19:52:42 -0500 Subject: in Haptic, make the looping logic easier to read. --- include/notifications/haptic.h | 2 +- src/haptic.cpp | 51 ++++++++++++++++++++++-------------------- 2 files changed, 28 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/include/notifications/haptic.h b/include/notifications/haptic.h index d82d1c9..bfb5679 100644 --- a/include/notifications/haptic.h +++ b/include/notifications/haptic.h @@ -31,7 +31,7 @@ namespace notifications { ***/ /** - * A class that forces the screen display on and inhibits sleep + * Tries to emit haptic feedback to match the user-specified mode. */ class Haptic { diff --git a/src/haptic.cpp b/src/haptic.cpp index a27c334..54a7d49 100644 --- a/src/haptic.cpp +++ b/src/haptic.cpp @@ -22,6 +22,7 @@ #include +#include #include namespace unity { @@ -88,60 +89,62 @@ private: switch (m_mode) { case MODE_PULSE: // the only mode currently supported... :) + // one second on, one second off. - m_vibrate_pattern_msec = std::vector({1000, 1000}); + m_pattern = std::vector({1000u, 1000u}); break; } - // Set up a loop so that the pattern keeps repeating. - // NB: VibratePattern takes a repeat arg, but we avoid it because - // there's no way to cancel a pattern once it's started... - // The phone would keep vibrating long after the alarm was dismissed! - // So we stick to a short pattern and handle the repeat loop manually. - guint interval_msec = 0; - for (const auto& msec : m_vibrate_pattern_msec) - interval_msec += msec; - m_tag = g_timeout_add (interval_msec, on_timeout, this); - on_timeout (this); + // Set up a loop to keep repeating the pattern + auto msec = std::accumulate(m_pattern.begin(), m_pattern.end(), 0u); + m_tag = g_timeout_add(msec, call_vibrate_pattern_static, this); + call_vibrate_pattern(); } - static gboolean on_timeout (gpointer gself) + static gboolean call_vibrate_pattern_static (gpointer gself) { - auto self = static_cast(gself); + static_cast(gself)->call_vibrate_pattern(); + return G_SOURCE_CONTINUE; + } - // build the pattern array of uint32s + void call_vibrate_pattern() + { + // build the vibrate pattern GVariantBuilder builder; g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY); - for (const auto& msec : self->m_vibrate_pattern_msec) + for (const auto& msec : m_pattern) g_variant_builder_add_value (&builder, g_variant_new_uint32(msec)); auto pattern_array = g_variant_builder_end (&builder); - // build the argument list + /* Use a repeat_count of 1 here because we handle looping ourselves. + NB: VibratePattern could do it for us, but doesn't let us cancel + a running loop -- we could keep vibrating even after "this" was + destructed */ + auto repeat_count = g_variant_new_uint32 (1u); + g_variant_builder_init (&builder, G_VARIANT_TYPE_TUPLE); g_variant_builder_add_value (&builder, pattern_array); - g_variant_builder_add_value (&builder, g_variant_new_uint32(1u)); - auto args = g_variant_builder_end (&builder); + g_variant_builder_add_value (&builder, repeat_count); + auto vibrate_pattern_args = g_variant_builder_end (&builder); - g_dbus_connection_call (self->m_bus, + g_dbus_connection_call (m_bus, BUS_HAPTIC_NAME, BUS_HAPTIC_PATH, BUS_HAPTIC_INTERFACE, "VibratePattern", - args, + vibrate_pattern_args, nullptr, G_DBUS_CALL_FLAGS_NONE, -1, - self->m_cancellable, + m_cancellable, nullptr, nullptr); - - return G_SOURCE_CONTINUE; } const Mode m_mode; GCancellable * m_cancellable = nullptr; GDBusConnection * m_bus = nullptr; - std::vector m_vibrate_pattern_msec; + std::vector m_pattern; guint m_tag = 0; }; -- cgit v1.2.3 From 6da58726eb18e7205845ec1af4cb43d08d988e31 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Fri, 1 Aug 2014 11:42:43 -0500 Subject: refactor changes based on Antti's feedback --- include/notifications/CMakeLists.txt | 2 -- src/notifications.cpp | 27 ++++++++++++++------------- 2 files changed, 14 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/include/notifications/CMakeLists.txt b/include/notifications/CMakeLists.txt index 139597f..e69de29 100644 --- a/include/notifications/CMakeLists.txt +++ b/include/notifications/CMakeLists.txt @@ -1,2 +0,0 @@ - - diff --git a/src/notifications.cpp b/src/notifications.cpp index da7351b..dfb1dc6 100644 --- a/src/notifications.cpp +++ b/src/notifications.cpp @@ -156,14 +156,14 @@ public: void close_all () { - // close() removes the item from m_notifications, - // so increment the iterator before it gets invalidated - for (auto it=m_notifications.begin(), e=m_notifications.end(); it!=e; ) - { - const int key = it->first; - ++it; - close (key); - } + // call close() on all our keys + + std::set keys; + for (const auto& it : m_notifications) + keys.insert (it.first); + + for (const int key : keys) + close (key); } void close (int key) @@ -171,15 +171,16 @@ public: auto it = m_notifications.find(key); if (it != m_notifications.end()) { - // tell the server to close, call the close() callback, - // and immediately forget about the nn. + // tell the server to close the notification GError * error = nullptr; if (!notify_notification_close (it->second.nn.get(), &error)) { g_warning ("Unable to close notification %d: %s", key, error->message); g_error_free (error); } - on_closed (key); + + // call the user callback and remove it from our bookkeeping + remove_closed_notification (key); } } @@ -270,10 +271,10 @@ private: { const GQuark q = notification_key_quark(); const gpointer gkey = g_object_get_qdata(G_OBJECT(nn), q); - static_cast(gself)->on_closed(GPOINTER_TO_INT(gkey)); + static_cast(gself)->remove_closed_notification(GPOINTER_TO_INT(gkey)); } - void on_closed (int key) + void remove_closed_notification (int key) { auto it = m_notifications.find(key); g_return_if_fail (it != m_notifications.end()); -- cgit v1.2.3