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/module.hpp"
11#include "../utility/utility.hpp"
12#include "../numeric/module.hpp"
13#include "../observer/module.hpp"
14#include "../dispatch/dispatch.hpp"
15#include "../macros.hpp"
16#include <vector>
17#include <mutex>
18#include <expected>
19#include <system_error>
20
21hi_export_module(hikogui.settings.os_settings : intf);
22
23namespace hi::inline v1 {
24
26public:
28 using callback_token = notifier_type::callback_token;
29 using callback_proto = notifier_type::callback_proto;
30
35 [[nodiscard]] static std::vector<language_tag> language_tags() noexcept
36 {
37 hi_axiom(_populated.load(std::memory_order::acquire));
38 hilet lock = std::scoped_lock(_mutex);
39 return _language_tags;
40 }
41
46 [[nodiscard]] static std::locale locale() noexcept
47 {
48 hi_axiom(_populated.load(std::memory_order::acquire));
49 hilet lock = std::scoped_lock(_mutex);
50 return _locale;
51 }
52
53
58 [[nodiscard]] static bool left_to_right() noexcept
59 {
60 hi_axiom(_populated.load(std::memory_order::acquire));
61 return _left_to_right.load(std::memory_order::relaxed);
62 }
63
66 [[nodiscard]] static hi::theme_mode theme_mode() noexcept
67 {
68 hi_axiom(_populated.load(std::memory_order::acquire));
69 return _theme_mode.load(std::memory_order::relaxed);
70 }
71
74 [[nodiscard]] static hi::subpixel_orientation subpixel_orientation() noexcept
75 {
76 hi_axiom(_populated.load(std::memory_order::acquire));
77 return _subpixel_orientation.load(std::memory_order::relaxed);
78 }
79
90 [[nodiscard]] static bool uniform_HDR() noexcept
91 {
92 hi_axiom(_populated.load(std::memory_order::acquire));
93 return _uniform_HDR.load(std::memory_order::relaxed);
94 }
95
98 [[nodiscard]] static std::chrono::milliseconds double_click_interval() noexcept
99 {
100 hi_axiom(_populated.load(std::memory_order::acquire));
101 return _double_click_interval.load(std::memory_order::relaxed);
102 }
103
106 [[nodiscard]] static float double_click_distance() noexcept
107 {
108 hi_axiom(_populated.load(std::memory_order::acquire));
109 return _double_click_distance.load(std::memory_order::relaxed);
110 }
111
116 [[nodiscard]] static std::chrono::milliseconds keyboard_repeat_delay() noexcept
117 {
118 hi_axiom(_populated.load(std::memory_order::acquire));
119 return _keyboard_repeat_delay.load(std::memory_order::relaxed);
120 }
121
127 {
128 hi_axiom(_populated.load(std::memory_order::acquire));
129 return _keyboard_repeat_interval.load(std::memory_order::relaxed);
130 }
131
136 [[nodiscard]] static std::chrono::milliseconds cursor_blink_delay() noexcept
137 {
138 hi_axiom(_populated.load(std::memory_order::acquire));
139 return _cursor_blink_delay.load(std::memory_order::relaxed);
140 }
141
147 [[nodiscard]] static std::chrono::milliseconds cursor_blink_interval() noexcept
148 {
149 hi_axiom(_populated.load(std::memory_order::acquire));
150 return _cursor_blink_interval.load(std::memory_order::relaxed);
151 }
152
157 [[nodiscard]] static float minimum_window_width() noexcept
158 {
159 hi_axiom(_populated.load(std::memory_order::acquire));
160 return _minimum_window_width.load(std::memory_order::relaxed);
161 }
162
167 [[nodiscard]] static float minimum_window_height() noexcept
168 {
169 hi_axiom(_populated.load(std::memory_order::acquire));
170 return _minimum_window_height.load(std::memory_order::relaxed);
171 }
172
177 [[nodiscard]] static float maximum_window_width() noexcept
178 {
179 hi_axiom(_populated.load(std::memory_order::acquire));
180 return _maximum_window_width.load(std::memory_order::relaxed);
181 }
182
187 [[nodiscard]] static float maximum_window_height() noexcept
188 {
189 hi_axiom(_populated.load(std::memory_order::acquire));
190 return _maximum_window_height.load(std::memory_order::relaxed);
191 }
192
197 [[nodiscard]] static aarectangle primary_monitor_rectangle() noexcept
198 {
199 hi_axiom(_populated.load(std::memory_order::acquire));
200 hilet lock = std::scoped_lock(_mutex);
201 return _primary_monitor_rectangle;
202 }
203
206 [[nodiscard]] static uintptr_t primary_monitor_id() noexcept
207 {
208 hi_axiom(_populated.load(std::memory_order::acquire));
209 return _primary_monitor_id.load(std::memory_order::relaxed);
210 }
211
216 [[nodiscard]] static aarectangle desktop_rectangle() noexcept
217 {
218 hi_axiom(_populated.load(std::memory_order::acquire));
219 hilet lock = std::scoped_lock(_mutex);
220 return _desktop_rectangle;
221 }
222
227 [[nodiscard]] static hi::policy policy() noexcept
228 {
229 return policy::unspecified;
230 }
231
236 [[nodiscard]] static hi::policy gpu_policy() noexcept
237 {
238 hi_axiom(_populated.load(std::memory_order::acquire));
239 return _gpu_policy.load(std::memory_order::relaxed);
240 }
241
263 [[nodiscard]] static std::vector<uuid> preferred_gpus(hi::policy performance_policy) noexcept;
264
265 [[nodiscard]] static callback_token
266 subscribe(forward_of<callback_proto> auto&& callback, callback_flags flags = callback_flags::synchronous) noexcept
267 {
268 hilet lock = std::scoped_lock(_mutex);
269 return _notifier.subscribe(hi_forward(callback), flags);
270 }
271
276 static bool start_subsystem() noexcept
277 {
278 return hi::start_subsystem(_started, false, subsystem_init, subsystem_deinit);
279 }
280
283 static void gather() noexcept
284 {
285 hilet lock = std::scoped_lock(_mutex);
286 auto setting_has_changed = false;
287
288 hilet current_time = std::chrono::utc_clock::now();
289 if (current_time < _gather_last_time + gather_minimum_interval) {
290 return;
291 }
292 _gather_last_time = current_time;
293
294 try {
295 auto language_tags = gather_languages();
296 if (language_tags.empty()) {
297 // If not language is configured on the system, use English as default.
298 language_tags.emplace_back("en-Latn-US");
299 }
300
301 auto left_to_right = language_tags.front().left_to_right();
302
303 // Add all the variants of languages, for searching into translations.
304 language_tags = variants(language_tags);
305
306 auto language_changed = compare_store(_language_tags, language_tags);
307 language_changed |= compare_store(_left_to_right, left_to_right);
308
309 if (language_changed) {
310 setting_has_changed = true;
311 hi_log_info("OS language order has changed: {}", _language_tags);
312 }
313 } catch (std::exception const& e) {
314 hi_log_error("Failed to get OS language: {}", e.what());
315 }
316
317 if (auto optional_locale = gather_locale()) {
318 if (compare_store(_locale, *optional_locale)) {
319 setting_has_changed = true;
320 hi_log_info("OS locale has changed: {}", _locale.name());
321 }
322 } else {
323 hi_log_error("Failed to get OS locale: {}", optional_locale.error().message());
324 }
325
326 try {
327 if (compare_store(_theme_mode, gather_theme_mode())) {
328 setting_has_changed = true;
329 hi_log_info("OS theme-mode has changed: {}", _theme_mode.load());
330 }
331 } catch (std::exception const& e) {
332 hi_log_error("Failed to get OS theme-mode: {}", e.what());
333 }
334
335 try {
336 if (compare_store(_subpixel_orientation, gather_subpixel_orientation())) {
337 setting_has_changed = true;
338 hi_log_info("OS sub-pixel orientation has changed: {}", _subpixel_orientation.load());
339 }
340 } catch (std::exception const& e) {
341 hi_log_error("Failed to get OS sub-pixel orientation: {}", e.what());
342 }
343
344 try {
345 if (compare_store(_uniform_HDR, gather_uniform_HDR())) {
346 setting_has_changed = true;
347 hi_log_info("OS uniform-HDR has changed: {}", _uniform_HDR.load());
348 }
349 } catch (std::exception const& e) {
350 hi_log_error("Failed to get OS uniform-HDR: {}", e.what());
351 }
352
353 try {
354 if (compare_store(_double_click_interval, gather_double_click_interval())) {
355 setting_has_changed = true;
356 hi_log_info("OS double click interval has changed: {}", _double_click_interval.load());
357 }
358 } catch (std::exception const& e) {
359 hi_log_error("Failed to get OS double click interval: {}", e.what());
360 }
361
362 try {
363 if (compare_store(_double_click_distance, gather_double_click_distance())) {
364 setting_has_changed = true;
365 hi_log_info("OS double click distance has changed: {}", _double_click_distance.load());
366 }
367 } catch (std::exception const& e) {
368 hi_log_error("Failed to get OS double click distance: {}", e.what());
369 }
370
371 try {
372 if (compare_store(_keyboard_repeat_delay, gather_keyboard_repeat_delay())) {
373 setting_has_changed = true;
374 hi_log_info("OS keyboard repeat delay has changed: {}", _keyboard_repeat_delay.load());
375 }
376 } catch (std::exception const& e) {
377 hi_log_error("Failed to get OS keyboard repeat delay: {}", e.what());
378 }
379
380 try {
381 if (compare_store(_keyboard_repeat_interval, gather_keyboard_repeat_interval())) {
382 setting_has_changed = true;
383 hi_log_info("OS keyboard repeat interval has changed: {}", _keyboard_repeat_interval.load());
384 }
385 } catch (std::exception const& e) {
386 hi_log_error("Failed to get OS keyboard repeat interval: {}", e.what());
387 }
388
389 try {
390 if (compare_store(_cursor_blink_interval, gather_cursor_blink_interval())) {
391 setting_has_changed = true;
392 if (_cursor_blink_interval.load() < std::chrono::minutes(1)) {
393 hi_log_info("OS cursor blink interval has changed: {}", _cursor_blink_interval.load());
394 } else {
395 hi_log_info("OS cursor blink interval has changed: no-blinking");
396 }
397 }
398 } catch (std::exception const& e) {
399 hi_log_error("Failed to get OS cursor blink interval: {}", e.what());
400 }
401
402 try {
403 if (compare_store(_cursor_blink_delay, gather_cursor_blink_delay())) {
404 setting_has_changed = true;
405 hi_log_info("OS cursor blink delay has changed: {}", _cursor_blink_delay.load());
406 }
407 } catch (std::exception const& e) {
408 hi_log_error("Failed to get OS cursor blink delay: {}", e.what());
409 }
410
411 try {
412 if (compare_store(_minimum_window_width, gather_minimum_window_width())) {
413 setting_has_changed = true;
414 hi_log_info("OS minimum window width has changed: {}", _minimum_window_width.load());
415 }
416 } catch (std::exception const& e) {
417 hi_log_error("Failed to get OS minimum window width: {}", e.what());
418 }
419
420 try {
421 if (compare_store(_minimum_window_height, gather_minimum_window_height())) {
422 setting_has_changed = true;
423 hi_log_info("OS minimum window height has changed: {}", _minimum_window_height.load());
424 }
425 } catch (std::exception const& e) {
426 hi_log_error("Failed to get OS minimum window height: {}", e.what());
427 }
428
429 try {
430 if (compare_store(_maximum_window_width, gather_maximum_window_width())) {
431 setting_has_changed = true;
432 hi_log_info("OS maximum window width has changed: {}", _maximum_window_width.load());
433 }
434 } catch (std::exception const& e) {
435 hi_log_error("Failed to get OS maximum window width: {}", e.what());
436 }
437
438 try {
439 if (compare_store(_maximum_window_height, gather_maximum_window_height())) {
440 setting_has_changed = true;
441 hi_log_info("OS maximum window height has changed: {}", _maximum_window_height.load());
442 }
443 } catch (std::exception const& e) {
444 hi_log_error("Failed to get OS maximum window height: {}", e.what());
445 }
446
447 try {
448 hilet primary_monitor_id = gather_primary_monitor_id();
449 if (compare_store(_primary_monitor_id, primary_monitor_id)) {
450 setting_has_changed = true;
451 hi_log_info("OS primary monitor id has changed: {}, updating vsync source", primary_monitor_id);
452 loop::main().set_vsync_monitor_id(primary_monitor_id);
453 }
454 } catch (std::exception const& e) {
455 hi_log_error("Failed to get OS primary monitor id: {}", e.what());
456 }
457
458 try {
459 if (compare_store(_primary_monitor_rectangle, gather_primary_monitor_rectangle())) {
460 setting_has_changed = true;
461 hi_log_info("OS primary monitor rectangle has changed: {}", _primary_monitor_rectangle);
462 }
463 } catch (std::exception const& e) {
464 hi_log_error("Failed to get OS primary monitor rectangle: {}", e.what());
465 }
466
467 try {
468 if (compare_store(_desktop_rectangle, gather_desktop_rectangle())) {
469 setting_has_changed = true;
470 hi_log_info("OS desktop rectangle has changed: {}", _desktop_rectangle);
471 }
472 } catch (std::exception const& e) {
473 hi_log_error("Failed to get OS desktop rectangle: {}", e.what());
474 }
475
476 try {
477 if (compare_store(_gpu_policy, gather_gpu_policy())) {
478 setting_has_changed = true;
479 hi_log_info("GPU policy has changed: {}", _gpu_policy.load());
480 }
481 } catch (std::exception const& e) {
482 hi_log_error("Failed to get the GPU policy: {}", e.what());
483 }
484
485 _populated.store(true, std::memory_order::release);
486 if (setting_has_changed) {
487 _notifier();
488 }
489 }
490
491private:
492 constexpr static std::chrono::duration gather_interval = std::chrono::seconds(5);
493 constexpr static std::chrono::duration gather_minimum_interval = std::chrono::seconds(1);
494
495 static inline std::atomic<bool> _started = false;
496 static inline std::atomic<bool> _populated = false;
497 static inline unfair_mutex _mutex;
498 static inline loop::timer_callback_token _gather_cbt;
499 static inline utc_nanoseconds _gather_last_time;
500
501 static inline notifier_type _notifier;
502
503 static inline std::vector<language_tag> _language_tags = {};
504 static inline std::locale _locale = std::locale{""};
505 static inline std::atomic<bool> _left_to_right = true;
506 static inline std::atomic<hi::theme_mode> _theme_mode = theme_mode::dark;
507 static inline std::atomic<bool> _uniform_HDR = false;
508 static inline std::atomic<hi::subpixel_orientation> _subpixel_orientation = hi::subpixel_orientation::unknown;
509 static inline std::atomic<std::chrono::milliseconds> _double_click_interval = std::chrono::milliseconds(500);
510 static inline std::atomic<float> _double_click_distance = 4.0f;
511 static inline std::atomic<std::chrono::milliseconds> _keyboard_repeat_delay = std::chrono::milliseconds(250);
512 static inline std::atomic<std::chrono::milliseconds> _keyboard_repeat_interval = std::chrono::milliseconds(33);
513 static inline std::atomic<std::chrono::milliseconds> _cursor_blink_interval = std::chrono::milliseconds(1000);
514 static inline std::atomic<std::chrono::milliseconds> _cursor_blink_delay = std::chrono::milliseconds(1000);
515 static inline std::atomic<float> _minimum_window_width = 40.0f;
516 static inline std::atomic<float> _minimum_window_height = 25.0f;
517 static inline std::atomic<float> _maximum_window_width = 1920.0f;
518 static inline std::atomic<float> _maximum_window_height = 1080.0f;
519 static inline std::atomic<uintptr_t> _primary_monitor_id = 0;
520 static inline aarectangle _primary_monitor_rectangle = aarectangle{0.0f, 0.0f, 1920.0f, 1080.0f};
521 static inline aarectangle _desktop_rectangle = aarectangle{0.0f, 0.0f, 1920.0f, 1080.0f};
522 static inline std::atomic<hi::policy> _gpu_policy = policy::unspecified;
523
524 [[nodiscard]] static bool subsystem_init() noexcept
525 {
526 _gather_cbt = loop::timer().repeat_function(gather_interval, [] {
527 os_settings::gather();
528 });
529
530 return true;
531 }
532
533 static void subsystem_deinit() noexcept
534 {
535 if (_started.exchange(false)) {
536 _gather_cbt = nullptr;
537 }
538 }
539
540 [[nodiscard]] static std::vector<language_tag> gather_languages();
541 [[nodiscard]] static std::expected<std::locale, std::error_code> gather_locale() noexcept;
542 [[nodiscard]] static hi::theme_mode gather_theme_mode();
543 [[nodiscard]] static hi::subpixel_orientation gather_subpixel_orientation();
544 [[nodiscard]] static bool gather_uniform_HDR();
545 [[nodiscard]] static std::chrono::milliseconds gather_double_click_interval();
546 [[nodiscard]] static float gather_double_click_distance();
547 [[nodiscard]] static std::chrono::milliseconds gather_keyboard_repeat_delay();
548 [[nodiscard]] static std::chrono::milliseconds gather_keyboard_repeat_interval();
549 [[nodiscard]] static std::chrono::milliseconds gather_cursor_blink_interval();
550 [[nodiscard]] static std::chrono::milliseconds gather_cursor_blink_delay();
551 [[nodiscard]] static float gather_minimum_window_width();
552 [[nodiscard]] static float gather_minimum_window_height();
553 [[nodiscard]] static float gather_maximum_window_width();
554 [[nodiscard]] static float gather_maximum_window_height();
555 [[nodiscard]] static uintptr_t gather_primary_monitor_id();
556 [[nodiscard]] static aarectangle gather_primary_monitor_rectangle();
557 [[nodiscard]] static aarectangle gather_desktop_rectangle();
558 [[nodiscard]] static hi::policy gather_gpu_policy();
559};
560
561} // 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:116
STL namespace.
DOXYGEN BUG.
Definition algorithm.hpp:16
callback_flags
Definition callback_flags.hpp:14
geometry/margins.hpp
Definition lookahead_iterator.hpp:5
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:377
A notifier which can be used to call a set of registered callbacks.
Definition notifier.hpp:27
Definition os_settings_intf.hpp:25
static std::chrono::milliseconds keyboard_repeat_delay() noexcept
Get the delay before the keyboard starts repeating.
Definition os_settings_intf.hpp:116
static aarectangle primary_monitor_rectangle() noexcept
Get the rectangle of the primary monitor.
Definition os_settings_intf.hpp:197
static bool start_subsystem() noexcept
Get the global os_settings instance.
Definition os_settings_intf.hpp:276
static float maximum_window_height() noexcept
The maximum height a window is allowed to be.
Definition os_settings_intf.hpp:187
static std::chrono::milliseconds keyboard_repeat_interval() noexcept
Get the keyboard repeat interval.
Definition os_settings_intf.hpp:126
static hi::subpixel_orientation subpixel_orientation() noexcept
Get the configured light/dark theme mode.
Definition os_settings_intf.hpp:74
static std::chrono::milliseconds cursor_blink_delay() noexcept
Get the cursor blink delay.
Definition os_settings_intf.hpp:136
static std::locale locale() noexcept
Get the current local.
Definition os_settings_intf.hpp:46
static aarectangle desktop_rectangle() noexcept
Get the rectangle describing the desktop.
Definition os_settings_intf.hpp:216
static void gather() noexcept
Gather the settings from the operating system now.
Definition os_settings_intf.hpp:283
static float double_click_distance() noexcept
Get the distance from the previous mouse position to detect double click.
Definition os_settings_intf.hpp:106
static uintptr_t primary_monitor_id() noexcept
Get an opaque id of the primary monitor.
Definition os_settings_intf.hpp:206
static hi::policy policy() noexcept
Get the global performance policy.
Definition os_settings_intf.hpp:227
static float minimum_window_width() noexcept
The minimum width a window is allowed to be.
Definition os_settings_intf.hpp:157
static float maximum_window_width() noexcept
The maximum width a window is allowed to be.
Definition os_settings_intf.hpp:177
static bool left_to_right() noexcept
Check if the configured writing direction is left-to-right.
Definition os_settings_intf.hpp:58
static float minimum_window_height() noexcept
The minimum height a window is allowed to be.
Definition os_settings_intf.hpp:167
static std::chrono::milliseconds double_click_interval() noexcept
Get the mouse double click interval.
Definition os_settings_intf.hpp:98
static bool uniform_HDR() noexcept
Whether SDR and HDR application can coexists on the same display.
Definition os_settings_intf.hpp:90
static hi::policy gpu_policy() noexcept
Get the policy for selecting a GPU.
Definition os_settings_intf.hpp:236
static std::vector< language_tag > language_tags() noexcept
Get the language tags for the configured languages.
Definition os_settings_intf.hpp:35
static hi::theme_mode theme_mode() noexcept
Get the configured light/dark theme mode.
Definition os_settings_intf.hpp:66
static std::chrono::milliseconds cursor_blink_interval() noexcept
Get the cursor blink interval.
Definition os_settings_intf.hpp:147
T exchange(T... args)
T what(T... args)