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 --- src/notifications.cpp | 367 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 367 insertions(+) create mode 100644 src/notifications.cpp (limited to 'src/notifications.cpp') diff --git a/src/notifications.cpp b/src/notifications.cpp new file mode 100644 index 0000000..41ced42 --- /dev/null +++ b/src/notifications.cpp @@ -0,0 +1,367 @@ +/* + * 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 +#include +#include +#include + +namespace unity { +namespace indicator { +namespace notifications { + +static G_DEFINE_QUARK(NotificationKey, notification_key) + +static G_DEFINE_QUARK(NotificationAction, notification_action) + +/*** +**** +***/ + +class Builder::Impl +{ +public: + std::string m_title; + std::string m_body; + std::string m_icon_name; + std::chrono::seconds m_duration; + std::set m_string_hints; + std::vector> m_actions; + std::function m_closed_callback; +}; + +Builder::Builder(): + impl(new Impl()) +{ +} + +Builder::~Builder() +{ +} + +void +Builder::set_title (const std::string& title) +{ + impl->m_title = title; +} + +void +Builder::set_body (const std::string& body) +{ + impl->m_body = body; +} + +void +Builder::set_icon_name (const std::string& icon_name) +{ + impl->m_icon_name = icon_name; +} + +void +Builder::set_timeout (const std::chrono::seconds& duration) +{ + impl->m_duration = duration; +} + +void +Builder::add_hint (const std::string& name) +{ + impl->m_string_hints.insert (name); +} + +void +Builder::add_action (const std::string& action, const std::string& label) +{ + impl->m_actions.push_back(std::pair(action,label)); +} + +void +Builder::set_closed_callback (std::function cb) +{ + impl->m_closed_callback.swap (cb); +} + +/*** +**** +***/ + +class Engine::Impl +{ + struct notification_data + { + std::shared_ptr nn; + std::function closed_callback; + }; + +public: + + Impl(const std::string& app_name): + m_app_name(app_name) + { + if (!notify_init(app_name.c_str())) + g_critical("Unable to initialize libnotify!"); + + // put the server capabilities into m_caps + auto caps_gl = notify_get_server_caps(); + std::string caps_str; + for(auto l=caps_gl; l!=nullptr; l=l->next) + { + m_caps.insert((const char*)l->data); + + caps_str += (const char*) l->data;; + if (l->next != nullptr) + caps_str += ", "; + } + + g_debug("%s notify_get_server() returned [%s]", G_STRFUNC, caps_str.c_str()); + g_list_free_full(caps_gl, g_free); + } + + ~Impl() + { + close_all (); + + notify_uninit (); + } + + const std::string& app_name() const + { + return m_app_name; + } + + bool supports_actions() const + { + return m_caps.count("actions") != 0; + } + + bool 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(), end=m_notifications.end(); it!=end; ) + { + const int key = it->first; + ++it; + if (!close (key)) + all_closed = false; + } + + return all_closed; + } + + bool 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) + { + 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) + { + 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()); + + if (info.m_duration.count() != 0) + { + const auto& d= info.m_duration; + auto ms = std::chrono::duration_cast(d); + + notify_notification_set_hint (nn, + HINT_TIMEOUT, + g_variant_new_int32(ms.count())); + } + + for (const auto& hint : info.m_string_hints) + { + notify_notification_set_hint (nn, + hint.c_str(), + g_variant_new_boolean(true)); + } + + for (const auto& action : info.m_actions) + { + notify_notification_add_action (nn, + action.first.c_str(), + action.second.c_str(), + on_notification_clicked, + nullptr, + nullptr); + } + + // if we can show it, keep it + GError * error = nullptr; + if (notify_notification_show(nn, &error)) + { + static int next_key = 1; + const int key = next_key++; + + g_signal_connect (nn, "closed", + G_CALLBACK(on_notification_closed), this); + g_object_set_qdata (G_OBJECT(nn), + notification_key_quark(), + GINT_TO_POINTER(key)); + + notification_data ndata; + ndata.closed_callback = info.m_closed_callback; + ndata.nn.reset(nn, [this](NotifyNotification * n) { + g_signal_handlers_disconnect_by_data(n, this); + g_object_unref (G_OBJECT(n)); + }); + + m_notifications[key] = ndata; + ret = key; + } + else + { + g_critical ("Unable to show notification for '%s': %s", info.m_title.c_str(), error->message); + g_error_free (error); + g_object_unref (nn); + } + + return ret; + } + +private: + + static void on_notification_clicked (NotifyNotification * nn, + char * action, + gpointer) + { + g_object_set_qdata_full (G_OBJECT(nn), + notification_action_quark(), + g_strdup(action), + g_free); + } + + 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); + } + + void on_closed (int key) + { + auto it = m_notifications.find(key); + g_return_if_fail (it != m_notifications.end()); + + const auto& ndata = it->second; + auto nn = ndata.nn.get(); + if (ndata.closed_callback) + { + std::string action; + + const auto q = notification_action_quark(); + const auto p = g_object_get_qdata (G_OBJECT(nn), q); + if (p != nullptr) + action = static_cast(p); + + ndata.closed_callback (action); + } + + g_signal_handlers_disconnect_by_data(nn, this); + m_notifications.erase(it); + } + + /*** + **** + ***/ + + const std::string m_app_name; + std::map m_notifications; + std::set m_caps; + + static constexpr char const * HINT_TIMEOUT {"x-canonical-snap-decisions-timeout"}; +}; + +/*** +**** +***/ + +Engine::Engine(const std::string& app_name): + impl(new Impl(app_name)) +{ +} + +Engine::~Engine() +{ +} + +bool +Engine::supports_actions() const +{ + return impl->supports_actions(); +} + +int +Engine::show(const Builder& builder) +{ + return impl->show(builder); +} + +bool +Engine::close_all() +{ + return impl->close_all(); +} + +bool +Engine::close(int key) +{ + return impl->close(key); +} + +const std::string& +Engine::app_name() const +{ + return impl->app_name(); +} + +/*** +**** +***/ + +} // namespace notifications +} // namespace indicator +} // namespace unity + -- cgit v1.2.3 From a1e8addae51d3f8a026630b47b0c42c11cd52507 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Sun, 27 Jul 2014 00:08:29 -0500 Subject: copyediting --- src/notifications.cpp | 2 +- src/snap.cpp | 29 +++++++++++++---------------- 2 files changed, 14 insertions(+), 17 deletions(-) (limited to 'src/notifications.cpp') diff --git a/src/notifications.cpp b/src/notifications.cpp index 41ced42..486a910 100644 --- a/src/notifications.cpp +++ b/src/notifications.cpp @@ -160,7 +160,7 @@ public: // close() removes the item from m_notifications, // so increment the iterator before it gets invalidated - for (auto it=m_notifications.begin(), end=m_notifications.end(); it!=end; ) + for (auto it=m_notifications.begin(), e=m_notifications.end(); it!=e; ) { const int key = it->first; ++it; diff --git a/src/snap.cpp b/src/snap.cpp index 4da4b45..d99e5ef 100644 --- a/src/snap.cpp +++ b/src/snap.cpp @@ -109,38 +109,35 @@ void Snap::operator()(const Appointment& appointment, auto sound = std::make_shared(uri, volume, loop); // show a notification... - const auto minutes = m_settings->alarm_duration.get(); + const auto minutes = std::chrono::minutes(m_settings->alarm_duration.get()); const bool interactive = m_engine->supports_actions(); - uin::Builder notification_builder; - notification_builder.set_body (appointment.summary); - notification_builder.set_icon_name ("alarm-clock"); - notification_builder.add_hint (uin::Builder::HINT_SNAP); - notification_builder.add_hint (uin::Builder::HINT_TINT); + 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()); - notification_builder.set_title (title); + b.set_title (title); g_free (title); - notification_builder.set_timeout (std::chrono::duration_cast(std::chrono::minutes(minutes))); + b.set_timeout (std::chrono::duration_cast(minutes)); if (interactive) { - notification_builder.add_action ("show", _("Show")); - notification_builder.add_action ("dismiss", _("Dismiss")); + 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 - notification_builder.set_closed_callback([appointment, - show, - dismiss, - sound, - awake](const std::string& action){ + 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 (notification_builder); + const auto key = m_engine->show(b); if (key) m_notifications.insert (key); else -- 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 'src/notifications.cpp') 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 'src/notifications.cpp') 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 f6df9730bd512982850b7f234f883eabbd925e7d Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 30 Jul 2014 14:53:10 -0500 Subject: remove testing stub --- src/notifications.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/notifications.cpp') diff --git a/src/notifications.cpp b/src/notifications.cpp index bad4b1a..da7351b 100644 --- a/src/notifications.cpp +++ b/src/notifications.cpp @@ -327,7 +327,7 @@ Engine::~Engine() bool Engine::supports_actions() const { - return true;//impl->supports_actions(); + return impl->supports_actions(); } int -- 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 'src/notifications.cpp') 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 From 91c7ce04f148bbeed31ac65d41a6e20b2c080014 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Fri, 8 Aug 2014 15:24:25 -0500 Subject: in notifications.cpp, register for the 'closed' signal before calling notification_notify(). --- src/notifications.cpp | 51 ++++++++++++++++++++++++--------------------------- 1 file changed, 24 insertions(+), 27 deletions(-) (limited to 'src/notifications.cpp') diff --git a/src/notifications.cpp b/src/notifications.cpp index dfb1dc6..b19fa48 100644 --- a/src/notifications.cpp +++ b/src/notifications.cpp @@ -189,30 +189,36 @@ 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()); + std::shared_ptr nn ( + notify_notification_new(info.m_title.c_str(), + info.m_body.c_str(), + info.m_icon_name.c_str()), + [this](NotifyNotification * n) { + g_signal_handlers_disconnect_by_data(n, this); + g_object_unref (G_OBJECT(n)); + } + ); if (info.m_duration.count() != 0) { const auto& d= info.m_duration; auto ms = std::chrono::duration_cast(d); - notify_notification_set_hint (nn, + notify_notification_set_hint (nn.get(), HINT_TIMEOUT, g_variant_new_int32(ms.count())); } for (const auto& hint : info.m_string_hints) { - notify_notification_set_hint (nn, + notify_notification_set_hint (nn.get(), hint.c_str(), g_variant_new_boolean(true)); } for (const auto& action : info.m_actions) { - notify_notification_add_action (nn, + notify_notification_add_action (nn.get(), action.first.c_str(), action.second.c_str(), on_notification_clicked, @@ -220,27 +226,19 @@ public: nullptr); } - // if we can show it, keep it + static int next_key = 1; + const int key = next_key++; + g_object_set_qdata (G_OBJECT(nn.get()), + notification_key_quark(), + GINT_TO_POINTER(key)); + + m_notifications[key] = { nn, info.m_closed_callback }; + g_signal_connect (nn.get(), "closed", + G_CALLBACK(on_notification_closed), this); + GError * error = nullptr; - if (notify_notification_show(nn, &error)) + if (notify_notification_show(nn.get(), &error)) { - static int next_key = 1; - const int key = next_key++; - - g_signal_connect (nn, "closed", - G_CALLBACK(on_notification_closed), this); - g_object_set_qdata (G_OBJECT(nn), - notification_key_quark(), - GINT_TO_POINTER(key)); - - notification_data ndata; - ndata.closed_callback = info.m_closed_callback; - ndata.nn.reset(nn, [this](NotifyNotification * n) { - g_signal_handlers_disconnect_by_data(n, this); - g_object_unref (G_OBJECT(n)); - }); - - m_notifications[key] = ndata; ret = key; } else @@ -249,7 +247,7 @@ public: info.m_title.c_str(), error->message); g_error_free (error); - g_object_unref (nn); + m_notifications.erase(key); } return ret; @@ -293,7 +291,6 @@ private: ndata.closed_callback (action); } - g_signal_handlers_disconnect_by_data(nn, this); m_notifications.erase(it); } -- cgit v1.2.3 From 9cc6380c1cb0c4e96a893a1b9d2720d2ed3181bd Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Fri, 8 Aug 2014 15:31:49 -0500 Subject: in notifications, don't ask the notification server for its capabilities until we need them. --- src/notifications.cpp | 44 ++++++++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 18 deletions(-) (limited to 'src/notifications.cpp') diff --git a/src/notifications.cpp b/src/notifications.cpp index b19fa48..18f15d9 100644 --- a/src/notifications.cpp +++ b/src/notifications.cpp @@ -120,21 +120,6 @@ public: { if (!notify_init(app_name.c_str())) g_critical("Unable to initialize libnotify!"); - - // put the server capabilities into m_caps - auto caps_gl = notify_get_server_caps(); - std::string caps_str; - for(auto l=caps_gl; l!=nullptr; l=l->next) - { - m_caps.insert((const char*)l->data); - - caps_str += (const char*) l->data;; - if (l->next != nullptr) - caps_str += ", "; - } - - g_debug("%s notify_get_server() returned [%s]", G_STRFUNC, caps_str.c_str()); - g_list_free_full(caps_gl, g_free); } ~Impl() @@ -151,7 +136,7 @@ public: bool supports_actions() const { - return m_caps.count("actions") != 0; + return server_caps().count("actions") != 0; } void close_all () @@ -255,6 +240,28 @@ public: private: + const std::set& server_caps() const + { + if (G_UNLIKELY(m_lazy_caps.empty())) + { + auto caps_gl = notify_get_server_caps(); + std::string caps_str; + for(auto l=caps_gl; l!=nullptr; l=l->next) + { + m_lazy_caps.insert((const char*)l->data); + + caps_str += (const char*) l->data;; + if (l->next != nullptr) + caps_str += ", "; + } + + g_debug("%s notify_get_server() returned [%s]", G_STRFUNC, caps_str.c_str()); + g_list_free_full(caps_gl, g_free); + } + + return m_lazy_caps; + } + static void on_notification_clicked (NotifyNotification * nn, char * action, gpointer) @@ -303,8 +310,9 @@ private: // key-to-data std::map m_notifications; - // server capabilities - std::set m_caps; + // server capabilities. + // as the name indicates, don't use this directly: use server_caps() instead + mutable std::set m_lazy_caps; static constexpr char const * HINT_TIMEOUT {"x-canonical-snap-decisions-timeout"}; }; -- cgit v1.2.3