#![warn(missing_docs)]
use alloc::boxed::Box;
use alloc::{format, string::String};
use i_slint_core::api::PlatformError;
use i_slint_core::graphics::{RequestedGraphicsAPI, RequestedOpenGLVersion};
#[i_slint_core_macros::slint_doc]
#[derive(Default)]
pub struct BackendSelector {
requested_graphics_api: Option<RequestedGraphicsAPI>,
backend: Option<String>,
renderer: Option<String>,
selected: bool,
#[cfg(feature = "unstable-winit-030")]
winit_window_attributes_hook: Option<
Box<
dyn Fn(
i_slint_backend_winit::winit::window::WindowAttributes,
) -> i_slint_backend_winit::winit::window::WindowAttributes,
>,
>,
#[cfg(feature = "unstable-winit-030")]
winit_event_loop_builder: Option<i_slint_backend_winit::EventLoopBuilder>,
#[cfg(feature = "unstable-winit-030")]
winit_custom_application_handler:
Option<Box<dyn i_slint_backend_winit::CustomApplicationHandler>>,
#[cfg(all(target_os = "linux", feature = "unstable-libinput-09"))]
libinput_event_hook: Option<Box<dyn Fn(&input::Event) -> bool>>,
}
impl BackendSelector {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn require_opengl_es_with_version(mut self, major: u8, minor: u8) -> Self {
self.requested_graphics_api =
Some(RequestedOpenGLVersion::OpenGLES(Some((major, minor))).into());
self
}
#[must_use]
pub fn require_opengl_es(mut self) -> Self {
self.requested_graphics_api = Some(RequestedOpenGLVersion::OpenGLES(None).into());
self
}
#[must_use]
pub fn require_opengl(mut self) -> Self {
self.requested_graphics_api = Some(RequestedOpenGLVersion::OpenGL(None).into());
self
}
#[must_use]
pub fn require_opengl_with_version(mut self, major: u8, minor: u8) -> Self {
self.requested_graphics_api =
Some(RequestedOpenGLVersion::OpenGL(Some((major, minor))).into());
self
}
#[must_use]
pub fn require_metal(mut self) -> Self {
self.requested_graphics_api = Some(RequestedGraphicsAPI::Metal);
self
}
#[must_use]
pub fn require_vulkan(mut self) -> Self {
self.requested_graphics_api = Some(RequestedGraphicsAPI::Vulkan);
self
}
#[must_use]
pub fn require_d3d(mut self) -> Self {
self.requested_graphics_api = Some(RequestedGraphicsAPI::Direct3D);
self
}
#[i_slint_core_macros::slint_doc]
#[cfg(feature = "unstable-wgpu-26")]
#[must_use]
pub fn require_wgpu_26(
mut self,
configuration: i_slint_core::graphics::wgpu_26::api::WGPUConfiguration,
) -> Self {
self.requested_graphics_api = Some(RequestedGraphicsAPI::WGPU26(configuration));
self
}
#[i_slint_core_macros::slint_doc]
#[must_use]
#[cfg(feature = "unstable-winit-030")]
pub fn with_winit_window_attributes_hook(
mut self,
hook: impl Fn(
i_slint_backend_winit::winit::window::WindowAttributes,
) -> i_slint_backend_winit::winit::window::WindowAttributes
+ 'static,
) -> Self {
self.winit_window_attributes_hook = Some(Box::new(hook));
self
}
#[i_slint_core_macros::slint_doc]
#[must_use]
#[cfg(feature = "unstable-winit-030")]
pub fn with_winit_event_loop_builder(
mut self,
event_loop_builder: i_slint_backend_winit::EventLoopBuilder,
) -> Self {
self.winit_event_loop_builder = Some(event_loop_builder);
self
}
#[i_slint_core_macros::slint_doc]
#[must_use]
#[cfg(feature = "unstable-winit-030")]
pub fn with_winit_custom_application_handler(
mut self,
custom_application_handler: impl i_slint_backend_winit::CustomApplicationHandler + 'static,
) -> Self {
self.winit_custom_application_handler = Some(Box::new(custom_application_handler));
self
}
#[i_slint_core_macros::slint_doc]
#[must_use]
#[cfg(all(target_os = "linux", feature = "unstable-libinput-09"))]
pub fn with_libinput_event_hook(
mut self,
event_hook: impl Fn(&input::Event) -> bool + 'static,
) -> Self {
self.libinput_event_hook = Some(Box::new(event_hook));
self
}
#[must_use]
pub fn renderer_name(mut self, name: String) -> Self {
self.renderer = Some(name);
self
}
#[must_use]
pub fn backend_name(mut self, name: String) -> Self {
self.backend = Some(name);
self
}
pub fn select(mut self) -> Result<(), PlatformError> {
self.select_internal()
}
fn select_internal(&mut self) -> Result<(), PlatformError> {
self.selected = true;
#[cfg(any(
feature = "i-slint-backend-qt",
feature = "i-slint-backend-winit",
feature = "i-slint-backend-linuxkms"
))]
if self.backend.is_none() || self.renderer.is_none() {
let backend_config = std::env::var("SLINT_BACKEND").unwrap_or_default();
let backend_config = backend_config.to_lowercase();
let (backend, renderer) = super::parse_backend_env_var(backend_config.as_str());
if !backend.is_empty() {
self.backend.get_or_insert_with(|| backend.to_owned());
}
if !renderer.is_empty() {
self.renderer.get_or_insert_with(|| renderer.to_owned());
}
}
let backend_name = self.backend.as_deref().unwrap_or_else(|| {
#[cfg(feature = "i-slint-backend-winit")]
if self.requested_graphics_api.is_some() {
return "winit";
}
super::DEFAULT_BACKEND_NAME
});
let backend: Box<dyn i_slint_core::platform::Platform> = match backend_name {
#[cfg(all(feature = "i-slint-backend-linuxkms", target_os = "linux"))]
"linuxkms" => {
if self.requested_graphics_api.is_some() {
return Err("The linuxkms backend does not implement renderer selection by graphics API".into());
}
let mut builder = i_slint_backend_linuxkms::BackendBuilder::default();
if let Some(renderer_name) = self.renderer.as_ref() {
builder = builder.with_renderer_name(renderer_name.into());
}
#[cfg(all(target_os = "linux", feature = "unstable-libinput-09"))]
if let Some(event_hook) = self.libinput_event_hook.take() {
builder = builder.with_libinput_event_hook(event_hook);
}
Box::new(builder.build()?)
}
#[cfg(feature = "i-slint-backend-winit")]
"winit" => {
let builder = i_slint_backend_winit::Backend::builder();
let builder = match self.requested_graphics_api.as_ref() {
Some(api) => builder.request_graphics_api(api.clone()),
None => builder,
};
let builder = match self.renderer.as_ref() {
Some(name) => builder.with_renderer_name(name),
None => builder,
};
#[cfg(feature = "unstable-winit-030")]
let builder = match self.winit_window_attributes_hook.take() {
Some(hook) => builder.with_window_attributes_hook(hook),
None => builder,
};
#[cfg(feature = "unstable-winit-030")]
let builder = match self.winit_event_loop_builder.take() {
Some(event_loop_builder) => builder.with_event_loop_builder(event_loop_builder),
None => builder,
};
#[cfg(feature = "unstable-winit-030")]
let builder = match self.winit_custom_application_handler.take() {
Some(custom_application_handler) => {
builder.with_custom_application_handler(custom_application_handler)
}
None => builder,
};
Box::new(builder.build()?)
}
#[cfg(feature = "i-slint-backend-qt")]
"qt" => {
if self.requested_graphics_api.is_some() {
return Err(
"The qt backend does not implement renderer selection by graphics API"
.into(),
);
}
if self.renderer.is_some() {
return Err(
"The qt backend does not implement renderer selection by name".into()
);
}
Box::new(i_slint_backend_qt::Backend::new())
}
requested_backend => {
return Err(format!(
"{requested_backend} backend requested but it is not available"
)
.into());
}
};
i_slint_core::platform::set_platform(backend).map_err(PlatformError::SetPlatformError)
}
}
impl Drop for BackendSelector {
fn drop(&mut self) {
if !self.selected {
self.select_internal().unwrap();
}
}
}