From cff541acfef717aae4c9b696627b4817e7eac851 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 24 Apr 2014 11:04:55 -0500 Subject: rename 'clock-watcher' as 'alarm-queue' because the former name isn't very informative. --- src/CMakeLists.txt | 2 +- src/alarm-queue.cpp | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/clock-watcher.cpp | 81 --------------------------------------------------- src/main.cpp | 6 ++-- 4 files changed, 85 insertions(+), 85 deletions(-) create mode 100644 src/alarm-queue.cpp delete mode 100644 src/clock-watcher.cpp (limited to 'src') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9bc22f2..6b65ebc 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -10,10 +10,10 @@ add_definitions (-DTIMEZONE_FILE="/etc/timezone" add_library (${SERVICE_LIB} STATIC actions.cpp actions-live.cpp + alarm-queue.cpp appointment.cpp clock.cpp clock-live.cpp - clock-watcher.cpp date-time.cpp engine-eds.cpp exporter.cpp diff --git a/src/alarm-queue.cpp b/src/alarm-queue.cpp new file mode 100644 index 0000000..9e9da7f --- /dev/null +++ b/src/alarm-queue.cpp @@ -0,0 +1,81 @@ +/* + * 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 + +namespace unity { +namespace indicator { +namespace datetime { + +/*** +**** +***/ + +AlarmQueueImpl::AlarmQueueImpl(const std::shared_ptr& clock, + const std::shared_ptr& upcoming_planner): + m_clock(clock), + m_upcoming_planner(upcoming_planner) +{ + m_clock->date_changed.connect([this](){ + const auto now = m_clock->localtime(); + g_debug("AlarmQueue %p refretching appointments due to date change: %s", this, now.format("%F %T").c_str()); + m_upcoming_planner->date().set(now); + }); + + m_clock->minute_changed.connect([this](){ + g_debug("AlarmQueue %p calling pulse() due to clock minute_changed", this); + pulse(); + }); + + m_upcoming_planner->appointments().changed().connect([this](const std::vector&){ + g_debug("AlarmQueue %p calling pulse() due to appointments changed", this); + pulse(); + }); + + pulse(); +} + +core::Signal& AlarmQueueImpl::alarm_reached() +{ + return m_alarm_reached; +} + +void AlarmQueueImpl::pulse() +{ + const auto now = m_clock->localtime(); + + for(const auto& appointment : m_upcoming_planner->appointments().get()) + { + if (m_triggered.count(appointment.uid)) + continue; + if (!DateTime::is_same_minute(now, appointment.begin)) + continue; + + m_triggered.insert(appointment.uid); + m_alarm_reached(appointment); + } +} + +/*** +**** +***/ + +} // namespace datetime +} // namespace indicator +} // namespace unity diff --git a/src/clock-watcher.cpp b/src/clock-watcher.cpp deleted file mode 100644 index 5da66c8..0000000 --- a/src/clock-watcher.cpp +++ /dev/null @@ -1,81 +0,0 @@ -/* - * 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 - -namespace unity { -namespace indicator { -namespace datetime { - -/*** -**** -***/ - -ClockWatcherImpl::ClockWatcherImpl(const std::shared_ptr& clock, - const std::shared_ptr& upcoming_planner): - m_clock(clock), - m_upcoming_planner(upcoming_planner) -{ - m_clock->date_changed.connect([this](){ - const auto now = m_clock->localtime(); - g_debug("ClockWatcher %p refretching appointments due to date change: %s", this, now.format("%F %T").c_str()); - m_upcoming_planner->date().set(now); - }); - - m_clock->minute_changed.connect([this](){ - g_debug("ClockWatcher %p calling pulse() due to clock minute_changed", this); - pulse(); - }); - - m_upcoming_planner->appointments().changed().connect([this](const std::vector&){ - g_debug("ClockWatcher %p calling pulse() due to appointments changed", this); - pulse(); - }); - - pulse(); -} - -core::Signal& ClockWatcherImpl::alarm_reached() -{ - return m_alarm_reached; -} - -void ClockWatcherImpl::pulse() -{ - const auto now = m_clock->localtime(); - - for(const auto& appointment : m_upcoming_planner->appointments().get()) - { - if (m_triggered.count(appointment.uid)) - continue; - if (!DateTime::is_same_minute(now, appointment.begin)) - continue; - - m_triggered.insert(appointment.uid); - m_alarm_reached(appointment); - } -} - -/*** -**** -***/ - -} // namespace datetime -} // namespace indicator -} // namespace unity diff --git a/src/main.cpp b/src/main.cpp index c7b35e5..238bd02 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -18,8 +18,8 @@ */ #include +#include #include -#include #include #include #include @@ -80,9 +80,9 @@ main(int /*argc*/, char** /*argv*/) // snap decisions std::shared_ptr upcoming_planner(new UpcomingPlanner(std::shared_ptr(new SimpleRangePlanner(engine, file_timezone)), now)); - ClockWatcherImpl clock_watcher(live_clock, upcoming_planner); + AlarmQueueImpl alarm_queue(live_clock, upcoming_planner); Snap snap; - clock_watcher.alarm_reached().connect([&snap](const Appointment& appt){ + alarm_queue.alarm_reached().connect([&snap](const Appointment& appt){ auto snap_show = [](const Appointment& a){ const char* url; if(!a.url.empty()) -- cgit v1.2.3 From a396e6af3cd16530202f6cbecbd45c7a3f6ac893 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 24 Apr 2014 22:34:42 -0500 Subject: hw alarms --- src/CMakeLists.txt | 6 +- src/alarm-queue-simple.cpp | 153 ++++++++++++++++++++++++++++++++++++++ src/alarm-queue.cpp | 81 -------------------- src/date-time.cpp | 5 ++ src/main.cpp | 110 ++++++++++++++++++++------- src/planner-range.cpp | 2 +- src/wakeup-timer-mainloop.cpp | 135 +++++++++++++++++++++++++++++++++ src/wakeup-timer-uha.cpp | 168 ++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 549 insertions(+), 111 deletions(-) create mode 100644 src/alarm-queue-simple.cpp delete mode 100644 src/alarm-queue.cpp create mode 100644 src/wakeup-timer-mainloop.cpp create mode 100644 src/wakeup-timer-uha.cpp (limited to 'src') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6b65ebc..924e538 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -10,7 +10,7 @@ add_definitions (-DTIMEZONE_FILE="/etc/timezone" add_library (${SERVICE_LIB} STATIC actions.cpp actions-live.cpp - alarm-queue.cpp + alarm-queue-simple.cpp appointment.cpp clock.cpp clock-live.cpp @@ -30,7 +30,9 @@ add_library (${SERVICE_LIB} STATIC timezone-file.cpp timezone-geoclue.cpp timezones-live.cpp - utils.c) + utils.c + wakeup-timer-uha.cpp + wakeup-timer-mainloop.cpp) include_directories (${CMAKE_SOURCE_DIR}) link_directories (${SERVICE_DEPS_LIBRARY_DIRS}) diff --git a/src/alarm-queue-simple.cpp b/src/alarm-queue-simple.cpp new file mode 100644 index 0000000..2fd289c --- /dev/null +++ b/src/alarm-queue-simple.cpp @@ -0,0 +1,153 @@ +/* + * 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 + +namespace unity { +namespace indicator { +namespace datetime { + +/*** +**** Public API +***/ + +SimpleAlarmQueue::SimpleAlarmQueue(const std::shared_ptr& clock, + const std::shared_ptr& planner, + const std::shared_ptr& timer): + m_clock(clock), + m_planner(planner), + m_timer(timer), + m_time(clock->localtime()) +{ + m_planner->appointments().changed().connect([this](const std::vector&){ + g_debug("AlarmQueue %p calling requeue() due to appointments changed", this); + requeue(); + }); + + m_clock->minute_changed.connect([=]{ + const auto now = m_clock->localtime(); + constexpr auto skew_threshold_usec = int64_t{90} * G_USEC_PER_SEC; + const bool clock_jumped = std::abs(now - m_time) > skew_threshold_usec; + m_time = now; + if (clock_jumped) { + g_message("AlarmQueue %p calling requeue() due to clock skew", this); + requeue(); + } + }); + + m_timer->timeout().connect([this](){ + g_message("AlarmQueue %p calling requeue() due to timeout", this); + requeue(); + }); + + requeue(); +} + +SimpleAlarmQueue::~SimpleAlarmQueue() +{ +} + +core::Signal& SimpleAlarmQueue::alarm_reached() +{ + return m_alarm_reached; +} + +/*** +**** +***/ + +void SimpleAlarmQueue::requeue() +{ + // kick any current alarms + for (auto current : find_current_alarms()) + { + m_triggered.insert(current.uid); + m_alarm_reached(current); + } + + // idle until the next alarm + Appointment next; + if (find_next_alarm(next)) + { + g_debug ("setting timer to wake up for next appointment '%s' at %s", + next.summary.c_str(), + next.begin.format("%F %T").c_str()); + + m_timer->set_wakeup_time(next.begin); + } +} + +// find the next alarm that will kick now or in the future +bool SimpleAlarmQueue::find_next_alarm(Appointment& setme) const +{ + bool found = false; + Appointment tmp; + const auto now = m_clock->localtime(); + const auto beginning_of_minute = now.add_full (0, 0, 0, 0, 0, -now.seconds()); + + for(const auto& walk : m_planner->appointments().get()) + { + if (m_triggered.count(walk.uid)) // did we already use this one? + continue; + + if (walk.begin < beginning_of_minute) // has this one already passed? + continue; + + if (found && (tmp.begin < walk.begin)) // do we already have a better match? + continue; + + tmp = walk; + found = true; + } + + if (found) + setme = tmp; + + return found; +} + +// find the alarm(s) that should kick right now +std::vector SimpleAlarmQueue::find_current_alarms() const +{ + std::vector appointments; + + const auto now = m_clock->localtime(); + + for(const auto& walk : m_planner->appointments().get()) + { + if (m_triggered.count(walk.uid)) // did we already use this one? + continue; + if (!DateTime::is_same_minute(now, walk.begin)) + continue; + + appointments.push_back(walk); + } + + return appointments; +} + +/*** +**** +***/ + +} // namespace datetime +} // namespace indicator +} // namespace unity diff --git a/src/alarm-queue.cpp b/src/alarm-queue.cpp deleted file mode 100644 index 9e9da7f..0000000 --- a/src/alarm-queue.cpp +++ /dev/null @@ -1,81 +0,0 @@ -/* - * 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 - -namespace unity { -namespace indicator { -namespace datetime { - -/*** -**** -***/ - -AlarmQueueImpl::AlarmQueueImpl(const std::shared_ptr& clock, - const std::shared_ptr& upcoming_planner): - m_clock(clock), - m_upcoming_planner(upcoming_planner) -{ - m_clock->date_changed.connect([this](){ - const auto now = m_clock->localtime(); - g_debug("AlarmQueue %p refretching appointments due to date change: %s", this, now.format("%F %T").c_str()); - m_upcoming_planner->date().set(now); - }); - - m_clock->minute_changed.connect([this](){ - g_debug("AlarmQueue %p calling pulse() due to clock minute_changed", this); - pulse(); - }); - - m_upcoming_planner->appointments().changed().connect([this](const std::vector&){ - g_debug("AlarmQueue %p calling pulse() due to appointments changed", this); - pulse(); - }); - - pulse(); -} - -core::Signal& AlarmQueueImpl::alarm_reached() -{ - return m_alarm_reached; -} - -void AlarmQueueImpl::pulse() -{ - const auto now = m_clock->localtime(); - - for(const auto& appointment : m_upcoming_planner->appointments().get()) - { - if (m_triggered.count(appointment.uid)) - continue; - if (!DateTime::is_same_minute(now, appointment.begin)) - continue; - - m_triggered.insert(appointment.uid); - m_alarm_reached(appointment); - } -} - -/*** -**** -***/ - -} // namespace datetime -} // namespace indicator -} // namespace unity diff --git a/src/date-time.cpp b/src/date-time.cpp index a1c1d1b..a139ea9 100644 --- a/src/date-time.cpp +++ b/src/date-time.cpp @@ -174,6 +174,11 @@ bool DateTime::operator==(const DateTime& that) const return g_date_time_compare(get(), that.get()) == 0; } +int64_t DateTime::operator- (const DateTime& that) const +{ + return g_date_time_difference(get(), that.get()); +} + bool DateTime::is_same_day(const DateTime& a, const DateTime& b) { // it's meaningless to compare uninitialized dates diff --git a/src/main.cpp b/src/main.cpp index 238bd02..e35c5da 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -18,7 +18,7 @@ */ #include -#include +#include #include #include #include @@ -31,6 +31,8 @@ #include #include #include +#include +#include #include // bindtextdomain() #include @@ -42,6 +44,79 @@ using namespace unity::indicator::datetime; +namespace +{ + std::shared_ptr create_engine() + { + std::shared_ptr engine; + + // we don't show appointments in the greeter, + // so no need to connect to EDS there... + if (!g_strcmp0("lightdm", g_get_user_name())) + engine.reset(new MockEngine); + else + engine.reset(new EdsEngine); + + return engine; + } + + std::shared_ptr create_wakeup_timer(const std::shared_ptr& clock) + { + std::shared_ptr wakeup_timer; + + if (UhaWakeupTimer::is_supported()) // prefer to use the platform API + wakeup_timer = std::make_shared(clock); + else + wakeup_timer = std::make_shared(clock); + + return wakeup_timer; + } + + std::shared_ptr create_state(const std::shared_ptr& engine, + const std::shared_ptr& tz) + { + // create the live objects + auto live_settings = std::make_shared(); + auto live_timezones = std::make_shared(live_settings, TIMEZONE_FILE); + auto live_clock = std::make_shared(live_timezones); + + // create a full-month planner currently pointing to the current month + const auto now = live_clock->localtime(); + auto range_planner = std::make_shared(engine, tz); + auto calendar_month = std::make_shared(range_planner, now); + + // create an upcoming-events planner currently pointing to the current date + range_planner = std::make_shared(engine, tz); + auto calendar_upcoming = std::make_shared(range_planner, now); + + // create the state + auto state = std::make_shared(); + state->settings = live_settings; + state->clock = live_clock; + state->locations = std::make_shared(live_settings, live_timezones); + state->calendar_month = calendar_month; + state->calendar_upcoming = calendar_upcoming; + return state; + } + + std::shared_ptr create_simple_alarm_queue(const std::shared_ptr& clock, + const std::shared_ptr& engine, + const std::shared_ptr& tz) + { + // create an upcoming-events planner that =always= tracks the clock's date + auto range_planner = std::make_shared(engine, tz); + auto upcoming_planner = std::make_shared(range_planner, clock->localtime()); + clock->date_changed.connect([clock,upcoming_planner](){ + const auto now = clock->localtime(); + g_debug("refretching appointments due to date change: %s", now.format("%F %T").c_str()); + upcoming_planner->date().set(now); + }); + + auto wakeup_timer = create_wakeup_timer(clock); + return std::make_shared(clock, upcoming_planner, wakeup_timer); + } +} + int main(int /*argc*/, char** /*argv*/) { @@ -54,35 +129,16 @@ main(int /*argc*/, char** /*argv*/) bindtextdomain(GETTEXT_PACKAGE, GNOMELOCALEDIR); textdomain(GETTEXT_PACKAGE); - // we don't show appointments in the greeter, - // so no need to connect to EDS there... - std::shared_ptr engine; - if (!g_strcmp0("lightdm", g_get_user_name())) - engine.reset(new MockEngine); - else - engine.reset(new EdsEngine); - - // build the state, actions, and menufactory - std::shared_ptr state(new State); - std::shared_ptr live_settings(new LiveSettings); - std::shared_ptr live_timezones(new LiveTimezones(live_settings, TIMEZONE_FILE)); - std::shared_ptr live_clock(new LiveClock(live_timezones)); - std::shared_ptr file_timezone(new FileTimezone(TIMEZONE_FILE)); - const auto now = live_clock->localtime(); - state->settings = live_settings; - state->clock = live_clock; - state->locations.reset(new SettingsLocations(live_settings, live_timezones)); - auto calendar_month = new MonthPlanner(std::shared_ptr(new SimpleRangePlanner(engine, file_timezone)), now); - state->calendar_month.reset(calendar_month); - state->calendar_upcoming.reset(new UpcomingPlanner(std::shared_ptr(new SimpleRangePlanner(engine, file_timezone)), now)); - std::shared_ptr actions(new LiveActions(state)); + auto engine = create_engine(); + auto timezone = std::make_shared(TIMEZONE_FILE); + auto state = create_state(engine, timezone); + auto actions = std::make_shared(state); MenuFactory factory(actions, state); - // snap decisions - std::shared_ptr upcoming_planner(new UpcomingPlanner(std::shared_ptr(new SimpleRangePlanner(engine, file_timezone)), now)); - AlarmQueueImpl alarm_queue(live_clock, upcoming_planner); + // set up the snap decisions Snap snap; - alarm_queue.alarm_reached().connect([&snap](const Appointment& appt){ + auto alarm_queue = create_simple_alarm_queue(state->clock, engine, timezone); + alarm_queue->alarm_reached().connect([&snap](const Appointment& appt){ auto snap_show = [](const Appointment& a){ const char* url; if(!a.url.empty()) diff --git a/src/planner-range.cpp b/src/planner-range.cpp index 93946e0..41b0f56 100644 --- a/src/planner-range.cpp +++ b/src/planner-range.cpp @@ -34,7 +34,7 @@ SimpleRangePlanner::SimpleRangePlanner(const std::shared_ptr& engine, m_range(std::pair(DateTime::NowLocal(), DateTime::NowLocal())) { engine->changed().connect([this](){ - g_debug("RangePlanner %p rebuilding soon because Engine %p emitted 'changed' signal%p", this, m_engine.get()); + g_debug("RangePlanner %p rebuilding soon because Engine %p emitted 'changed' signal", this, m_engine.get()); rebuild_soon(); }); diff --git a/src/wakeup-timer-mainloop.cpp b/src/wakeup-timer-mainloop.cpp new file mode 100644 index 0000000..53dd684 --- /dev/null +++ b/src/wakeup-timer-mainloop.cpp @@ -0,0 +1,135 @@ +/* + * 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 + +namespace unity { +namespace indicator { +namespace datetime { + +/*** +**** +***/ + +class MainloopWakeupTimer::Impl +{ + +public: + + Impl(const std::shared_ptr& clock): + m_clock(clock) + { + } + + ~Impl() + { + cancel_timer(); + } + + void set_wakeup_time(const DateTime& d) + { + m_wakeup_time = d; + + rebuild_timer(); + } + + core::Signal<>& timeout() { return m_timeout; } + +private: + + void rebuild_timer() + { + cancel_timer(); + + g_return_if_fail(m_wakeup_time.is_set()); + + const auto now = m_clock->localtime(); + const auto difference_usec = g_date_time_difference(m_wakeup_time.get(), now.get()); + const guint interval_msec = (guint)difference_usec / 1000u; + g_message("setting wakeup timer to kick at %s, which is in %zu seconds", + m_wakeup_time.format("%F %T").c_str(), + size_t{interval_msec/1000}); + + m_timeout_tag = g_timeout_add_full(G_PRIORITY_HIGH, + interval_msec, + on_timeout, + this, + nullptr); + } + + static gboolean on_timeout(gpointer gself) + { + g_message("%s %s", G_STRLOC, G_STRFUNC); + static_cast(gself)->on_timeout(); + return G_SOURCE_REMOVE; + } + + void on_timeout() + { + cancel_timer(); + m_timeout(); + } + + void cancel_timer() + { + if (m_timeout_tag != 0) + { + g_source_remove(m_timeout_tag); + m_timeout_tag = 0; + } + } + + core::Signal<> m_timeout; + const std::shared_ptr& m_clock; + guint m_timeout_tag = 0; + DateTime m_wakeup_time; +}; + +/*** +**** +***/ + +MainloopWakeupTimer::MainloopWakeupTimer(const std::shared_ptr& clock): + p(new Impl(clock)) +{ +} + +MainloopWakeupTimer::~MainloopWakeupTimer() +{ +} + +void MainloopWakeupTimer::set_wakeup_time(const DateTime& d) +{ + p->set_wakeup_time(d); +} + +core::Signal<>& MainloopWakeupTimer::timeout() +{ + return p->timeout(); +} + +/*** +**** +***/ + +} // namespace datetime +} // namespace indicator +} // namespace unity diff --git a/src/wakeup-timer-uha.cpp b/src/wakeup-timer-uha.cpp new file mode 100644 index 0000000..dda109f --- /dev/null +++ b/src/wakeup-timer-uha.cpp @@ -0,0 +1,168 @@ +/* + * 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 // struct timespec +#include +#include + +namespace unity { +namespace indicator { +namespace datetime { + +/*** +**** +***/ + +class UhaWakeupTimer::Impl +{ + +public: + + Impl(const std::shared_ptr& clock): + m_clock(clock), + m_hardware_alarm(u_hardware_alarm_create()) + { + // fire up a worker thread that initially just sleeps + set_wakeup_time_to_the_distant_future(); + m_thread = std::move(std::thread([&](){threadfunc();})); + } + + ~Impl() + { + // tell the worker thread to wake up and exit + m_yielding = true; + set_wakeup_time(m_clock->localtime().add_full(0,0,0,0,0,0.1)); + + // wait for it to happen + if (m_thread.joinable()) + m_thread.join(); + + g_idle_remove_by_data(this); + + u_hardware_alarm_unref(m_hardware_alarm); + } + + void set_wakeup_time(const DateTime& d) + { + g_message("%s %s", G_STRLOC, G_STRFUNC); + std::lock_guard lg(m_mutex); + + g_message("%s setting hardware wakeup time to %s", G_STRFUNC, d.format("%F %T").c_str()); + + const auto diff_usec = d - m_clock->localtime(); + struct timespec ts; + ts.tv_sec = diff_usec / G_USEC_PER_SEC; + ts.tv_nsec = (diff_usec % G_USEC_PER_SEC) * 1000; + g_message("%s setting hardware alarm to kick %zu seconds from now", G_STRFUNC, (size_t)ts.tv_sec); + u_hardware_alarm_set_relative_to_with_behavior(m_hardware_alarm, + U_HARDWARE_ALARM_TIME_REFERENCE_NOW, + U_HARDWARE_ALARM_SLEEP_BEHAVIOR_WAKEUP_DEVICE, + &ts); + } + + core::Signal<>& timeout() { return m_timeout; } + +private: + + void set_wakeup_time_to_the_distant_future() + { + const auto next_year = m_clock->localtime().add_full(1,0,0,0,0,0); + set_wakeup_time(next_year); + } + + static gboolean kick_idle (gpointer gself) + { + static_cast(gself)->timeout(); + + return G_SOURCE_REMOVE; + } + + void threadfunc() + { + while (!m_yielding) + { + // wait for the next hw alarm + UHardwareAlarmWaitResult wait_result; + auto rc = u_hardware_alarm_wait_for_next_alarm(m_hardware_alarm, &wait_result); + g_return_if_fail (rc == U_STATUS_SUCCESS); + + // set a long wakeup interval for the next iteration of the loop. + // if there's another Appointment queued up by the Planner, + // our timeout() listener will call set_wakeup_time() to set the + // real wakeup interval. + set_wakeup_time_to_the_distant_future(); + + // delegate the kick back to the main thread + g_idle_add (kick_idle, this); + } + } + + core::Signal<> m_timeout; + std::recursive_mutex m_mutex; + bool m_yielding = false; + const std::shared_ptr& m_clock; + UHardwareAlarm m_hardware_alarm = nullptr; + std::thread m_thread; +}; + +/*** +**** +***/ + +UhaWakeupTimer::UhaWakeupTimer(const std::shared_ptr& clock): + p(new Impl(clock)) +{ +} + +UhaWakeupTimer::~UhaWakeupTimer() +{ +} + +bool UhaWakeupTimer::is_supported() +{ + auto foo = u_hardware_alarm_create(); + g_message ("%s hardware alarm %p", G_STRFUNC, foo); + return foo != nullptr; +} + +void UhaWakeupTimer::set_wakeup_time(const DateTime& d) +{ + p->set_wakeup_time(d); +} + +core::Signal<>& UhaWakeupTimer::timeout() +{ + return p->timeout(); +} + +/*** +**** +***/ + +} // namespace datetime +} // namespace indicator +} // namespace unity -- cgit v1.2.3 From 02319cb816824442fa59736f97e570726e01ce41 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 10 Jun 2014 09:00:14 -0500 Subject: in wakeup-timer-mainloop, don't just cast to uint without checking sign. (h/t ted) --- src/wakeup-timer-mainloop.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/wakeup-timer-mainloop.cpp b/src/wakeup-timer-mainloop.cpp index dfef037..4f99c8c 100644 --- a/src/wakeup-timer-mainloop.cpp +++ b/src/wakeup-timer-mainloop.cpp @@ -21,6 +21,8 @@ #include +#include // abs() + namespace unity { namespace indicator { namespace datetime { @@ -63,7 +65,7 @@ private: const auto now = m_clock->localtime(); const auto difference_usec = g_date_time_difference(m_wakeup_time.get(), now.get()); - const guint interval_msec = (guint)difference_usec / 1000u; + const guint interval_msec = std::abs(difference_usec) / 1000u; g_debug("%s setting wakeup timer to kick at %s, which is in %zu seconds", G_STRFUNC, m_wakeup_time.format("%F %T").c_str(), -- cgit v1.2.3 From 0e5acaedb9cb419b7fc607de175fce23025805c2 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 10 Jun 2014 09:01:39 -0500 Subject: in alarm-queue-simple, call requeue() if time skew is detected. --- src/alarm-queue-simple.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/alarm-queue-simple.cpp b/src/alarm-queue-simple.cpp index 1c871bf..00608c9 100644 --- a/src/alarm-queue-simple.cpp +++ b/src/alarm-queue-simple.cpp @@ -35,7 +35,7 @@ SimpleAlarmQueue::SimpleAlarmQueue(const std::shared_ptr& clock, m_clock(clock), m_planner(planner), m_timer(timer), - m_time(clock->localtime()) + m_datetime(clock->localtime()) { m_planner->appointments().changed().connect([this](const std::vector&){ g_debug("AlarmQueue %p calling requeue() due to appointments changed", this); @@ -45,8 +45,8 @@ SimpleAlarmQueue::SimpleAlarmQueue(const std::shared_ptr& clock, m_clock->minute_changed.connect([=]{ const auto now = m_clock->localtime(); constexpr auto skew_threshold_usec = int64_t{90} * G_USEC_PER_SEC; - const bool clock_jumped = std::abs(now - m_time) > skew_threshold_usec; - m_time = now; + const bool clock_jumped = std::abs(now - m_datetime) > skew_threshold_usec; + m_datetime = now; if (clock_jumped) { g_debug("AlarmQueue %p calling requeue() due to clock skew", this); requeue(); -- cgit v1.2.3 From 3993fcd4ba813f3c978c98648ad5fa2f4d848d7d Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 10 Jun 2014 09:02:17 -0500 Subject: in engine-eds, add debug messages --- src/engine-eds.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/engine-eds.cpp b/src/engine-eds.cpp index da93206..1949193 100644 --- a/src/engine-eds.cpp +++ b/src/engine-eds.cpp @@ -418,6 +418,14 @@ private: auto status = ICAL_STATUS_NONE; e_cal_component_get_status(component, &status); + const auto begin_dt = DateTime(begin); + const auto end_dt = DateTime(end); + g_debug ("got appointment from %s to %s, uid %s status %d", + begin_dt.format("%F %T").c_str(), + end_dt.format("%F %T").c_str(), + uid, + (int)status); + if ((uid != nullptr) && (status != ICAL_STATUS_COMPLETED) && (status != ICAL_STATUS_CANCELLED)) @@ -430,8 +438,8 @@ private: if (text.value) appointment.summary = text.value; - appointment.begin = DateTime(begin); - appointment.end = DateTime(end); + appointment.begin = begin_dt; + appointment.end = end_dt; appointment.color = subtask->color; appointment.uid = uid; -- cgit v1.2.3 From 79372164fe92a3022df32ef2c6812445126ac00e Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 10 Jun 2014 09:05:35 -0500 Subject: in wakeup-timer-uha, set idle wakeup to 1 day from now rather than 1 year from now. --- src/wakeup-timer-uha.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/wakeup-timer-uha.cpp b/src/wakeup-timer-uha.cpp index b634f52..0db9fb0 100644 --- a/src/wakeup-timer-uha.cpp +++ b/src/wakeup-timer-uha.cpp @@ -89,8 +89,8 @@ private: void set_wakeup_time_to_the_distant_future() { - const auto next_year = m_clock->localtime().add_full(1,0,0,0,0,0); - set_wakeup_time(next_year); + const auto tomorrow = m_clock->localtime().add_full(0,0,1,0,0,0); + set_wakeup_time(tomorrow); } static gboolean kick_idle (gpointer gself) -- cgit v1.2.3 From ce7397dce272c913d93a6bd3852907bc917a4e6f Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 10 Jun 2014 09:06:46 -0500 Subject: in wakeup-timer-uha, sync with platform-api changes and add a safeguard to ensure the service doesn't try to sleep for an event that's already passed. --- src/wakeup-timer-uha.cpp | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/wakeup-timer-uha.cpp b/src/wakeup-timer-uha.cpp index 0db9fb0..7e19075 100644 --- a/src/wakeup-timer-uha.cpp +++ b/src/wakeup-timer-uha.cpp @@ -71,16 +71,23 @@ public: g_debug("%s %s", G_STRLOC, G_STRFUNC); std::lock_guard lg(m_mutex); - const auto diff_usec = d - m_clock->localtime(); - struct timespec ts; - ts.tv_sec = diff_usec / G_USEC_PER_SEC; - ts.tv_nsec = (diff_usec % G_USEC_PER_SEC) * 1000; - g_debug("%s setting hardware wakeup time to %s (%zu seconds from now)", - G_STRFUNC, (size_t)ts.tv_sec, d.format("%F %T").c_str()); + const auto wakeup_time = d.to_unix(); + + // simple sanity check: don't try to wait for something that's already passed + const auto now = m_clock->localtime().to_unix(); + g_return_if_fail (wakeup_time >= now); + + struct timespec sleep_interval; + sleep_interval.tv_sec = wakeup_time; + sleep_interval.tv_nsec = 0; + g_debug("%s %s setting hardware wakeup time to %s (%zu seconds from now)", + G_STRLOC, G_STRFUNC, + d.format("%F %T").c_str(), + (size_t)(wakeup_time - now)); u_hardware_alarm_set_relative_to_with_behavior(m_hardware_alarm, - U_HARDWARE_ALARM_TIME_REFERENCE_NOW, + U_HARDWARE_ALARM_TIME_REFERENCE_RTC, U_HARDWARE_ALARM_SLEEP_BEHAVIOR_WAKEUP_DEVICE, - &ts); + &sleep_interval); } core::Signal<>& timeout() { return m_timeout; } @@ -106,6 +113,7 @@ private: { // wait for the next hw alarm UHardwareAlarmWaitResult wait_result; + g_debug ("calling wait_for_next_alarm"); auto rc = u_hardware_alarm_wait_for_next_alarm(m_hardware_alarm, &wait_result); g_return_if_fail (rc == U_STATUS_SUCCESS); -- cgit v1.2.3 From e32110f08d6888e25db5bcedb7fd274aa8caea51 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 10 Jun 2014 11:56:07 -0500 Subject: in UhaWakeupTimer, ensure the timeout signal gets emitted from inside a glib idle callback. --- src/wakeup-timer-uha.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/wakeup-timer-uha.cpp b/src/wakeup-timer-uha.cpp index 7e19075..437eda2 100644 --- a/src/wakeup-timer-uha.cpp +++ b/src/wakeup-timer-uha.cpp @@ -102,7 +102,7 @@ private: static gboolean kick_idle (gpointer gself) { - static_cast(gself)->timeout(); + static_cast(gself)->m_timeout(); return G_SOURCE_REMOVE; } -- cgit v1.2.3 From 03a7615f1ff6f4bb1d8bf955c042151336edac9b Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 10 Jun 2014 11:56:32 -0500 Subject: in SimpleAlarmQueue, the 'alarms we don't want to trigger' list needs to be composed of uid + timestamp. Keying off of only timestamp doesn't work because UIDs can be recycled as users edit and reuse alarms. --- src/alarm-queue-simple.cpp | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/alarm-queue-simple.cpp b/src/alarm-queue-simple.cpp index 00608c9..2e36380 100644 --- a/src/alarm-queue-simple.cpp +++ b/src/alarm-queue-simple.cpp @@ -79,7 +79,8 @@ void SimpleAlarmQueue::requeue() // kick any current alarms for (auto current : find_current_alarms()) { - m_triggered.insert(current.uid); + const std::pair trig {current.uid, current.begin}; + m_triggered.insert(trig); m_alarm_reached(current); } @@ -103,16 +104,26 @@ bool SimpleAlarmQueue::find_next_alarm(Appointment& setme) const const auto now = m_clock->localtime(); const auto beginning_of_minute = now.add_full (0, 0, 0, 0, 0, -now.seconds()); - for(const auto& walk : m_planner->appointments().get()) + const auto appointments = m_planner->appointments().get(); + g_message ("planner has %zu appointments in it", (size_t)appointments.size()); + + for(const auto& walk : appointments) { - if (m_triggered.count(walk.uid)) // did we already use this one? + const std::pair trig {walk.uid, walk.begin}; + if (m_triggered.count(trig)) { + g_message ("skipping; already used"); continue; + } - if (walk.begin < beginning_of_minute) // has this one already passed? + if (walk.begin < beginning_of_minute) { // has this one already passed? + g_message ("skipping; too old"); continue; + } - if (found && (tmp.begin < walk.begin)) // do we already have a better match? + if (found && (tmp.begin < walk.begin)) { // do we already have a better match? + g_message ("skipping; bad match"); continue; + } tmp = walk; found = true; @@ -133,7 +144,8 @@ std::vector SimpleAlarmQueue::find_current_alarms() const for(const auto& walk : m_planner->appointments().get()) { - if (m_triggered.count(walk.uid)) // did we already use this one? + const std::pair trig {walk.uid, walk.begin}; + if (m_triggered.count(trig)) // did we already use this one? continue; if (!DateTime::is_same_minute(now, walk.begin)) continue; -- cgit v1.2.3 From a5e5f4ccf55c61be72daa28e4e5b581a6dd9571b Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 10 Jun 2014 12:01:57 -0500 Subject: remove some debug tracers that shouldn't've been committed --- src/alarm-queue-simple.cpp | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/alarm-queue-simple.cpp b/src/alarm-queue-simple.cpp index 2e36380..fa6c0bc 100644 --- a/src/alarm-queue-simple.cpp +++ b/src/alarm-queue-simple.cpp @@ -105,25 +105,19 @@ bool SimpleAlarmQueue::find_next_alarm(Appointment& setme) const const auto beginning_of_minute = now.add_full (0, 0, 0, 0, 0, -now.seconds()); const auto appointments = m_planner->appointments().get(); - g_message ("planner has %zu appointments in it", (size_t)appointments.size()); + g_debug ("planner has %zu appointments in it", (size_t)appointments.size()); for(const auto& walk : appointments) { const std::pair trig {walk.uid, walk.begin}; - if (m_triggered.count(trig)) { - g_message ("skipping; already used"); + if (m_triggered.count(trig)) continue; - } - if (walk.begin < beginning_of_minute) { // has this one already passed? - g_message ("skipping; too old"); + if (walk.begin < beginning_of_minute) // has this one already passed? continue; - } - if (found && (tmp.begin < walk.begin)) { // do we already have a better match? - g_message ("skipping; bad match"); + if (found && (tmp.begin < walk.begin)) // do we already have a better match? continue; - } tmp = walk; found = true; -- cgit v1.2.3 From 3f7fad001e4fe74a89b60d2e217750baaf3b0392 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 10 Jun 2014 23:04:52 -0500 Subject: Don't build the hw-alarms pieces if ubuntu/hardware/alarm.h can't be found. Don't require libplatform-hardware-api-* unless the arch is armhf, i386, or amd64. --- src/CMakeLists.txt | 57 +++++++++++++++++++++++++++++------------------------- src/main.cpp | 10 +++++++++- 2 files changed, 40 insertions(+), 27 deletions(-) (limited to 'src') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 924e538..ffa1523 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -7,32 +7,37 @@ SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -g ${CXX_WARNING_ARGS} ${GCO add_definitions (-DTIMEZONE_FILE="/etc/timezone" -DG_LOG_DOMAIN="Indicator-Datetime") -add_library (${SERVICE_LIB} STATIC - actions.cpp - actions-live.cpp - alarm-queue-simple.cpp - appointment.cpp - clock.cpp - clock-live.cpp - date-time.cpp - engine-eds.cpp - exporter.cpp - formatter.cpp - formatter-desktop.cpp - locations.cpp - locations-settings.cpp - menu.cpp - planner-month.cpp - planner-range.cpp - planner-upcoming.cpp - settings-live.cpp - snap.cpp - timezone-file.cpp - timezone-geoclue.cpp - timezones-live.cpp - utils.c - wakeup-timer-uha.cpp - wakeup-timer-mainloop.cpp) +set (SERVICE_SOURCES + actions.cpp + actions-live.cpp + alarm-queue-simple.cpp + appointment.cpp + clock.cpp + clock-live.cpp + date-time.cpp + engine-eds.cpp + exporter.cpp + formatter.cpp + formatter-desktop.cpp + locations.cpp + locations-settings.cpp + menu.cpp + planner-month.cpp + planner-range.cpp + planner-upcoming.cpp + settings-live.cpp + snap.cpp + timezone-file.cpp + timezone-geoclue.cpp + timezones-live.cpp + utils.c + wakeup-timer-mainloop.cpp) + +if (HAVE_UBUNTU_HW_ALARM_H) + set (SERVICE_SOURCES ${SERVICE_SOURCES} wakeup-timer-uha.cpp) +endif () + +add_library (${SERVICE_LIB} STATIC ${SERVICE_SOURCES}) include_directories (${CMAKE_SOURCE_DIR}) link_directories (${SERVICE_DEPS_LIBRARY_DIRS}) diff --git a/src/main.cpp b/src/main.cpp index e35c5da..adbd371 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -32,7 +32,13 @@ #include #include #include -#include + +#ifdef HAVE_UBUNTU_HW_ALARM_H + #warning using hw alarms + #include +#else + #warning not using hw arlarms +#endif #include // bindtextdomain() #include @@ -64,9 +70,11 @@ namespace { std::shared_ptr wakeup_timer; +#ifdef HAVE_UBUNTU_HW_ALARM_H if (UhaWakeupTimer::is_supported()) // prefer to use the platform API wakeup_timer = std::make_shared(clock); else +#endif wakeup_timer = std::make_shared(clock); return wakeup_timer; -- cgit v1.2.3 From db2898b2da7231490fe77ebcef0fb373ce1f2776 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 10 Jun 2014 23:06:46 -0500 Subject: remove a pair of #warning tracers --- src/main.cpp | 3 --- 1 file changed, 3 deletions(-) (limited to 'src') diff --git a/src/main.cpp b/src/main.cpp index adbd371..079fe35 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -34,10 +34,7 @@ #include #ifdef HAVE_UBUNTU_HW_ALARM_H - #warning using hw alarms #include -#else - #warning not using hw arlarms #endif #include // bindtextdomain() -- cgit v1.2.3