HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
os_settings_intf.hpp
1// Copyright Take Vos 2022.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
4
5#pragma once
6
7#include "theme_mode.hpp"
8#include "subpixel_orientation.hpp"
9#include "../i18n/i18n.hpp"
10#include "../geometry/geometry.hpp"
11#include "../utility/utility.hpp"
12#include "../numeric/numeric.hpp"
13#include "../observer/observer.hpp"
14#include "../dispatch/dispatch.hpp"
15#include "../concurrency/concurrency.hpp"
16#include "../telemetry/telemetry.hpp"
17#include "../time/time.hpp"
18#include "../char_maps/char_maps.hpp" // XXX #616
19#include "../macros.hpp"
20#include <vector>
21#include <mutex>
22#include <expected>
23#include <system_error>
24#include <chrono>
25#include <atomic>
26
27hi_export_module(hikogui.settings.os_settings : intf);
28
29hi_export namespace hi::inline v1 {
30
32public:
37 [[nodiscard]] static std::vector<language_tag> language_tags() noexcept
38 {
39 hi_axiom(_populated.load(std::memory_order::acquire));
40 auto const lock = std::scoped_lock(_mutex);
41 return _language_tags;
42 }
43
48 [[nodiscard]] static std::locale locale() noexcept
49 {
50 hi_axiom(_populated.load(std::memory_order::acquire));
51 auto const lock = std::scoped_lock(_mutex);
52 return _locale;
53 }
54
55
60 [[nodiscard]] static bool left_to_right() noexcept
61 {
62 hi_axiom(_populated.load(std::memory_order::acquire));
63 return _left_to_right.load(std::memory_order::relaxed);
64 }
65
71 [[nodiscard]] static hi::alignment alignment(hi::alignment rhs) noexcept
72 {
73 return left_to_right() ? rhs : mirror(rhs);
74 }
75
82 {
83 return left_to_right() ? rhs : mirror(rhs);
84 }
85
88 [[nodiscard]] static hi::theme_mode theme_mode() noexcept
89 {
90 hi_axiom(_populated.load(std::memory_order::acquire));
91 return _theme_mode.load(std::memory_order::relaxed);
92 }
93
96 [[nodiscard]] static hi::subpixel_orientation subpixel_orientation() noexcept
97 {
98 hi_axiom(_populated.load(std::memory_order::acquire));
99 return _subpixel_orientation.load(std::memory_order::relaxed);
100 }
101
112 [[nodiscard]] static bool uniform_HDR() noexcept
113 {
114 hi_axiom(_populated.load(std::memory_order::acquire));
115 return _uniform_HDR.load(std::memory_order::relaxed);
116 }
117
120 [[nodiscard]] static std::chrono::milliseconds double_click_interval() noexcept
121 {
122 hi_axiom(_populated.load(std::memory_order::acquire));
123 return _double_click_interval.load(std::memory_order::relaxed);
124 }
125
128 [[nodiscard]] static float double_click_distance() noexcept
129 {
130 hi_axiom(_populated.load(std::memory_order::acquire));
131 return _double_click_distance.load(std::memory_order::relaxed);
132 }
133
138 [[nodiscard]] static std::chrono::milliseconds keyboard_repeat_delay() noexcept
139 {
140 hi_axiom(_populated.load(std::memory_order::acquire));
141 return _keyboard_repeat_delay.load(std::memory_order::relaxed);
142 }
143
149 {
150 hi_axiom(_populated.load(std::memory_order::acquire));
151 return _keyboard_repeat_interval.load(std::memory_order::relaxed);
152 }
153
158 [[nodiscard]] static std::chrono::milliseconds cursor_blink_delay() noexcept
159 {
160 hi_axiom(_populated.load(std::memory_order::acquire));
161 return _cursor_blink_delay.load(std::memory_order::relaxed);
162 }
163
169 [[nodiscard]] static std::chrono::milliseconds cursor_blink_interval() noexcept
170 {
171 hi_axiom(_populated.load(std::memory_order::acquire));
172 return _cursor_blink_interval.load(std::memory_order::relaxed);
173 }
174
179 [[nodiscard]] static float minimum_window_width() noexcept
180 {
181 hi_axiom(_populated.load(std::memory_order::acquire));
182 return _minimum_window_width.load(std::memory_order::relaxed);
183 }
184
189 [[nodiscard]] static float minimum_window_height() noexcept
190 {
191 hi_axiom(_populated.load(std::memory_order::acquire));
192 return _minimum_window_height.load(std::memory_order::relaxed);
193 }
194
199 [[nodiscard]] static float maximum_window_width() noexcept
200 {
201 hi_axiom(_populated.load(std::memory_order::acquire));
202 return _maximum_window_width.load(std::memory_order::relaxed);
203 }
204
209 [[nodiscard]] static float maximum_window_height() noexcept
210 {
211 hi_axiom(_populated.load(std::memory_order::acquire));
212 return _maximum_window_height.load(std::memory_order::relaxed);
213 }
214
219 [[nodiscard]] static aarectangle primary_monitor_rectangle() noexcept
220 {
221 hi_axiom(_populated.load(std::memory_order::acquire));
222 auto const lock = std::scoped_lock(_mutex);
223 return _primary_monitor_rectangle;
224 }
225
228 [[nodiscard]] static uintptr_t primary_monitor_id() noexcept
229 {
230 hi_axiom(_populated.load(std::memory_order::acquire));
231 return _primary_monitor_id.load(std::memory_order::relaxed);
232 }
233
238 [[nodiscard]] static aarectangle desktop_rectangle() noexcept
239 {
240 hi_axiom(_populated.load(std::memory_order::acquire));
241 auto const lock = std::scoped_lock(_mutex);
242 return _desktop_rectangle;
243 }
244
249 [[nodiscard]] static hi::policy policy() noexcept
250 {
251 return policy::unspecified;
252 }
253
258 [[nodiscard]] static hi::policy gpu_policy() noexcept
259 {
260 hi_axiom(_populated.load(std::memory_order::acquire));
261 return _gpu_policy.load(std::memory_order::relaxed);
262 }
263
285 [[nodiscard]] static std::vector<uuid> preferred_gpus(hi::policy performance_policy) noexcept;
286
287 template<forward_of<void()> Func>
288 [[nodiscard]] static callback<void()> subscribe(Func &&func, callback_flags flags = callback_flags::synchronous) noexcept
289 {
290 auto const lock = std::scoped_lock(_mutex);
291 return _notifier.subscribe(std::forward<Func>(func), flags);
292 }
293
298 static bool start_subsystem() noexcept
299 {
300 return hi::start_subsystem(_started, false, subsystem_init, subsystem_deinit);
301 }
302
305 static void gather() noexcept
306 {
307 auto const lock = std::scoped_lock(_mutex);
308 auto setting_has_changed = false;
309
310 auto const current_time = std::chrono::utc_clock::now();
311 if (current_time < _gather_last_time + gather_minimum_interval) {
312 return;
313 }
314 _gather_last_time = current_time;
315
316 try {
317 auto language_tags = gather_languages();
318 if (language_tags.empty()) {
319 // If not language is configured on the system, use English as default.
320 language_tags.emplace_back("en-Latn-US");
321 }
322
323 // Add all the variants of languages, for searching into translations.
324 language_tags = variants(language_tags);
325
326 if (compare_store(_language_tags, language_tags)) {
327 setting_has_changed = true;
328 hi_log_info("OS language order has changed: {}", _language_tags);
329 }
330 } catch (std::exception const& e) {
331 hi_log_error("Failed to get OS language: {}", e.what());
332 }
333
334 if (auto optional_locale = gather_locale()) {
335 if (compare_store(_locale, *optional_locale)) {
336 setting_has_changed = true;
337 hi_log_info("OS locale has changed: {}", _locale.name());
338 }
339 } else {
340 hi_log_error("Failed to get OS locale: {}", optional_locale.error().message());
341 }
342
343 if (compare_store(_left_to_right, gather_left_to_right())) {
344 setting_has_changed = true;
345 hi_log_info("OS mirrored-GUI has changed: {}", not _left_to_right);
346 }
347
348 try {
349 if (compare_store(_theme_mode, gather_theme_mode())) {
350 setting_has_changed = true;
351 hi_log_info("OS theme-mode has changed: {}", _theme_mode.load());
352 }
353 } catch (std::exception const& e) {
354 hi_log_error("Failed to get OS theme-mode: {}", e.what());
355 }
356
357 try {
358 if (compare_store(_subpixel_orientation, gather_subpixel_orientation())) {
359 setting_has_changed = true;
360 hi_log_info("OS sub-pixel orientation has changed: {}", _subpixel_orientation.load());
361 }
362 } catch (std::exception const& e) {
363 hi_log_error("Failed to get OS sub-pixel orientation: {}", e.what());
364 }
365
366 try {
367 if (compare_store(_uniform_HDR, gather_uniform_HDR())) {
368 setting_has_changed = true;
369 hi_log_info("OS uniform-HDR has changed: {}", _uniform_HDR.load());
370 }
371 } catch (std::exception const& e) {
372 hi_log_error("Failed to get OS uniform-HDR: {}", e.what());
373 }
374
375 try {
376 if (compare_store(_double_click_interval, gather_double_click_interval())) {
377 setting_has_changed = true;
378 hi_log_info("OS double click interval has changed: {}", _double_click_interval.load());
379 }
380 } catch (std::exception const& e) {
381 hi_log_error("Failed to get OS double click interval: {}", e.what());
382 }
383
384 try {
385 if (compare_store(_double_click_distance, gather_double_click_distance())) {
386 setting_has_changed = true;
387 hi_log_info("OS double click distance has changed: {}", _double_click_distance.load());
388 }
389 } catch (std::exception const& e) {
390 hi_log_error("Failed to get OS double click distance: {}", e.what());
391 }
392
393 try {
394 if (compare_store(_keyboard_repeat_delay, gather_keyboard_repeat_delay())) {
395 setting_has_changed = true;
396 hi_log_info("OS keyboard repeat delay has changed: {}", _keyboard_repeat_delay.load());
397 }
398 } catch (std::exception const& e) {
399 hi_log_error("Failed to get OS keyboard repeat delay: {}", e.what());
400 }
401
402 try {
403 if (compare_store(_keyboard_repeat_interval, gather_keyboard_repeat_interval())) {
404 setting_has_changed = true;
405 hi_log_info("OS keyboard repeat interval has changed: {}", _keyboard_repeat_interval.load());
406 }
407 } catch (std::exception const& e) {
408 hi_log_error("Failed to get OS keyboard repeat interval: {}", e.what());
409 }
410
411 try {
412 if (compare_store(_cursor_blink_interval, gather_cursor_blink_interval())) {
413 setting_has_changed = true;
414 if (_cursor_blink_interval.load() < std::chrono::minutes(1)) {
415 hi_log_info("OS cursor blink interval has changed: {}", _cursor_blink_interval.load());
416 } else {
417 hi_log_info("OS cursor blink interval has changed: no-blinking");
418 }
419 }
420 } catch (std::exception const& e) {
421 hi_log_error("Failed to get OS cursor blink interval: {}", e.what());
422 }
423
424 try {
425 if (compare_store(_cursor_blink_delay, gather_cursor_blink_delay())) {
426 setting_has_changed = true;
427 hi_log_info("OS cursor blink delay has changed: {}", _cursor_blink_delay.load());
428 }
429 } catch (std::exception const& e) {
430 hi_log_error("Failed to get OS cursor blink delay: {}", e.what());
431 }
432
433 try {
434 if (compare_store(_minimum_window_width, gather_minimum_window_width())) {
435 setting_has_changed = true;
436 hi_log_info("OS minimum window width has changed: {}", _minimum_window_width.load());
437 }
438 } catch (std::exception const& e) {
439 hi_log_error("Failed to get OS minimum window width: {}", e.what());
440 }
441
442 try {
443 if (compare_store(_minimum_window_height, gather_minimum_window_height())) {
444 setting_has_changed = true;
445 hi_log_info("OS minimum window height has changed: {}", _minimum_window_height.load());
446 }
447 } catch (std::exception const& e) {
448 hi_log_error("Failed to get OS minimum window height: {}", e.what());
449 }
450
451 try {
452 if (compare_store(_maximum_window_width, gather_maximum_window_width())) {
453 setting_has_changed = true;
454 hi_log_info("OS maximum window width has changed: {}", _maximum_window_width.load());
455 }
456 } catch (std::exception const& e) {
457 hi_log_error("Failed to get OS maximum window width: {}", e.what());
458 }
459
460 try {
461 if (compare_store(_maximum_window_height, gather_maximum_window_height())) {
462 setting_has_changed = true;
463 hi_log_info("OS maximum window height has changed: {}", _maximum_window_height.load());
464 }
465 } catch (std::exception const& e) {
466 hi_log_error("Failed to get OS maximum window height: {}", e.what());
467 }
468
469 try {
470 auto const primary_monitor_id = gather_primary_monitor_id();
471 if (compare_store(_primary_monitor_id, primary_monitor_id)) {
472 setting_has_changed = true;
473 hi_log_info("OS primary monitor id has changed: {}, updating vsync source", primary_monitor_id);
474 loop::main().set_vsync_monitor_id(primary_monitor_id);
475 }
476 } catch (std::exception const& e) {
477 hi_log_error("Failed to get OS primary monitor id: {}", e.what());
478 }
479
480 try {
481 if (compare_store(_primary_monitor_rectangle, gather_primary_monitor_rectangle())) {
482 setting_has_changed = true;
483 hi_log_info("OS primary monitor rectangle has changed: {}", _primary_monitor_rectangle);
484 }
485 } catch (std::exception const& e) {
486 hi_log_error("Failed to get OS primary monitor rectangle: {}", e.what());
487 }
488
489 try {
490 if (compare_store(_desktop_rectangle, gather_desktop_rectangle())) {
491 setting_has_changed = true;
492 hi_log_info("OS desktop rectangle has changed: {}", _desktop_rectangle);
493 }
494 } catch (std::exception const& e) {
495 hi_log_error("Failed to get OS desktop rectangle: {}", e.what());
496 }
497
498 try {
499 if (compare_store(_gpu_policy, gather_gpu_policy())) {
500 setting_has_changed = true;
501 hi_log_info("GPU policy has changed: {}", _gpu_policy.load());
502 }
503 } catch (std::exception const& e) {
504 hi_log_error("Failed to get the GPU policy: {}", e.what());
505 }
506
507 _populated.store(true, std::memory_order::release);
508 if (setting_has_changed) {
509 _notifier();
510 }
511 }
512
513private:
514 constexpr static std::chrono::duration gather_interval = std::chrono::seconds(5);
515 constexpr static std::chrono::duration gather_minimum_interval = std::chrono::seconds(1);
516
517 static inline std::atomic<bool> _started = false;
518 static inline std::atomic<bool> _populated = false;
519 static inline unfair_mutex _mutex;
520 static inline utc_nanoseconds _gather_last_time;
521
522 static inline notifier<void()> _notifier;
523
524 static inline std::vector<language_tag> _language_tags = {};
525 static inline std::locale _locale = std::locale{""};
526 static inline std::atomic<bool> _left_to_right = true;
527 static inline std::atomic<hi::theme_mode> _theme_mode = theme_mode::dark;
528 static inline std::atomic<bool> _uniform_HDR = false;
529 static inline std::atomic<hi::subpixel_orientation> _subpixel_orientation = hi::subpixel_orientation::unknown;
530 static inline std::atomic<std::chrono::milliseconds> _double_click_interval = std::chrono::milliseconds(500);
531 static inline std::atomic<float> _double_click_distance = 4.0f;
532 static inline std::atomic<std::chrono::milliseconds> _keyboard_repeat_delay = std::chrono::milliseconds(250);
533 static inline std::atomic<std::chrono::milliseconds> _keyboard_repeat_interval = std::chrono::milliseconds(33);
534 static inline std::atomic<std::chrono::milliseconds> _cursor_blink_interval = std::chrono::milliseconds(1000);
535 static inline std::atomic<std::chrono::milliseconds> _cursor_blink_delay = std::chrono::milliseconds(1000);
536 static inline std::atomic<float> _minimum_window_width = 40.0f;
537 static inline std::atomic<float> _minimum_window_height = 25.0f;
538 static inline std::atomic<float> _maximum_window_width = 1920.0f;
539 static inline std::atomic<float> _maximum_window_height = 1080.0f;
540 static inline std::atomic<uintptr_t> _primary_monitor_id = 0;
541 static inline aarectangle _primary_monitor_rectangle = aarectangle{0.0f, 0.0f, 1920.0f, 1080.0f};
542 static inline aarectangle _desktop_rectangle = aarectangle{0.0f, 0.0f, 1920.0f, 1080.0f};
543 static inline std::atomic<hi::policy> _gpu_policy = policy::unspecified;
544
545 static inline callback<void()> _gather_cbt;
546
547 [[nodiscard]] static bool subsystem_init() noexcept
548 {
549 _gather_cbt = loop::timer().repeat_function(gather_interval, [] {
550 os_settings::gather();
551 });
552
553 return true;
554 }
555
556 static void subsystem_deinit() noexcept
557 {
558 if (_started.exchange(false)) {
559 _gather_cbt = nullptr;
560 }
561 }
562
563 [[nodiscard]] static std::vector<language_tag> gather_languages() noexcept;
564 [[nodiscard]] static std::expected<std::locale, std::error_code> gather_locale() noexcept;
565 [[nodiscard]] static bool gather_left_to_right() noexcept;
566 [[nodiscard]] static hi::theme_mode gather_theme_mode();
567 [[nodiscard]] static hi::subpixel_orientation gather_subpixel_orientation();
568 [[nodiscard]] static bool gather_uniform_HDR();
569 [[nodiscard]] static std::chrono::milliseconds gather_double_click_interval();
570 [[nodiscard]] static float gather_double_click_distance();
571 [[nodiscard]] static std::chrono::milliseconds gather_keyboard_repeat_delay();
572 [[nodiscard]] static std::chrono::milliseconds gather_keyboard_repeat_interval();
573 [[nodiscard]] static std::chrono::milliseconds gather_cursor_blink_interval();
574 [[nodiscard]] static std::chrono::milliseconds gather_cursor_blink_delay();
575 [[nodiscard]] static float gather_minimum_window_width();
576 [[nodiscard]] static float gather_minimum_window_height();
577 [[nodiscard]] static float gather_maximum_window_width();
578 [[nodiscard]] static float gather_maximum_window_height();
579 [[nodiscard]] static uintptr_t gather_primary_monitor_id();
580 [[nodiscard]] static aarectangle gather_primary_monitor_rectangle();
581 [[nodiscard]] static aarectangle gather_desktop_rectangle();
582 [[nodiscard]] static hi::policy gather_gpu_policy();
583};
584
585} // namespace hi::inline v1
T::value_type start_subsystem(T &check_variable, typename T::value_type off_value, typename T::value_type(*init_function)(), void(*deinit_function)())
Start a sub-system.
Definition subsystem.hpp:117
horizontal_alignment
Horizontal alignment.
Definition alignment.hpp:102
STL namespace.
DOXYGEN BUG.
Definition algorithm_misc.hpp:20
callback_flags
Definition callback_flags.hpp:15
The HikoGUI namespace.
Definition recursive_iterator.hpp:15
policy
The performance policy to use.
Definition policy.hpp:18
constexpr Out narrow_cast(In const &rhs) noexcept
Cast numeric values without loss of precision.
Definition cast.hpp:378
A notifier which can be used to call a set of registered callbacks.
Definition notifier.hpp:26
Horizontal/Vertical alignment combination.
Definition alignment.hpp:244
Definition os_settings_intf.hpp:31
static std::chrono::milliseconds keyboard_repeat_delay() noexcept
Get the delay before the keyboard starts repeating.
Definition os_settings_intf.hpp:138
static aarectangle primary_monitor_rectangle() noexcept
Get the rectangle of the primary monitor.
Definition os_settings_intf.hpp:219
static bool start_subsystem() noexcept
Get the global os_settings instance.
Definition os_settings_intf.hpp:298
static hi::alignment alignment(hi::alignment rhs) noexcept
Get the alignment based on the writing direction.
Definition os_settings_intf.hpp:71
static float maximum_window_height() noexcept
The maximum height a window is allowed to be.
Definition os_settings_intf.hpp:209
static std::chrono::milliseconds keyboard_repeat_interval() noexcept
Get the keyboard repeat interval.
Definition os_settings_intf.hpp:148
static hi::subpixel_orientation subpixel_orientation() noexcept
Get the configured light/dark theme mode.
Definition os_settings_intf.hpp:96
static std::chrono::milliseconds cursor_blink_delay() noexcept
Get the cursor blink delay.
Definition os_settings_intf.hpp:158
static std::locale locale() noexcept
Get the current local.
Definition os_settings_intf.hpp:48
static aarectangle desktop_rectangle() noexcept
Get the rectangle describing the desktop.
Definition os_settings_intf.hpp:238
static void gather() noexcept
Gather the settings from the operating system now.
Definition os_settings_intf.hpp:305
static float double_click_distance() noexcept
Get the distance from the previous mouse position to detect double click.
Definition os_settings_intf.hpp:128
static uintptr_t primary_monitor_id() noexcept
Get an opaque id of the primary monitor.
Definition os_settings_intf.hpp:228
static hi::policy policy() noexcept
Get the global performance policy.
Definition os_settings_intf.hpp:249
static float minimum_window_width() noexcept
The minimum width a window is allowed to be.
Definition os_settings_intf.hpp:179
static float maximum_window_width() noexcept
The maximum width a window is allowed to be.
Definition os_settings_intf.hpp:199
static bool left_to_right() noexcept
Check if the configured writing direction is left-to-right.
Definition os_settings_intf.hpp:60
static float minimum_window_height() noexcept
The minimum height a window is allowed to be.
Definition os_settings_intf.hpp:189
static std::chrono::milliseconds double_click_interval() noexcept
Get the mouse double click interval.
Definition os_settings_intf.hpp:120
static bool uniform_HDR() noexcept
Whether SDR and HDR application can coexists on the same display.
Definition os_settings_intf.hpp:112
static hi::horizontal_alignment alignment(hi::horizontal_alignment rhs) noexcept
Get the alignment based on the writing direction.
Definition os_settings_intf.hpp:81
static hi::policy gpu_policy() noexcept
Get the policy for selecting a GPU.
Definition os_settings_intf.hpp:258
static std::vector< language_tag > language_tags() noexcept
Get the language tags for the configured languages.
Definition os_settings_intf.hpp:37
static hi::theme_mode theme_mode() noexcept
Get the configured light/dark theme mode.
Definition os_settings_intf.hpp:88
static std::chrono::milliseconds cursor_blink_interval() noexcept
Get the cursor blink interval.
Definition os_settings_intf.hpp:169
T exchange(T... args)
T what(T... args)