Skip to main content

ventana_backend_auto/
lib.rs

1pub mod backend;
2
3use {
4  backend::{
5    appkit::AppKit,
6    linux::Linux,
7    win32::Win32,
8  },
9  hal::{
10    backend::Backend as HalBackend,
11    error::RequestError,
12    monitor::BackendMonitor,
13    settings::WindowSettings,
14    window::BackendWindow,
15  },
16  std::{
17    collections::VecDeque,
18    fmt::Debug,
19    sync::{
20      Arc,
21      LazyLock,
22      Mutex,
23    },
24  },
25};
26
27#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
28pub enum Backend {
29  #[default]
30  Win32 = 0,
31  AppKit = 1,
32  X11 = 2,
33  Wayland = 3,
34  Web = 4,
35  Android = 5,
36  UIKit = 6,
37}
38
39impl Backend {
40  pub fn backend(self) -> Option<&'static dyn HalBackend> {
41    match self {
42      Backend::Win32 => Win32::backend(),
43      Backend::AppKit => AppKit::backend(),
44      Backend::X11 => Linux::x11(),
45      Backend::Wayland => Linux::wayland(),
46      Backend::Web => unimplemented!(),
47      Backend::Android => unimplemented!(),
48      Backend::UIKit => unimplemented!(),
49    }
50  }
51
52  pub fn is_available(&self) -> bool {
53    match self {
54      Backend::Win32 => Win32::is_available(),
55      Backend::AppKit => AppKit::is_available(),
56      Backend::X11 => Linux::is_x11_available(),
57      Backend::Wayland => Linux::is_wayland_available(),
58      Backend::Web => false,
59      Backend::Android => false,
60      Backend::UIKit => false,
61    }
62  }
63}
64
65static PREFERENCES: LazyLock<Mutex<&'static [Backend]>> = LazyLock::new(|| {
66  Mutex::new(&[
67    Backend::Win32,
68    Backend::AppKit,
69    Backend::X11,
70    Backend::Wayland,
71    Backend::Web,
72    Backend::Android,
73    Backend::UIKit,
74  ])
75});
76
77#[derive(Clone)]
78pub struct AutoBackend(&'static dyn HalBackend);
79
80impl Debug for AutoBackend {
81  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
82    write!(f, "{}", self.name())
83  }
84}
85
86impl HalBackend for AutoBackend {
87  fn instance() -> Option<&'static Self>
88  where
89    Self: Sized,
90  {
91    static INSTANCE: LazyLock<Option<AutoBackend>> =
92      LazyLock::new(|| AutoBackend::auto().ok().map(AutoBackend));
93    INSTANCE.as_ref()
94  }
95
96  fn is_available() -> bool
97  where
98    Self: Sized,
99  {
100    Win32::is_available() || Linux::is_available() || AppKit::is_available()
101  }
102
103  fn name(&self) -> &'static str {
104    self.0.name()
105  }
106
107  fn create_window(&self, settings: WindowSettings) -> Result<Arc<dyn BackendWindow>, RequestError> {
108    self.0.create_window(settings)
109  }
110
111  fn list_available_monitors(&self) -> Result<VecDeque<Arc<dyn BackendMonitor>>, RequestError> {
112    self.0.list_available_monitors()
113  }
114
115  fn primary_monitor(&self) -> Result<Arc<dyn BackendMonitor>, RequestError> {
116    self.0.primary_monitor()
117  }
118}
119
120impl AutoBackend {
121  /// Attempts to select a backend from the first-party backend implementations. Returns `RequestError::NotSupported` if none are available.
122  fn auto() -> Result<&'static dyn HalBackend, RequestError> {
123    let Some(backend) = PREFERENCES.lock().unwrap().iter().find(|b| b.is_available()) else {
124      return Err(RequestError::not_supported("no supported backend available to auto-select from"));
125    };
126    backend.backend().ok_or(RequestError::not_supported("failed to initialize backend"))
127  }
128
129  /// Sets which backends the AutoBackend will select from and in what order they are selected.
130  ///
131  /// Default: `[Win32, AppKit, Wayland, X11, Web, Android, UIKit,]`
132  pub fn set_preferences(preferences: &'static [Backend]) {
133    *PREFERENCES.lock().unwrap() = preferences;
134  }
135}