Cache calendar per clock instance, weekdays properly handle locales.
This commit is contained in:
		
							parent
							
								
									3c268d83c2
								
							
						
					
					
						commit
						d1f427618f
					
				|  | @ -12,6 +12,11 @@ | ||||||
| 
 | 
 | ||||||
| namespace waybar::modules { | namespace waybar::modules { | ||||||
| 
 | 
 | ||||||
|  | struct waybar_time { | ||||||
|  |   std::locale locale; | ||||||
|  |   date::zoned_time<std::chrono::system_clock::duration> ztime; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| class Clock : public ALabel { | class Clock : public ALabel { | ||||||
|  public: |  public: | ||||||
|   Clock(const std::string&, const Json::Value&); |   Clock(const std::string&, const Json::Value&); | ||||||
|  | @ -23,6 +28,12 @@ class Clock : public ALabel { | ||||||
|   std::locale locale_; |   std::locale locale_; | ||||||
|   const date::time_zone* time_zone_; |   const date::time_zone* time_zone_; | ||||||
|   bool fixed_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
 | }  // namespace waybar::modules
 | ||||||
|  |  | ||||||
							
								
								
									
										10
									
								
								meson.build
								
								
								
								
							
							
						
						
									
										10
									
								
								meson.build
								
								
								
								
							|  | @ -42,11 +42,15 @@ if not compiler.has_header('filesystem') | ||||||
|     endif |     endif | ||||||
| endif | endif | ||||||
| 
 | 
 | ||||||
| code = '''#include<langinfo.h> | code = ''' | ||||||
|  | #include <langinfo.h> | ||||||
|  | #include <locale.h> | ||||||
| int main(int argc, char** argv) { | int main(int argc, char** argv) { | ||||||
|  |     locale_t locale = newlocale(LC_ALL, "en_US.UTF-8", nullptr); | ||||||
|     char* str; |     char* str; | ||||||
|     str = nl_langinfo(_NL_TIME_WEEK_1STDAY); |     str = nl_langinfo_l(_NL_TIME_WEEK_1STDAY, locale); | ||||||
|     str = nl_langinfo(_NL_TIME_FIRST_WEEKDAY); |     str = nl_langinfo_l(_NL_TIME_FIRST_WEEKDAY, locale); | ||||||
|  |     freelocale(locale); | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
| ''' | ''' | ||||||
|  |  | ||||||
|  | @ -1,101 +1,12 @@ | ||||||
| #include "modules/clock.hpp" | #include "modules/clock.hpp" | ||||||
| #include <sstream> | #include <sstream> | ||||||
|  | #include <type_traits> | ||||||
| #ifdef HAVE_LANGINFO_1STDAY | #ifdef HAVE_LANGINFO_1STDAY | ||||||
| #include <langinfo.h> | #include <langinfo.h> | ||||||
|  | #include <locale.h> | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| using zoned_time = date::zoned_time<std::chrono::system_clock::duration>; | using waybar::modules::waybar_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<date::days>(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 << "<b><u>" << date::format("%e", d) << "</u></b>"; |  | ||||||
|     } else { |  | ||||||
|       os << date::format("%e", d); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   auto result = os.str(); |  | ||||||
|   cached_calendar.set(ymd, result); |  | ||||||
|   return result; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) | waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) | ||||||
|     : ALabel(config, "clock", id, "{:%H:%M}", 60) |     : 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<date::days>(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 << "<b><u>" << date::format("%e", d) << "</u></b>"; | ||||||
|  |     } 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 <auto fn> | ||||||
|  | using deleter_from_fn = std::integral_constant<decltype(fn), fn>; | ||||||
|  | 
 | ||||||
|  | template <typename T, auto fn> | ||||||
|  | using deleting_unique_ptr = std::unique_ptr<T, deleter_from_fn<fn>>; | ||||||
|  | #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<std::remove_pointer<locale_t>::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 <> | template <> | ||||||
| struct fmt::formatter<waybar_time> : fmt::formatter<std::tm> { | struct fmt::formatter<waybar_time> : fmt::formatter<std::tm> { | ||||||
|   template <typename FormatContext> |   template <typename FormatContext> | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue