From 8e05aab4d9564c0f5e9deccbd99baae5ac751466 Mon Sep 17 00:00:00 2001 From: Skirmantas Kligys Date: Fri, 31 Jan 2020 08:54:41 -0800 Subject: [PATCH 1/7] Current month calendar in clock tooltip. --- man/waybar-clock.5.scd | 4 ++ meson.build | 4 +- resources/config | 2 +- src/modules/clock.cpp | 96 ++++++++++++++++++++++++++++++++++++----- subprojects/utfcpp.wrap | 9 ++++ 5 files changed, 103 insertions(+), 12 deletions(-) create mode 100644 subprojects/utfcpp.wrap diff --git a/man/waybar-clock.5.scd b/man/waybar-clock.5.scd index e3213f3..6684d89 100644 --- a/man/waybar-clock.5.scd +++ b/man/waybar-clock.5.scd @@ -65,6 +65,10 @@ The *clock* module displays the current date and time. View all valid format options in *strftime(3)*. +# FORMAT REPLACEMENTS + +*{calendar}*: Current month calendar + # EXAMPLES ``` diff --git a/meson.build b/meson.build index 69439ac..812cc42 100644 --- a/meson.build +++ b/meson.build @@ -67,6 +67,7 @@ gtk_layer_shell = dependency('gtk-layer-shell-0', fallback : ['gtk-layer-shell', 'gtk_layer_shell_dep']) systemd = dependency('systemd', required: get_option('systemd')) tz_dep = dependency('date', default_options : [ 'use_system_tzdb=true' ], fallback: [ 'date', 'tz_dep' ]) +utfcpp = dependency('utfcpp', fallback: [ 'utfcpp', 'utfcpp_dep' ]) prefix = get_option('prefix') conf_data = configuration_data() @@ -168,7 +169,8 @@ executable( libudev, libmpdclient, gtk_layer_shell, - tz_dep + tz_dep, + utfcpp ], include_directories: [include_directories('include')], install: true, diff --git a/resources/config b/resources/config index 8dfa012..116d377 100644 --- a/resources/config +++ b/resources/config @@ -65,7 +65,7 @@ }, "clock": { // "timezone": "America/New_York", - "tooltip-format": "{:%Y-%m-%d | %H:%M}", + "tooltip-format": "{:%Y %B}\n{calendar}", "format-alt": "{:%Y-%m-%d}" }, "cpu": { diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 38af115..f918137 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -1,4 +1,86 @@ #include "modules/clock.hpp" +#include +#include + +using zoned_time = date::zoned_time; + +struct waybar_time { + std::locale locale; + zoned_time ztime; +}; + +namespace { + +size_t utf8_strlen(const std::string& s) { + return utf8::distance(s.begin(), s.end()); +} + +std::string utf8_substr(const std::string s, size_t len) { + utf8::iterator it(s.begin(), s.begin(), s.end()); + for (size_t i = 0; i < len; ++i) { + ++it; + } + int byte_count = it.base() - s.begin(); + return s.substr(0, byte_count); +} + +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 << ' '; + auto wd_string = date::format(locale, "%a", wd); + auto wd_string_len = utf8_strlen(wd_string); + if (wd_string_len > 2) { + wd_string = utf8_substr(wd_string, 2); + wd_string_len = 2; + } + const std::string pad(2 - wd_string_len, ' '); + os << pad << wd_string; + } while (++wd != first_dow); + os << "\n"; +} + +std::string calendar_text(const waybar_time& wtime, const date::weekday& first_dow) { + const auto daypoint = date::floor(wtime.ztime.get_local_time()); + const auto ymd = date::year_month_day(daypoint); + const date::year_month ym(ymd.year(), ymd.month()); + const auto curr_day = ymd.day(); + + std::stringstream os; + 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 d = date::day(1); + do { + if (wd != first_dow) os << ' '; + if (d == curr_day) { + os << "" << date::format("%e", d) << ""; + } else { + os << date::format("%e", d); + } + ++d; + } while (++wd != first_dow); + + // Following weeks. + auto last_day = (ym/date::literals::last).day(); + for ( ; d <= last_day; ++d, ++wd) { + os << ((wd == first_dow) ? '\n' : ' '); + if (d == curr_day) { + os << "" << date::format("%e", d) << ""; + } else { + os << date::format("%e", d); + } + } + + return os.str(); +} + +} waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) : ALabel(config, "clock", id, "{:%H:%M}", 60) @@ -24,13 +106,6 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) }; } -using zoned_time = date::zoned_time; - -struct waybar_time { - std::locale locale; - zoned_time ztime; -}; - auto waybar::modules::Clock::update() -> void { if (!fixed_time_zone_) { // Time zone can change. Be sure to pick that. @@ -44,11 +119,12 @@ auto waybar::modules::Clock::update() -> void { if (tooltipEnabled()) { if (config_["tooltip-format"].isString()) { + const auto calendar = calendar_text(wtime, date::Sunday); auto tooltip_format = config_["tooltip-format"].asString(); - auto tooltip_text = fmt::format(tooltip_format, wtime); - label_.set_tooltip_text(tooltip_text); + auto tooltip_text = fmt::format(tooltip_format, wtime, fmt::arg("calendar", calendar)); + label_.set_tooltip_markup(tooltip_text); } else { - label_.set_tooltip_text(text); + label_.set_tooltip_markup(text); } } } diff --git a/subprojects/utfcpp.wrap b/subprojects/utfcpp.wrap new file mode 100644 index 0000000..51cf131 --- /dev/null +++ b/subprojects/utfcpp.wrap @@ -0,0 +1,9 @@ +[wrap-file] +directory = utfcpp-2.3.5 +source_url = https://github.com/nemtrif/utfcpp/archive/v2.3.5.zip +source_filename = utfcpp-2.3.5.zip +source_hash = 1d5cb7d908202d734ec35b84087400013f352ffb4612e978ffb948986b76df14 + +patch_url = https://github.com/mesonbuild/utfcpp/releases/download/2.3.5-1/utfcpp.zip +patch_filename = utfcpp-2.3.5-1-wrap.zip +patch_hash = 5f504de947b34bb5995fcdb66a1ea1392288076a9400ead829da79e73441a13f From ea9591baea2e661829a7509ad51c681dc4925dff Mon Sep 17 00:00:00 2001 From: Skirmantas Kligys Date: Sun, 2 Feb 2020 14:35:07 -0800 Subject: [PATCH 2/7] Switch from utfcpp to Glib::ustring for UTF-8 string mangling. --- meson.build | 4 +--- src/modules/clock.cpp | 28 +++++++--------------------- subprojects/utfcpp.wrap | 9 --------- 3 files changed, 8 insertions(+), 33 deletions(-) delete mode 100644 subprojects/utfcpp.wrap diff --git a/meson.build b/meson.build index 812cc42..69439ac 100644 --- a/meson.build +++ b/meson.build @@ -67,7 +67,6 @@ gtk_layer_shell = dependency('gtk-layer-shell-0', fallback : ['gtk-layer-shell', 'gtk_layer_shell_dep']) systemd = dependency('systemd', required: get_option('systemd')) tz_dep = dependency('date', default_options : [ 'use_system_tzdb=true' ], fallback: [ 'date', 'tz_dep' ]) -utfcpp = dependency('utfcpp', fallback: [ 'utfcpp', 'utfcpp_dep' ]) prefix = get_option('prefix') conf_data = configuration_data() @@ -169,8 +168,7 @@ executable( libudev, libmpdclient, gtk_layer_shell, - tz_dep, - utfcpp + tz_dep ], include_directories: [include_directories('include')], install: true, diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index f918137..5efca40 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -1,6 +1,5 @@ #include "modules/clock.hpp" #include -#include using zoned_time = date::zoned_time; @@ -11,31 +10,18 @@ struct waybar_time { namespace { -size_t utf8_strlen(const std::string& s) { - return utf8::distance(s.begin(), s.end()); -} - -std::string utf8_substr(const std::string s, size_t len) { - utf8::iterator it(s.begin(), s.begin(), s.end()); - for (size_t i = 0; i < len; ++i) { - ++it; - } - int byte_count = it.base() - s.begin(); - return s.substr(0, byte_count); -} - 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 << ' '; - auto wd_string = date::format(locale, "%a", wd); - auto wd_string_len = utf8_strlen(wd_string); - if (wd_string_len > 2) { - wd_string = utf8_substr(wd_string, 2); - wd_string_len = 2; + 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_string_len, ' '); - os << pad << wd_string; + const std::string pad(2 - wd_len, ' '); + os << pad << wd_ustring; } while (++wd != first_dow); os << "\n"; } diff --git a/subprojects/utfcpp.wrap b/subprojects/utfcpp.wrap deleted file mode 100644 index 51cf131..0000000 --- a/subprojects/utfcpp.wrap +++ /dev/null @@ -1,9 +0,0 @@ -[wrap-file] -directory = utfcpp-2.3.5 -source_url = https://github.com/nemtrif/utfcpp/archive/v2.3.5.zip -source_filename = utfcpp-2.3.5.zip -source_hash = 1d5cb7d908202d734ec35b84087400013f352ffb4612e978ffb948986b76df14 - -patch_url = https://github.com/mesonbuild/utfcpp/releases/download/2.3.5-1/utfcpp.zip -patch_filename = utfcpp-2.3.5-1-wrap.zip -patch_hash = 5f504de947b34bb5995fcdb66a1ea1392288076a9400ead829da79e73441a13f From f6b200568742cbd94dc8cac17e7ac6728cc82008 Mon Sep 17 00:00:00 2001 From: Skirmantas Kligys Date: Sun, 2 Feb 2020 14:44:26 -0800 Subject: [PATCH 3/7] Cache calendar tooltip text to reduce computations. --- src/modules/clock.cpp | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 5efca40..e719353 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -1,4 +1,5 @@ #include "modules/clock.hpp" +#include #include using zoned_time = date::zoned_time; @@ -26,9 +27,28 @@ void weekdays_header(const std::locale& locale, const date::weekday& 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_; + } +}; + +std::mutex cached_calendar_mutex; // protects cached_calendar. +CachedCalendar cached_calendar; + std::string calendar_text(const waybar_time& wtime, const date::weekday& first_dow) { const auto daypoint = date::floor(wtime.ztime.get_local_time()); const auto ymd = date::year_month_day(daypoint); + + const std::lock_guard lock(cached_calendar_mutex); + if (cached_calendar.ymd == ymd) { + return cached_calendar.text; + } + const date::year_month ym(ymd.year(), ymd.month()); const auto curr_day = ymd.day(); @@ -63,7 +83,9 @@ std::string calendar_text(const waybar_time& wtime, const date::weekday& first_d } } - return os.str(); + auto result = os.str(); + cached_calendar.set(ymd, result); + return result; } } From 218bb3bc2bb607b5c2978696d24ddf3f32203454 Mon Sep 17 00:00:00 2001 From: Skirmantas Kligys Date: Sun, 2 Feb 2020 14:55:37 -0800 Subject: [PATCH 4/7] Simpify calendar generation, single loop handles both first week and subsequent weeks. --- src/modules/clock.cpp | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index e719353..134288c 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -61,21 +61,13 @@ std::string calendar_text(const waybar_time& wtime, const date::weekday& first_d if (empty_days > 0) { os << std::string(empty_days * 3 - 1, ' '); } - auto d = date::day(1); - do { - if (wd != first_dow) os << ' '; - if (d == curr_day) { - os << "" << date::format("%e", d) << ""; - } else { - os << date::format("%e", d); - } - ++d; - } while (++wd != first_dow); - - // Following weeks. auto last_day = (ym/date::literals::last).day(); - for ( ; d <= last_day; ++d, ++wd) { - os << ((wd == first_dow) ? '\n' : ' '); + 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 { From 4c40f9c6351fc7ef18efff290879b81b680caaf6 Mon Sep 17 00:00:00 2001 From: Skirmantas Kligys Date: Mon, 3 Feb 2020 16:19:32 -0800 Subject: [PATCH 5/7] Stop using a mutex for guarding CachedCalendar. --- src/modules/clock.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 134288c..ef79274 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -1,5 +1,4 @@ #include "modules/clock.hpp" -#include #include using zoned_time = date::zoned_time; @@ -37,14 +36,11 @@ struct CachedCalendar { } }; -std::mutex cached_calendar_mutex; // protects cached_calendar. CachedCalendar cached_calendar; std::string calendar_text(const waybar_time& wtime, const date::weekday& first_dow) { const auto daypoint = date::floor(wtime.ztime.get_local_time()); const auto ymd = date::year_month_day(daypoint); - - const std::lock_guard lock(cached_calendar_mutex); if (cached_calendar.ymd == ymd) { return cached_calendar.text; } From cd2db19267214ca2422b5ecd07d7eb99d049132e Mon Sep 17 00:00:00 2001 From: Skirmantas Kligys Date: Mon, 3 Feb 2020 16:58:18 -0800 Subject: [PATCH 6/7] Detect presence, call nl_langinfo() to get first day of week. --- meson.build | 12 ++++++++++++ src/modules/clock.cpp | 23 +++++++++++++++++++++-- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/meson.build b/meson.build index 69439ac..98dd25e 100644 --- a/meson.build +++ b/meson.build @@ -42,6 +42,18 @@ if not compiler.has_header('filesystem') endif endif +code = '''#include +int main(int argc, char** argv) { + char* str; + str = nl_langinfo(_NL_TIME_WEEK_1STDAY); + str = nl_langinfo(_NL_TIME_FIRST_WEEKDAY); + return 0; +} +''' +if compiler.links(code, name : 'nl_langinfo with _NL_TIME_WEEK_1STDAY, _NL_TIME_FIRST_WEEKDAY') + add_project_arguments('-DHAVE_LANGINFO_1STDAY', language: 'cpp') +endif + add_global_arguments(cpp_args, language : 'cpp') add_global_link_arguments(cpp_link_args, language : 'cpp') diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index ef79274..8419d18 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -1,5 +1,8 @@ #include "modules/clock.hpp" #include +#ifdef HAVE_LANGINFO_1STDAY +#include +#endif using zoned_time = date::zoned_time; @@ -10,6 +13,17 @@ struct waybar_time { 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 { @@ -38,7 +52,7 @@ struct CachedCalendar { CachedCalendar cached_calendar; -std::string calendar_text(const waybar_time& wtime, const date::weekday& first_dow) { +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) { @@ -49,6 +63,11 @@ std::string calendar_text(const waybar_time& wtime, const date::weekday& first_d 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. @@ -115,7 +134,7 @@ auto waybar::modules::Clock::update() -> void { if (tooltipEnabled()) { if (config_["tooltip-format"].isString()) { - const auto calendar = calendar_text(wtime, date::Sunday); + const auto calendar = calendar_text(wtime); auto tooltip_format = config_["tooltip-format"].asString(); auto tooltip_text = fmt::format(tooltip_format, wtime, fmt::arg("calendar", calendar)); label_.set_tooltip_markup(tooltip_text); From d1f427618fbcdab4a11d84e6fde987f1b62c9bec Mon Sep 17 00:00:00 2001 From: Skirmantas Kligys Date: Wed, 5 Feb 2020 11:02:42 -0800 Subject: [PATCH 7/7] 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 e54f4d2..bb63241 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 98dd25e..8cfe5d8 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 8419d18..5655168 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