diff options
-rw-r--r-- | data/com.canonical.indicator.datetime.AlarmProperties.xml | 31 | ||||
-rw-r--r-- | include/datetime/exporter.h | 3 | ||||
-rw-r--r-- | src/CMakeLists.txt | 31 | ||||
-rw-r--r-- | src/exporter.cpp | 115 | ||||
-rw-r--r-- | src/main.cpp | 2 | ||||
-rw-r--r-- | tests/CMakeLists.txt | 2 | ||||
-rw-r--r-- | tests/test-exporter.cpp | 93 |
7 files changed, 261 insertions, 16 deletions
diff --git a/data/com.canonical.indicator.datetime.AlarmProperties.xml b/data/com.canonical.indicator.datetime.AlarmProperties.xml new file mode 100644 index 0000000..d25fa82 --- /dev/null +++ b/data/com.canonical.indicator.datetime.AlarmProperties.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> +<node xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd"> + <interface name="com.canonical.indicator.datetime.AlarmProperties"> + + <property name="DefaultSound" type="s" access="readwrite"> + <doc:doc> + <doc:description> + <doc:para>The default alarm sound's filename.</doc:para> + </doc:description> + </doc:doc> + </property> + + <property name="DefaultVolume" type="i" access="readwrite"> + <doc:doc> + <doc:description> + <doc:para>How loudly to play alarm sounds. [Range: 1-100]</doc:para> + </doc:description> + </doc:doc> + </property> + + <property name="Duration" type="i" access="readwrite"> + <doc:doc> + <doc:description> + <doc:para>How long an alarm's sound should be looped.</doc:para> + </doc:description> + </doc:doc> + </property> + + </interface> +</node> diff --git a/include/datetime/exporter.h b/include/datetime/exporter.h index 8ae70b1..dd57263 100644 --- a/include/datetime/exporter.h +++ b/include/datetime/exporter.h @@ -22,6 +22,7 @@ #include <datetime/actions.h> #include <datetime/menu.h> +#include <datetime/settings.h> #include <core/signal.h> @@ -38,7 +39,7 @@ namespace datetime { class Exporter { public: - Exporter(); + Exporter(const std::shared_ptr<Settings>&); ~Exporter(); core::Signal<>& name_lost(); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ffa1523..af09c71 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,13 +1,13 @@ set (SERVICE_LIB "indicatordatetimeservice") set (SERVICE_EXEC "indicator-datetime-service") -SET (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -g ${CXX_WARNING_ARGS} ${GCOV_FLAGS}") -SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -g ${CXX_WARNING_ARGS} ${GCOV_FLAGS}") - add_definitions (-DTIMEZONE_FILE="/etc/timezone" -DG_LOG_DOMAIN="Indicator-Datetime") -set (SERVICE_SOURCES +# handwritten sources +set (SERVICE_C_SOURCES + utils.c) +set (SERVICE_CXX_SOURCES actions.cpp actions-live.cpp alarm-queue-simple.cpp @@ -32,16 +32,33 @@ set (SERVICE_SOURCES timezones-live.cpp utils.c wakeup-timer-mainloop.cpp) - if (HAVE_UBUNTU_HW_ALARM_H) - set (SERVICE_SOURCES ${SERVICE_SOURCES} wakeup-timer-uha.cpp) + set (SERVICE_CXX_SOURCES ${SERVICE_CXX_SOURCES} wakeup-timer-uha.cpp) endif () -add_library (${SERVICE_LIB} STATIC ${SERVICE_SOURCES}) +# generated sources +include (GdbusCodegen) +set(SERVICE_GENERATED_SOURCES) +add_gdbus_codegen(SERVICE_GENERATED_SOURCES dbus-alarm-properties + com.canonical.indicator + ${CMAKE_SOURCE_DIR}/data/com.canonical.indicator.datetime.AlarmProperties.xml) + +# add warnings/coverage info on handwritten files +# but not the autogenerated ones... +set_source_files_properties(${SERVICE_CXX_SOURCES} + PROPERTIES COMPILE_FLAGS "${CXX_WARNING_ARGS} ${GCOV_FLAGS} -g -std=c++11") +set_source_files_properties(${SERVICE_C_SOURCES} + PROPERTIES COMPILE_FLAGS "${CXX_WARNING_ARGS} ${GCOV_FLAGS} -g -std=c99") + +# add the bin dir to our include path so our code can find the generated header files +include_directories (${CMAKE_CURRENT_BINARY_DIR}) + +add_library (${SERVICE_LIB} STATIC ${SERVICE_C_SOURCES} ${SERVICE_CXX_SOURCES} ${SERVICE_GENERATED_SOURCES}) include_directories (${CMAKE_SOURCE_DIR}) link_directories (${SERVICE_DEPS_LIBRARY_DIRS}) add_executable (${SERVICE_EXEC} main.cpp) +set_source_files_properties(${SERVICE_SOURCES} main.cpp PROPERTIES COMPILE_FLAGS "${CXX_WARNING_ARGS} -g -std=c++11") target_link_libraries (${SERVICE_EXEC} ${SERVICE_LIB} ${SERVICE_DEPS_LIBRARIES} ${GCOV_LIBS}) install (TARGETS ${SERVICE_EXEC} RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_PKGLIBEXECDIR}) diff --git a/src/exporter.cpp b/src/exporter.cpp index a4919e9..3ba95bf 100644 --- a/src/exporter.cpp +++ b/src/exporter.cpp @@ -20,6 +20,8 @@ #include <datetime/dbus-shared.h> #include <datetime/exporter.h> +#include "dbus-alarm-properties.h" + #include <glib/gi18n.h> #include <gio/gio.h> @@ -35,9 +37,11 @@ class Exporter::Impl { public: - Impl (Exporter* owner): - m_owner(owner) + Impl(const std::shared_ptr<Settings>& settings): + m_settings(settings), + m_alarm_props(datetime_alarm_properties_skeleton_new()) { + alarm_properties_init(); } ~Impl() @@ -51,6 +55,9 @@ public: g_dbus_connection_unexport_action_group(m_bus, m_exported_actions_id); } + g_dbus_interface_skeleton_unexport(G_DBUS_INTERFACE_SKELETON(m_alarm_props)); + g_clear_object(&m_alarm_props); + if (m_own_id) g_bus_unown_name(m_own_id); @@ -76,6 +83,95 @@ public: private: + /*** + **** + ***/ + + static void + on_gobject_notify_string(GObject* o, GParamSpec* pspec, gpointer p) + { + gchar* val = nullptr; + g_object_get (o, pspec->name, &val, nullptr); + static_cast<core::Property<std::string>*>(p)->set(val); + g_free(val); + } + void bind_string_property(gpointer o, const char* propname, + core::Property<std::string>& p) + { + // initialize the GObject property from the Settings + g_object_set(o, propname, p.get().c_str(), nullptr); + + // when the GObject changes, update the Settings + const std::string notify_propname = std::string("notify::") + propname; + g_signal_connect(o, notify_propname.c_str(), + G_CALLBACK(on_gobject_notify_string), &p); + + // when the Settings changes, update the GObject + p.changed().connect([o, propname](const std::string& s){ + g_object_set(o, propname, s.c_str(), nullptr); + }); + } + + + static void + on_gobject_notify_int(GObject* o, GParamSpec* pspec, gpointer p) + { + int val = 0; + g_object_get (o, pspec->name, &val, nullptr); + static_cast<core::Property<int>*>(p)->set(val); + } + void bind_int_property(gpointer o, const char* propname, core::Property<int>& p) + { + // initialize the GObject property from the Settings + g_object_set(o, propname, p.get(), nullptr); + + // when the GObject changes, update the Settings + const std::string notify_propname = std::string("notify::") + propname; + g_signal_connect(o, notify_propname.c_str(), + G_CALLBACK(on_gobject_notify_int), &p); + + // when the Settings changes, update the GObject + p.changed().connect([o, propname](int i){ + g_object_set(o, propname, i, nullptr); + }); + } + + + static void + on_gobject_notify_volume(GObject* o, GParamSpec* pspec, gpointer p) + { + int val = 0; + g_object_get (o, pspec->name, &val, nullptr); + static_cast<core::Property<AlarmVolume>*>(p)->set(AlarmVolume(val)); + } + void bind_volume_property(gpointer o, const char* propname, core::Property<AlarmVolume>& p) + { + // initialize the GObject property from the Settings + g_object_set(o, propname, (int)p.get(), nullptr); + + // when the GObject changes, update the Settings + const std::string notify_propname = std::string("notify::") + propname; + g_signal_connect(o, notify_propname.c_str(), + G_CALLBACK(on_gobject_notify_volume), &p); + + // when the Settings changes, update the GObject + p.changed().connect([o, propname](AlarmVolume i){ + g_object_set(o, propname, (int)i, nullptr); + }); + } + + + void alarm_properties_init() + { + bind_int_property(m_alarm_props, "duration", m_settings->alarm_duration); + bind_volume_property(m_alarm_props, "default-volume", m_settings->alarm_volume); + bind_string_property(m_alarm_props, "default-sound", m_settings->alarm_sound); + } + + /*** + **** + ***/ + static void on_bus_acquired(GDBusConnection* connection, const gchar* name, gpointer gthis) @@ -88,8 +184,14 @@ private: { m_bus = static_cast<GDBusConnection*>(g_object_ref(G_OBJECT(bus))); - // export the actions + // export the alarm properties GError * error = nullptr; + g_dbus_interface_skeleton_export(G_DBUS_INTERFACE_SKELETON(m_alarm_props), + m_bus, + BUS_PATH"/AlarmProperties", + &error); + + // export the actions const auto id = g_dbus_connection_export_action_group(m_bus, BUS_PATH, m_actions->action_group(), @@ -136,13 +238,14 @@ private: **** ***/ - Exporter const * m_owner; + std::shared_ptr<Settings> m_settings; std::set<guint> m_exported_menu_ids; guint m_own_id = 0; guint m_exported_actions_id = 0; GDBusConnection* m_bus = nullptr; std::shared_ptr<Actions> m_actions; std::vector<std::shared_ptr<Menu>> m_menus; + DatetimeAlarmProperties* m_alarm_props = nullptr; }; @@ -150,8 +253,8 @@ private: **** ***/ -Exporter::Exporter(): - p(new Impl(this)) +Exporter::Exporter(const std::shared_ptr<Settings>& settings): + p(new Impl(settings)) { } diff --git a/src/main.cpp b/src/main.cpp index 04845ce..cc81cd7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -163,7 +163,7 @@ main(int /*argc*/, char** /*argv*/) // export them & run until we lose the busname auto loop = g_main_loop_new(nullptr, false); - Exporter exporter; + Exporter exporter(state->settings); exporter.name_lost().connect([loop](){ g_message("%s exiting; failed/lost bus ownership", GETTEXT_PACKAGE); g_main_loop_quit(loop); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index fe6d7eb..26b326d 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -27,9 +27,11 @@ add_custom_command (OUTPUT gschemas.compiled # look for headers in our src dir, and also in the directories where we autogenerate files... include_directories (${CMAKE_SOURCE_DIR}/src) +include_directories (${CMAKE_BINARY_DIR}/src) include_directories (${CMAKE_CURRENT_BINARY_DIR}) include_directories (${DBUSTEST_INCLUDE_DIRS}) + add_definitions (-DSANDBOX="${CMAKE_CURRENT_BINARY_DIR}") diff --git a/tests/test-exporter.cpp b/tests/test-exporter.cpp index 4fb2c54..3f502cf 100644 --- a/tests/test-exporter.cpp +++ b/tests/test-exporter.cpp @@ -21,6 +21,8 @@ #include "state-mock.h" #include "glib-fixture.h" +#include "dbus-alarm-properties.h" + #include <datetime/actions.h> #include <datetime/dbus-shared.h> #include <datetime/exporter.h> @@ -76,13 +78,14 @@ TEST_F(ExporterFixture, Publish) { std::shared_ptr<State> state(new MockState); std::shared_ptr<Actions> actions(new MockActions(state)); + std::shared_ptr<Settings> settings(new Settings); std::vector<std::shared_ptr<Menu>> menus; MenuFactory menu_factory (actions, state); for(int i=0; i<Menu::NUM_PROFILES; i++) menus.push_back(menu_factory.buildMenu(Menu::Profile(i))); - Exporter exporter; + Exporter exporter(settings); exporter.publish(actions, menus); wait_msec(); @@ -135,3 +138,91 @@ TEST_F(ExporterFixture, Publish) g_clear_object(&exported); g_clear_object(&connection); } + +TEST_F(ExporterFixture, AlarmProperties) +{ + /*** + **** Set up the exporter + ***/ + + std::shared_ptr<State> state(new MockState); + std::shared_ptr<Actions> actions(new MockActions(state)); + std::shared_ptr<Settings> settings(new Settings); + std::vector<std::shared_ptr<Menu>> menus; + + MenuFactory menu_factory (actions, state); + for(int i=0; i<Menu::NUM_PROFILES; i++) + menus.push_back(menu_factory.buildMenu(Menu::Profile(i))); + + Exporter exporter(settings); + exporter.publish(actions, menus); + wait_msec(); + + /*** + **** Set up the proxy + ***/ + + auto on_proxy_ready = [](GObject*, GAsyncResult* res, gpointer gproxy){ + GError* error = nullptr; + *reinterpret_cast<DatetimeAlarmProperties**>(gproxy) = datetime_alarm_properties_proxy_new_for_bus_finish(res, &error); + EXPECT_TRUE(error == nullptr); + }; + + DatetimeAlarmProperties* proxy = nullptr; + datetime_alarm_properties_proxy_new_for_bus(G_BUS_TYPE_SESSION, + G_DBUS_PROXY_FLAGS_NONE, + BUS_NAME, + BUS_PATH"/AlarmProperties", + nullptr, + on_proxy_ready, + &proxy); + wait_msec(100); + ASSERT_TRUE(proxy != nullptr); + + /*** + **** Try changing the Settings -- do the DBus properties change to match it? + ***/ + + auto expected_volume = ALARM_VOLUME_VERY_LOUD; + int expected_duration = 60; + const char * expected_sound = "/tmp/foo.wav"; + settings->alarm_volume.set(expected_volume); + settings->alarm_duration.set(expected_duration); + settings->alarm_sound.set(expected_sound); + wait_msec(); + + static constexpr const char* const SOUND_PROP {"default-sound"}; + static constexpr const char* const VOLUME_PROP {"default-volume"}; + static constexpr const char* const DURATION_PROP {"duration"}; + + char* sound = nullptr; + int volume = -1; + int duration = -1; + g_object_get(proxy, SOUND_PROP, &sound, + VOLUME_PROP, &volume, + DURATION_PROP, &duration, + nullptr); + EXPECT_STREQ(expected_sound, sound); + EXPECT_EQ(expected_volume, volume); + EXPECT_EQ(expected_duration, duration); + + /*** + **** Try chaning the DBus properties -- do the Settings change to match it? + ***/ + + expected_volume = ALARM_VOLUME_VERY_QUIET; + expected_duration = 30; + expected_sound = "/tmp/bar.wav"; + g_object_set(proxy, SOUND_PROP, expected_sound, + VOLUME_PROP, expected_volume, + DURATION_PROP, expected_duration, + nullptr); + wait_msec(); + + EXPECT_STREQ(expected_sound, settings->alarm_sound.get().c_str()); + EXPECT_EQ(expected_volume, settings->alarm_volume.get()); + EXPECT_EQ(expected_duration, settings->alarm_duration.get()); + + // cleanup + g_clear_object(&proxy); +} |