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:
43 [[nodiscard]] static device_type device_type() noexcept
44 {
45 hi_axiom(_populated.load(std::memory_order::acquire));
46 return _device_type;
47 }
48
49 // [[nodiscard]] static device_type device_mode() noexcept
50
55 [[nodiscard]] static std::vector<language_tag> language_tags() noexcept
56 {
57 hi_axiom(_populated.load(std::memory_order::acquire));
58 auto const lock = std::scoped_lock(_mutex);
59 return _language_tags;
60 }
61
66 [[nodiscard]] static std::locale locale() noexcept
67 {
68 hi_axiom(_populated.load(std::memory_order::acquire));
69 auto const lock = std::scoped_lock(_mutex);
70 return _locale;
71 }
72
73
78 [[nodiscard]] static bool left_to_right() noexcept
79 {
80 hi_axiom(_populated.load(std::memory_order::acquire));
81 return _left_to_right.load(std::memory_order::relaxed);
82 }
83
89 [[nodiscard]] static hi::alignment alignment(hi::alignment rhs) noexcept
90 {
91 return left_to_right() ? rhs : mirror(rhs);
92 }
93
100 {
101 return left_to_right() ? rhs : mirror(rhs);
102 }
103
106 [[nodiscard]] static hi::theme_mode theme_mode() noexcept
107 {
108 hi_axiom(_populated.load(std::memory_order::acquire));
109 return _theme_mode.load(std::memory_order::relaxed);
110 }
111
114 [[nodiscard]] static hi::subpixel_orientation subpixel_orientation() noexcept
115 {
116 hi_axiom(_populated.load(std::memory_order::acquire));
117 return _subpixel_orientation.load(std::memory_order::relaxed);
118 }
119
130 [[nodiscard]] static bool uniform_HDR() noexcept
131 {
132 hi_axiom(_populated.load(std::memory_order::acquire));
133 return _uniform_HDR.load(std::memory_order::relaxed);
134 }
135
138 [[nodiscard]] static std::chrono::milliseconds double_click_interval() noexcept
139 {
140 hi_axiom(_populated.load(std::memory_order::acquire));
141 return _double_click_interval.load(std::memory_order::relaxed);
142 }
143
146 [[nodiscard]] static float double_click_distance() noexcept
147 {
148 hi_axiom(_populated.load(std::memory_order::acquire));
149 return _double_click_distance.load(std::memory_order::relaxed);
150 }
151
156 [[nodiscard]] static std::chrono::milliseconds keyboard_repeat_delay() noexcept
157 {
158 hi_axiom(_populated.load(std::memory_order::acquire));
159 return _keyboard_repeat_delay.load(std::memory_order::relaxed);
160 }
161
167 {
168 hi_axiom(_populated.load(std::memory_order::acquire));
169 return _keyboard_repeat_interval.load(std::memory_order::relaxed);
170 }
171
176 [[nodiscard]] static std::chrono::milliseconds cursor_blink_delay() noexcept
177 {
178 hi_axiom(_populated.load(std::memory_order::acquire));
179 return _cursor_blink_delay.load(std::memory_order::relaxed);
180 }
181
187 [[nodiscard]] static std::chrono::milliseconds cursor_blink_interval() noexcept
188 {
189 hi_axiom(_populated.load(std::memory_order::acquire));
190 return _cursor_blink_interval.load(std::memory_order::relaxed);
191 }
192
197 [[nodiscard]] static float minimum_window_width() noexcept
198 {
199 hi_axiom(_populated.load(std::memory_order::acquire));
200 return _minimum_window_width.load(std::memory_order::relaxed);
201 }
202
207 [[nodiscard]] static float minimum_window_height() noexcept
208 {
209 hi_axiom(_populated.load(std::memory_order::acquire));
210 return _minimum_window_height.load(std::memory_order::relaxed);
211 }
212
217 [[nodiscard]] static float maximum_window_width() noexcept
218 {
219 hi_axiom(_populated.load(std::memory_order::acquire));
220 return _maximum_window_width.load(std::memory_order::relaxed);
221 }
222
227 [[nodiscard]] static float maximum_window_height() noexcept
228 {
229 hi_axiom(_populated.load(std::memory_order::acquire));
230 return _maximum_window_height.load(std::memory_order::relaxed);
231 }
232
237 [[nodiscard]] static aarectangle primary_monitor_rectangle() noexcept
238 {
239 hi_axiom(_populated.load(std::memory_order::acquire));
240 auto const lock = std::scoped_lock(_mutex);
241 return _primary_monitor_rectangle;
242 }
243
246 [[nodiscard]] static uintptr_t primary_monitor_id() noexcept
247 {
248 hi_axiom(_populated.load(std::memory_order::acquire));
249 return _primary_monitor_id.load(std::memory_order::relaxed);
250 }
251
256 [[nodiscard]] static aarectangle desktop_rectangle() noexcept
257 {
258 hi_axiom(_populated.load(std::memory_order::acquire));
259 auto const lock = std::scoped_lock(_mutex);
260 return _desktop_rectangle;
261 }
262
267 [[nodiscard]] static hi::policy policy() noexcept
268 {
269 return policy::unspecified;
270 }
271
276 [[nodiscard]] static hi::policy gpu_policy() noexcept
277 {
278 hi_axiom(_populated.load(std::memory_order::acquire));
279 return _gpu_policy.load(std::memory_order::relaxed);
280 }
281
303 [[nodiscard]] static std::vector<uuid> preferred_gpus(hi::policy performance_policy) noexcept;
304
305 template<forward_of<void()> Func>
306 [[nodiscard]] static callback<void()> subscribe(Func &&func, callback_flags flags = callback_flags::synchronous) noexcept
307 {
308 auto const lock = std::scoped_lock(_mutex);
309 return _notifier.subscribe(std::forward<Func>(func), flags);
310 }
311
316 static bool start_subsystem() noexcept
317 {
318 return hi::start_subsystem(_started, false, subsystem_init, subsystem_deinit);
319 }
320
323 static void gather() noexcept
324 {
325 auto const lock = std::scoped_lock(_mutex);
326 auto setting_has_changed = false;
327
328 auto const current_time = std::chrono::utc_clock::now();
329 if (current_time < _gather_last_time + gather_minimum_interval) {
330 return;
331 }
332 _gather_last_time = current_time;
333
334 if (auto optional_device_type = gather_device_type()) {
335 if (compare_store(_device_type, *optional_device_type)) {
336 setting_has_changed = true;
337 hi_log_info("OS device type has changed: {}", *optional_device_type);
338 }
339
340 } else {
341 hi_log_error("Failed to get device type: {}", optional_device_type.error().message());
342 }
343
344 try {
345 auto language_tags = gather_languages();
346 if (language_tags.empty()) {
347 // If not language is configured on the system, use English as default.
348 language_tags.emplace_back("en-Latn-US");
349 }
350
351 // Add all the variants of languages, for searching into translations.
352 language_tags = variants(language_tags);
353
354 if (compare_store(_language_tags, language_tags)) {
355 setting_has_changed = true;
356 hi_log_info("OS language order has changed: {}", _language_tags);
357 }
358 } catch (std::exception const& e) {
359 hi_log_error("Failed to get OS language: {}", e.what());
360 }
361
362 if (auto optional_locale = gather_locale()) {
363 if (compare_store(_locale, *optional_locale)) {
364 setting_has_changed = true;
365 hi_log_info("OS locale has changed: {}", _locale.name());
366 }
367 } else {
368 hi_log_error("Failed to get OS locale: {}", optional_locale.error().message());
369 }
370
371 if (compare_store(_left_to_right, gather_left_to_right())) {
372 setting_has_changed = true;
373 hi_log_info("OS mirrored-GUI has changed: {}", not _left_to_right);
374 }
375
376 try {
377 if (compare_store(_theme_mode, gather_theme_mode())) {
378 setting_has_changed = true;
379 hi_log_info("OS theme-mode has changed: {}", _theme_mode.load());
380 }
381 } catch (std::exception const& e) {
382 hi_log_error("Failed to get OS theme-mode: {}", e.what());
383 }
384
385 try {
386 if (compare_store(_subpixel_orientation, gather_subpixel_orientation())) {
387 setting_has_changed = true;
388 hi_log_info("OS sub-pixel orientation has changed: {}", _subpixel_orientation.load());
389 }
390 } catch (std::exception const& e) {
391 hi_log_error("Failed to get OS sub-pixel orientation: {}", e.what());
392 }
393
394 try {
395 if (compare_store(_uniform_HDR, gather_uniform_HDR())) {
396 setting_has_changed = true;
397 hi_log_info("OS uniform-HDR has changed: {}", _uniform_HDR.load());
398 }
399 } catch (std::exception const& e) {
400 hi_log_error("Failed to get OS uniform-HDR: {}", e.what());
401 }
402
403 try {
404 if (compare_store(_double_click_interval, gather_double_click_interval())) {
405 setting_has_changed = true;
406 hi_log_info("OS double click interval has changed: {}", _double_click_interval.load());
407 }
408 } catch (std::exception const& e) {
409 hi_log_error("Failed to get OS double click interval: {}", e.what());
410 }
411
412 try {
413 if (compare_store(_double_click_distance, gather_double_click_distance())) {
414 setting_has_changed = true;
415 hi_log_info("OS double click distance has changed: {}", _double_click_distance.load());
416 }
417 } catch (std::exception const& e) {
418 hi_log_error("Failed to get OS double click distance: {}", e.what());
419 }
420
421 try {
422 if (compare_store(_keyboard_repeat_delay, gather_keyboard_repeat_delay())) {
423 setting_has_changed = true;
424 hi_log_info("OS keyboard repeat delay has changed: {}", _keyboard_repeat_delay.load());
425 }
426 } catch (std::exception const& e) {
427 hi_log_error("Failed to get OS keyboard repeat delay: {}", e.what());
428 }
429
430 try {
431 if (compare_store(_keyboard_repeat_interval, gather_keyboard_repeat_interval())) {
432 setting_has_changed = true;
433 hi_log_info("OS keyboard repeat interval has changed: {}", _keyboard_repeat_interval.load());
434 }
435 } catch (std::exception const& e) {
436 hi_log_error("Failed to get OS keyboard repeat interval: {}", e.what());
437 }
438
439 try {
440 if (compare_store(_cursor_blink_interval, gather_cursor_blink_interval())) {
441 setting_has_changed = true;
442 if (_cursor_blink_interval.load() < std::chrono::minutes(1)) {
443 hi_log_info("OS cursor blink interval has changed: {}", _cursor_blink_interval.load());
444 } else {
445 hi_log_info("OS cursor blink interval has changed: no-blinking");
446 }
447 }
448 } catch (std::exception const& e) {
449 hi_log_error("Failed to get OS cursor blink interval: {}", e.what());
450 }
451
452 try {
453 if (compare_store(_cursor_blink_delay, gather_cursor_blink_delay())) {
454 setting_has_changed = true;
455 hi_log_info("OS cursor blink delay has changed: {}", _cursor_blink_delay.load());
456 }
457 } catch (std::exception const& e) {
458 hi_log_error("Failed to get OS cursor blink delay: {}", e.what());
459 }
460
461 try {
462 if (compare_store(_minimum_window_width, gather_minimum_window_width())) {
463 setting_has_changed = true;
464 hi_log_info("OS minimum window width has changed: {}", _minimum_window_width.load());
465 }
466 } catch (std::exception const& e) {
467 hi_log_error("Failed to get OS minimum window width: {}", e.what());
468 }
469
470 try {
471 if (compare_store(_minimum_window_height, gather_minimum_window_height())) {
472 setting_has_changed = true;
473 hi_log_info("OS minimum window height has changed: {}", _minimum_window_height.load());
474 }
475 } catch (std::exception const& e) {
476 hi_log_error("Failed to get OS minimum window height: {}", e.what());
477 }
478
479 try {
480 if (compare_store(_maximum_window_width, gather_maximum_window_width())) {
481 setting_has_changed = true;
482 hi_log_info("OS maximum window width has changed: {}", _maximum_window_width.load());
483 }
484 } catch (std::exception const& e) {
485 hi_log_error("Failed to get OS maximum window width: {}", e.what());
486 }
487
488 try {
489 if (compare_store(_maximum_window_height, gather_maximum_window_height())) {
490 setting_has_changed = true;
491 hi_log_info("OS maximum window height has changed: {}", _maximum_window_height.load());
492 }
493 } catch (std::exception const& e) {
494 hi_log_error("Failed to get OS maximum window height: {}", e.what());
495 }
496
497 try {
498 auto const primary_monitor_id = gather_primary_monitor_id();
499 if (compare_store(_primary_monitor_id, primary_monitor_id)) {
500 setting_has_changed = true;
501 hi_log_info("OS primary monitor id has changed: {}, updating vsync source", primary_monitor_id);
502 loop::main().set_vsync_monitor_id(primary_monitor_id);
503 }
504 } catch (std::exception const& e) {
505 hi_log_error("Failed to get OS primary monitor id: {}", e.what());
506 }
507
508 try {
509 if (compare_store(_primary_monitor_rectangle, gather_primary_monitor_rectangle())) {
510 setting_has_changed = true;
511 hi_log_info("OS primary monitor rectangle has changed: {}", _primary_monitor_rectangle);
512 }
513 } catch (std::exception const& e) {
514 hi_log_error("Failed to get OS primary monitor rectangle: {}", e.what());
515 }
516
517 try {
518 if (compare_store(_desktop_rectangle, gather_desktop_rectangle())) {
519 setting_has_changed = true;
520 hi_log_info("OS desktop rectangle has changed: {}", _desktop_rectangle);
521 }
522 } catch (std::exception const& e) {
523 hi_log_error("Failed to get OS desktop rectangle: {}", e.what());
524 }
525
526 try {
527 if (compare_store(_gpu_policy, gather_gpu_policy())) {
528 setting_has_changed = true;
529 hi_log_info("GPU policy has changed: {}", _gpu_policy.load());
530 }
531 } catch (std::exception const& e) {
532 hi_log_error("Failed to get the GPU policy: {}", e.what());
533 }
534
535 _populated.store(true, std::memory_order::release);
536 if (setting_has_changed) {
537 _notifier();
538 }
539 }
540
541private:
542 constexpr static std::chrono::duration gather_interval = std::chrono::seconds(5);
543 constexpr static std::chrono::duration gather_minimum_interval = std::chrono::seconds(1);
544
545 static inline std::atomic<bool> _started = false;
546 static inline std::atomic<bool> _populated = false;
547 static inline unfair_mutex _mutex;
548 static inline utc_nanoseconds _gather_last_time;
549
550 static inline notifier<void()> _notifier;
551
552 static inline std::atomic<hi::device_type> _device_type = hi::device_type::desktop;
553 static inline std::vector<language_tag> _language_tags = {};
554 static inline std::locale _locale = std::locale{""};
555 static inline std::atomic<bool> _left_to_right = true;
556 static inline std::atomic<hi::theme_mode> _theme_mode = theme_mode::dark;
557 static inline std::atomic<bool> _uniform_HDR = false;
558 static inline std::atomic<hi::subpixel_orientation> _subpixel_orientation = hi::subpixel_orientation::unknown;
559 static inline std::atomic<std::chrono::milliseconds> _double_click_interval = std::chrono::milliseconds(500);
560 static inline std::atomic<float> _double_click_distance = 4.0f;
561 static inline std::atomic<std::chrono::milliseconds> _keyboard_repeat_delay = std::chrono::milliseconds(250);
562 static inline std::atomic<std::chrono::milliseconds> _keyboard_repeat_interval = std::chrono::milliseconds(33);
563 static inline std::atomic<std::chrono::milliseconds> _cursor_blink_interval = std::chrono::milliseconds(1000);
564 static inline std::atomic<std::chrono::milliseconds> _cursor_blink_delay = std::chrono::milliseconds(1000);
565 static inline std::atomic<float> _minimum_window_width = 40.0f;
566 static inline std::atomic<float> _minimum_window_height = 25.0f;
567 static inline std::atomic<float> _maximum_window_width = 1920.0f;
568 static inline std::atomic<float> _maximum_window_height = 1080.0f;
569 static inline std::atomic<uintptr_t> _primary_monitor_id = 0;
570 static inline aarectangle _primary_monitor_rectangle = aarectangle{0.0f, 0.0f, 1920.0f, 1080.0f};
571 static inline aarectangle _desktop_rectangle = aarectangle{0.0f, 0.0f, 1920.0f, 1080.0f};
572 static inline std::atomic<hi::policy> _gpu_policy = policy::unspecified;
573
574 static inline callback<void()> _gather_cbt;
575
576 [[nodiscard]] static bool subsystem_init() noexcept
577 {
578 _gather_cbt = loop::timer().repeat_function(gather_interval, [] {
579 os_settings::gather();
580 });
581
582 return true;
583 }
584
585 static void subsystem_deinit() noexcept
586 {
587 if (_started.exchange(false)) {
588 _gather_cbt = nullptr;
589 }
590 }
591
592 [[nodiscard]] static std::expected<hi::device_type, std::error_code> gather_device_type() noexcept;
593 [[nodiscard]] static std::vector<language_tag> gather_languages() noexcept;
594 [[nodiscard]] static std::expected<std::locale, std::error_code> gather_locale() noexcept;
595 [[nodiscard]] static bool gather_left_to_right() noexcept;
596 [[nodiscard]] static hi::theme_mode gather_theme_mode();
597 [[nodiscard]] static hi::subpixel_orientation gather_subpixel_orientation();
598 [[nodiscard]] static bool gather_uniform_HDR();
599 [[nodiscard]] static std::chrono::milliseconds gather_double_click_interval();
600 [[nodiscard]] static float gather_double_click_distance();
601 [[nodiscard]] static std::chrono::milliseconds gather_keyboard_repeat_delay();
602 [[nodiscard]] static std::chrono::milliseconds gather_keyboard_repeat_interval();
603 [[nodiscard]] static std::chrono::milliseconds gather_cursor_blink_interval();
604 [[nodiscard]] static std::chrono::milliseconds gather_cursor_blink_delay();
605 [[nodiscard]] static float gather_minimum_window_width();
606 [[nodiscard]] static float gather_minimum_window_height();
607 [[nodiscard]] static float gather_maximum_window_width();
608 [[nodiscard]] static float gather_maximum_window_height();
609 [[nodiscard]] static uintptr_t gather_primary_monitor_id();
610 [[nodiscard]] static aarectangle gather_primary_monitor_rectangle();
611 [[nodiscard]] static aarectangle gather_desktop_rectangle();
612 [[nodiscard]] static hi::policy gather_gpu_policy();
613};
614
615} // 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.
The HikoGUI namespace.
Definition array_generic.hpp:20
policy
The performance policy to use.
Definition policy.hpp:18
DOXYGEN BUG.
Definition algorithm_misc.hpp:20
callback_flags
Definition callback_flags.hpp:15
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:156
static aarectangle primary_monitor_rectangle() noexcept
Get the rectangle of the primary monitor.
Definition os_settings_intf.hpp:237
static bool start_subsystem() noexcept
Get the global os_settings instance.
Definition os_settings_intf.hpp:316
static hi::alignment alignment(hi::alignment rhs) noexcept
Get the alignment based on the writing direction.
Definition os_settings_intf.hpp:89
static float maximum_window_height() noexcept
The maximum height a window is allowed to be.
Definition os_settings_intf.hpp:227
static std::chrono::milliseconds keyboard_repeat_interval() noexcept
Get the keyboard repeat interval.
Definition os_settings_intf.hpp:166
static hi::subpixel_orientation subpixel_orientation() noexcept
Get the configured light/dark theme mode.
Definition os_settings_intf.hpp:114
static std::chrono::milliseconds cursor_blink_delay() noexcept
Get the cursor blink delay.
Definition os_settings_intf.hpp:176
static std::locale locale() noexcept
Get the current local.
Definition os_settings_intf.hpp:66
static aarectangle desktop_rectangle() noexcept
Get the rectangle describing the desktop.
Definition os_settings_intf.hpp:256
static void gather() noexcept
Gather the settings from the operating system now.
Definition os_settings_intf.hpp:323
static float double_click_distance() noexcept
Get the distance from the previous mouse position to detect double click.
Definition os_settings_intf.hpp:146
static uintptr_t primary_monitor_id() noexcept
Get an opaque id of the primary monitor.
Definition os_settings_intf.hpp:246
static hi::policy policy() noexcept
Get the global performance policy.
Definition os_settings_intf.hpp:267
static float minimum_window_width() noexcept
The minimum width a window is allowed to be.
Definition os_settings_intf.hpp:197
static float maximum_window_width() noexcept
The maximum width a window is allowed to be.
Definition os_settings_intf.hpp:217
static bool left_to_right() noexcept
Check if the configured writing direction is left-to-right.
Definition os_settings_intf.hpp:78
static float minimum_window_height() noexcept
The minimum height a window is allowed to be.
Definition os_settings_intf.hpp:207
static std::chrono::milliseconds double_click_interval() noexcept
Get the mouse double click interval.
Definition os_settings_intf.hpp:138
static bool uniform_HDR() noexcept
Whether SDR and HDR application can coexists on the same display.
Definition os_settings_intf.hpp:130
static hi::horizontal_alignment alignment(hi::horizontal_alignment rhs) noexcept
Get the alignment based on the writing direction.
Definition os_settings_intf.hpp:99
static hi::policy gpu_policy() noexcept
Get the policy for selecting a GPU.
Definition os_settings_intf.hpp:276
static device_type device_type() noexcept
Get the device this application is running on.
Definition os_settings_intf.hpp:43
static std::vector< language_tag > language_tags() noexcept
Get the language tags for the configured languages.
Definition os_settings_intf.hpp:55
static hi::theme_mode theme_mode() noexcept
Get the configured light/dark theme mode.
Definition os_settings_intf.hpp:106
static std::chrono::milliseconds cursor_blink_interval() noexcept
Get the cursor blink interval.
Definition os_settings_intf.hpp:187
T exchange(T... args)
T what(T... args)