i_slint_backend_selector/
lib.rs1#![doc = include_str!("README.md")]
5#![doc(html_logo_url = "https://slint.dev/logo/slint-logo-square-light.svg")]
6#![cfg_attr(
7 not(any(
8 feature = "i-slint-backend-qt",
9 feature = "i-slint-backend-winit",
10 feature = "i-slint-backend-linuxkms"
11 )),
12 no_std
13)]
14#![allow(unused)]
15
16extern crate alloc;
17
18use alloc::boxed::Box;
19pub use i_slint_core::SlintContext;
20use i_slint_core::platform::Platform;
21use i_slint_core::platform::PlatformError;
22
23#[cfg(all(feature = "i-slint-backend-qt", not(no_qt), not(target_os = "android")))]
24fn create_qt_backend() -> Result<Box<dyn Platform + 'static>, PlatformError> {
25 Ok(Box::new(default_backend::Backend::new()))
26}
27
28#[cfg(all(feature = "i-slint-backend-winit", not(target_os = "android")))]
29fn create_winit_backend() -> Result<Box<dyn Platform + 'static>, PlatformError> {
30 Ok(Box::new(i_slint_backend_winit::Backend::new()?))
31}
32
33#[cfg(all(feature = "i-slint-backend-linuxkms", target_os = "linux"))]
34fn create_linuxkms_backend() -> Result<Box<dyn Platform + 'static>, PlatformError> {
35 Ok(Box::new(i_slint_backend_linuxkms::BackendBuilder::default().build()?))
36}
37
38#[cfg(all(feature = "mcp", supports_headless))]
39fn create_headless_backend(renderer: &str) -> Result<Box<dyn Platform + 'static>, PlatformError> {
40 Ok(Box::new(i_slint_backend_testing::TestingBackend::new(
41 i_slint_backend_testing::TestingBackendOptions {
42 mock_time: false,
43 threading: true,
44 renderer_name: Some(renderer.into()),
45 },
46 )))
47}
48
49cfg_if::cfg_if! {
50 if #[cfg(target_os = "android")] {
51 const DEFAULT_BACKEND_NAME: &str = "";
52 } else if #[cfg(all(feature = "i-slint-backend-qt", not(no_qt)))] {
53 use i_slint_backend_qt as default_backend;
54 const DEFAULT_BACKEND_NAME: &str = "qt";
55 } else if #[cfg(feature = "i-slint-backend-winit")] {
56 use i_slint_backend_winit as default_backend;
57 const DEFAULT_BACKEND_NAME: &str = "winit";
58 } else if #[cfg(all(feature = "i-slint-backend-linuxkms", target_os = "linux"))] {
59 use i_slint_backend_linuxkms as default_backend;
60 const DEFAULT_BACKEND_NAME: &str = "linuxkms";
61 } else {
62 const DEFAULT_BACKEND_NAME: &str = "";
63 }
64}
65
66cfg_if::cfg_if! {
67 if #[cfg(all(not(target_os = "android"), any(
68 all(feature = "i-slint-backend-qt", not(no_qt)),
69 feature = "i-slint-backend-winit",
70 all(feature = "i-slint-backend-linuxkms", target_os = "linux")
71 )))] {
72 fn create_default_backend() -> Result<Box<dyn Platform + 'static>, PlatformError> {
73 use alloc::borrow::Cow;
74
75 let backends = [
76 #[cfg(all(feature = "i-slint-backend-qt", not(no_qt)))]
77 ("Qt", create_qt_backend as fn() -> Result<Box<(dyn Platform + 'static)>, PlatformError>),
78 #[cfg(feature = "i-slint-backend-winit")]
79 ("Winit", create_winit_backend as fn() -> Result<Box<(dyn Platform + 'static)>, PlatformError>),
80 #[cfg(all(feature = "i-slint-backend-linuxkms", target_os = "linux"))]
81 ("LinuxKMS", create_linuxkms_backend as fn() -> Result<Box<(dyn Platform + 'static)>, PlatformError>),
82 #[cfg(all(feature = "mcp", supports_headless))]
85 ("Headless", (|| create_headless_backend("")) as fn() -> Result<Box<(dyn Platform + 'static)>, PlatformError>),
86 ("", || Err(PlatformError::NoPlatform)),
87 ];
88
89 let mut backend_errors: Vec<Cow<str>> = Vec::new();
90
91 for (backend_name, backend_factory) in backends {
92 match backend_factory() {
93 Ok(platform) => return Ok(platform),
94 Err(err) => {
95 backend_errors.push(if !backend_name.is_empty() {
96 format!("Error from {backend_name} backend: {err}").into()
97 } else {
98 "No backends configured.".into()
99 });
100 },
101 }
102 }
103
104 Err(PlatformError::Other(format!("Could not initialize backend.\n{}", backend_errors.join("\n"))))
105 }
106
107 pub fn create_backend() -> Result<Box<dyn Platform + 'static>, PlatformError> {
108
109 let backend_config = std::env::var("SLINT_BACKEND").unwrap_or_default();
110 let backend_config = backend_config.to_lowercase();
111 let (event_loop, _renderer) = parse_backend_env_var(backend_config.as_str());
112
113 match event_loop {
114 #[cfg(all(feature = "i-slint-backend-qt", not(no_qt)))]
115 "qt" => return Ok(Box::new(i_slint_backend_qt::Backend::new())),
116 #[cfg(feature = "i-slint-backend-winit")]
117 "winit" => return i_slint_backend_winit::Backend::new_with_renderer_by_name((!_renderer.is_empty()).then_some(_renderer)).map(|b| Box::new(b) as Box<dyn Platform + 'static>),
118 #[cfg(all(feature = "i-slint-backend-linuxkms", target_os = "linux"))]
119 "linuxkms" => {
120 let mut builder = i_slint_backend_linuxkms::BackendBuilder::default();
121 if !_renderer.is_empty() {
122 builder = builder.with_renderer_name(_renderer.into());
123 }
124 return builder.build().map(|b| Box::new(b) as Box<dyn Platform + 'static>)
125 },
126 #[cfg(feature = "backend-testing")]
127 "testing" => return Ok(Box::new(i_slint_backend_testing::TestingBackend::new(
128 i_slint_backend_testing::TestingBackendOptions { mock_time: false, threading: true, ..Default::default() },
129 ))),
130 #[cfg(all(feature = "mcp", supports_headless))]
131 "headless" => return create_headless_backend(_renderer),
132 _ => {},
133 }
134
135 if !backend_config.is_empty() {
136 eprintln!("Could not load rendering backend {backend_config}, fallback to default")
137 }
138 create_default_backend()
139 }
140 pub use default_backend::{
141 native_widgets, NativeGlobals, NativeWidgets, HAS_NATIVE_STYLE,
142 };
143 } else {
144 pub fn create_backend() -> Result<Box<dyn Platform + 'static>, PlatformError> {
145 Err(PlatformError::NoPlatform)
146 }
147 pub mod native_widgets {}
148 pub type NativeWidgets = ();
149 pub type NativeGlobals = ();
150 pub const HAS_NATIVE_STYLE: bool = false;
151 }
152}
153
154pub fn parse_backend_env_var(backend_config: &str) -> (&str, &str) {
155 backend_config.split_once('-').unwrap_or(match backend_config {
156 "qt" => ("qt", ""),
157 "gl" | "winit" => ("winit", ""),
158 "femtovg" => ("winit", "femtovg"),
159 "skia" => ("winit", "skia"),
160 "sw" | "software" => ("winit", "software"),
161 "vello" => ("winit", "vello"),
162 "linuxkms" => ("linuxkms", ""),
163 x => (x, ""),
164 })
165}
166
167#[cfg(any(feature = "system-testing", feature = "mcp"))]
170pub fn init_testing_backends() {
171 #[cfg(feature = "system-testing")]
172 if let Err(e) = i_slint_backend_testing::systest::init() {
173 i_slint_core::debug_log!("System testing init failed: {e:?}");
174 }
175
176 #[cfg(feature = "mcp")]
177 if let Err(e) = i_slint_backend_testing::mcp_server::init() {
178 i_slint_core::debug_log!("MCP server init failed: {e:?}");
179 }
180}
181
182pub fn with_platform<R>(
185 f: impl FnOnce(&dyn Platform) -> Result<R, PlatformError>,
186) -> Result<R, PlatformError> {
187 with_global_context(|ctx| f(ctx.platform()))?
188}
189
190pub fn with_global_context<R>(f: impl FnOnce(&SlintContext) -> R) -> Result<R, PlatformError> {
193 let mut platform_created = false;
194 let result = i_slint_core::with_global_context(
195 || {
196 let backend = create_backend();
197 platform_created = backend.is_ok();
198 backend
199 },
200 f,
201 );
202
203 #[cfg(any(feature = "system-testing", feature = "mcp"))]
204 if result.is_ok() && platform_created {
205 init_testing_backends();
206 }
207
208 result
209}
210
211pub mod api;