From d1f427618fbcdab4a11d84e6fde987f1b62c9bec Mon Sep 17 00:00:00 2001 From: Skirmantas Kligys Date: Wed, 5 Feb 2020 11:02:42 -0800 Subject: [PATCH] Cache calendar per clock instance, weekdays properly handle locales. --- include/modules/clock.hpp | 11 +++ meson.build | 10 ++- src/modules/clock.cpp | 175 ++++++++++++++++++-------------------- 3 files changed, 101 insertions(+), 95 deletions(-) diff --git a/include/modules/clock.hpp b/include/modules/clock.hpp index e54f4d2c..bb63241d 100644 --- a/include/modules/clock.hpp +++ b/include/modules/clock.hpp @@ -12,6 +12,11 @@ namespace waybar::modules { +struct waybar_time { + std::locale locale; + date::zoned_time ztime; +}; + class Clock : public ALabel { public: Clock(const std::string&, const Json::Value&); @@ -23,6 +28,12 @@ class Clock : public ALabel { std::locale locale_; const date::time_zone* time_zone_; bool fixed_time_zone_; + date::year_month_day cached_calendar_ymd_; + std::string cached_calendar_text_; + + auto calendar_text(const waybar_time& wtime) -> std::string; + auto weekdays_header(const date::weekday& first_dow, std::ostream& os) -> void; + auto first_day_of_week() -> date::weekday; }; } // namespace waybar::modules diff --git a/meson.build b/meson.build index 98dd25e8..8cfe5d89 100644 --- a/meson.build +++ b/meson.build @@ -42,11 +42,15 @@ if not compiler.has_header('filesystem') endif endif -code = '''#include +code = ''' +#include +#include int main(int argc, char** argv) { + locale_t locale = newlocale(LC_ALL, "en_US.UTF-8", nullptr); char* str; - str = nl_langinfo(_NL_TIME_WEEK_1STDAY); - str = nl_langinfo(_NL_TIME_FIRST_WEEKDAY); + str = nl_langinfo_l(_NL_TIME_WEEK_1STDAY, locale); + str = nl_langinfo_l(_NL_TIME_FIRST_WEEKDAY, locale); + freelocale(locale); return 0; } ''' diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 8419d18b..5655168f 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -1,101 +1,12 @@ #include "modules/clock.hpp" #include +#include #ifdef HAVE_LANGINFO_1STDAY #include +#include #endif -using zoned_time = date::zoned_time; - -struct waybar_time { - std::locale locale; - zoned_time ztime; -}; - -namespace { - -#ifdef HAVE_LANGINFO_1STDAY -// Computations done similarly to Linux cal utility. -date::weekday first_day_of_week() { - const int i = (std::intptr_t) nl_langinfo(_NL_TIME_WEEK_1STDAY); - auto ymd = date::year(i / 10000)/(i / 100 % 100)/(i % 100); - auto wd = date::weekday(ymd); - uint8_t j = *nl_langinfo(_NL_TIME_FIRST_WEEKDAY); - return wd + date::days(j - 1); -} -#endif - -void weekdays_header(const std::locale& locale, const date::weekday& first_dow, std::ostream& os) { - auto wd = first_dow; - do { - if (wd != first_dow) os << ' '; - Glib::ustring wd_ustring(date::format(locale, "%a", wd)); - auto wd_len = wd_ustring.length(); - if (wd_len > 2) { - wd_ustring = wd_ustring.substr(0, 2); - wd_len = 2; - } - const std::string pad(2 - wd_len, ' '); - os << pad << wd_ustring; - } while (++wd != first_dow); - os << "\n"; -} - -struct CachedCalendar { - date::year_month_day ymd; - std::string text; - - void set(const date::year_month_day& ymd_, std::string text_) { - ymd = ymd_; - text = text_; - } -}; - -CachedCalendar cached_calendar; - -std::string calendar_text(const waybar_time& wtime) { - const auto daypoint = date::floor(wtime.ztime.get_local_time()); - const auto ymd = date::year_month_day(daypoint); - if (cached_calendar.ymd == ymd) { - return cached_calendar.text; - } - - const date::year_month ym(ymd.year(), ymd.month()); - const auto curr_day = ymd.day(); - - std::stringstream os; -#ifdef HAVE_LANGINFO_1STDAY - const auto first_dow = first_day_of_week(); -#else - const auto first_dow = date::Sunday; -#endif - weekdays_header(wtime.locale, first_dow, os); - - // First week prefixed with spaces if needed. - auto wd = date::weekday(ym/1); - auto empty_days = (wd - first_dow).count(); - if (empty_days > 0) { - os << std::string(empty_days * 3 - 1, ' '); - } - auto last_day = (ym/date::literals::last).day(); - for (auto d = date::day(1); d <= last_day; ++d, ++wd) { - if (wd != first_dow) { - os << ' '; - } else if (unsigned(d) != 1) { - os << '\n'; - } - if (d == curr_day) { - os << "" << date::format("%e", d) << ""; - } else { - os << date::format("%e", d); - } - } - - auto result = os.str(); - cached_calendar.set(ymd, result); - return result; -} - -} +using waybar::modules::waybar_time; waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) : ALabel(config, "clock", id, "{:%H:%M}", 60) @@ -144,6 +55,86 @@ auto waybar::modules::Clock::update() -> void { } } +auto waybar::modules::Clock::calendar_text(const waybar_time& wtime) -> std::string { + const auto daypoint = date::floor(wtime.ztime.get_local_time()); + const auto ymd = date::year_month_day(daypoint); + if (cached_calendar_ymd_ == ymd) { + return cached_calendar_text_; + } + + const date::year_month ym(ymd.year(), ymd.month()); + const auto curr_day = ymd.day(); + + std::stringstream os; + const auto first_dow = first_day_of_week(); + weekdays_header(first_dow, os); + + // First week prefixed with spaces if needed. + auto wd = date::weekday(ym/1); + auto empty_days = (wd - first_dow).count(); + if (empty_days > 0) { + os << std::string(empty_days * 3 - 1, ' '); + } + auto last_day = (ym/date::literals::last).day(); + for (auto d = date::day(1); d <= last_day; ++d, ++wd) { + if (wd != first_dow) { + os << ' '; + } else if (unsigned(d) != 1) { + os << '\n'; + } + if (d == curr_day) { + os << "" << date::format("%e", d) << ""; + } else { + os << date::format("%e", d); + } + } + + auto result = os.str(); + cached_calendar_ymd_ = ymd; + cached_calendar_text_ = result; + return result; +} + +auto waybar::modules::Clock::weekdays_header(const date::weekday& first_dow, std::ostream& os) -> void { + auto wd = first_dow; + do { + if (wd != first_dow) os << ' '; + Glib::ustring wd_ustring(date::format(locale_, "%a", wd)); + auto wd_len = wd_ustring.length(); + if (wd_len > 2) { + wd_ustring = wd_ustring.substr(0, 2); + wd_len = 2; + } + const std::string pad(2 - wd_len, ' '); + os << pad << wd_ustring; + } while (++wd != first_dow); + os << "\n"; +} + +#ifdef HAVE_LANGINFO_1STDAY +template +using deleter_from_fn = std::integral_constant; + +template +using deleting_unique_ptr = std::unique_ptr>; +#endif + +// Computations done similarly to Linux cal utility. +auto waybar::modules::Clock::first_day_of_week() -> date::weekday { +#ifdef HAVE_LANGINFO_1STDAY + deleting_unique_ptr::type, freelocale> + posix_locale{newlocale(LC_ALL, locale_.name().c_str(), nullptr)}; + if (posix_locale) { + const int i = (std::intptr_t) nl_langinfo_l(_NL_TIME_WEEK_1STDAY, posix_locale.get()); + auto ymd = date::year(i / 10000)/(i / 100 % 100)/(i % 100); + auto wd = date::weekday(ymd); + uint8_t j = *nl_langinfo_l(_NL_TIME_FIRST_WEEKDAY, posix_locale.get()); + return wd + date::days(j - 1); + } +#endif + return date::Sunday; +} + template <> struct fmt::formatter : fmt::formatter { template