Decode wallpaper off the shell UI thread during startup

This commit is contained in:
Samuel Onoja committed 2026-09-26 05:15:03 +01:00
1 parent 90e8986d8e
commit 2f8ba908a5
1 file changed
+43 -6
+43 -6
View File
@@ -80,6 +80,20 @@ impl From<ShellTheme> for BarMetrics {
fn main() -> cosmic::iced::Result { fn main() -> cosmic::iced::Result {
let config = config::load(); let config = config::load();
// Decode alongside toolkit/GPU initialization, never during a UI draw.
let wallpaper = config.wallpaper.path.clone().map(|path| {
let (sender, receiver) = cosmic::iced::futures::channel::oneshot::channel();
std::thread::spawn(move || {
let result =
cosmic::iced::advanced::graphics::image::load(&image::Handle::from_path(path))
.map(|pixels| {
image::Handle::from_rgba(pixels.width(), pixels.height(), pixels.into_raw())
})
.map_err(|error| format!("{error:?}"));
let _ = sender.send(result);
});
receiver
});
let mut settings = Settings::default() let mut settings = Settings::default()
.no_main_window(true) .no_main_window(true)
.client_decorations(false) .client_decorations(false)
@@ -91,13 +105,17 @@ fn main() -> cosmic::iced::Result {
settings = settings.default_font(cosmic::font::Font::with_name(font_family)); settings = settings.default_font(cosmic::font::Font::with_name(font_family));
} }
cosmic::app::run::<FereseShell>(settings, config) cosmic::app::run::<FereseShell>(settings, (config, wallpaper))
} }
type WallpaperLoad =
cosmic::iced::futures::channel::oneshot::Receiver<Result<image::Handle, String>>;
struct FereseShell { struct FereseShell {
core: Core, core: Core,
bar_surface_id: window::Id, bar_surface_id: window::Id,
config: ShellConfig, config: ShellConfig,
wallpaper: Option<image::Handle>,
control: Option<ShellControl>, control: Option<ShellControl>,
snapshot: ShellSnapshot, snapshot: ShellSnapshot,
overview_active: bool, overview_active: bool,
@@ -111,6 +129,7 @@ struct FereseShell {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
enum Message { enum Message {
WallpaperLoaded(Result<image::Handle, String>),
Event(Event, window::Id), Event(Event, window::Id),
NativeSurface( NativeSurface(
window::Id, window::Id,
@@ -129,7 +148,7 @@ enum Message {
impl cosmic::Application for FereseShell { impl cosmic::Application for FereseShell {
type Executor = cosmic::executor::Default; type Executor = cosmic::executor::Default;
type Flags = ShellConfig; type Flags = (ShellConfig, Option<WallpaperLoad>);
type Message = Message; type Message = Message;
const APP_ID: &'static str = APP_ID; const APP_ID: &'static str = APP_ID;
@@ -142,7 +161,7 @@ impl cosmic::Application for FereseShell {
&mut self.core &mut self.core
} }
fn init(core: Core, config: Self::Flags) -> (Self, Task<Self::Message>) { fn init(core: Core, (config, wallpaper): Self::Flags) -> (Self, Task<Self::Message>) {
let bar_surface_id = window::Id::unique(); let bar_surface_id = window::Id::unique();
let wallpaper_surface_id = window::Id::unique(); let wallpaper_surface_id = window::Id::unique();
let shell_theme = config.theme; let shell_theme = config.theme;
@@ -155,6 +174,7 @@ impl cosmic::Application for FereseShell {
status_error: None, status_error: None,
menu: None, menu: None,
config, config,
wallpaper: None,
control: ShellControl::connect() control: ShellControl::connect()
.map_err(|error| { .map_err(|error| {
eprintln!("ferese-shell: shell control unavailable: {error}"); eprintln!("ferese-shell: shell control unavailable: {error}");
@@ -211,7 +231,17 @@ impl cosmic::Application for FereseShell {
.map(cosmic::Action::Surface) .map(cosmic::Action::Surface)
.map(cosmic::task::message); .map(cosmic::task::message);
(app, Task::batch(tasks)) let wallpaper_task = match wallpaper {
Some(receiver) => cosmic::task::future(async move {
cosmic::Action::App(Message::WallpaperLoaded(
receiver
.await
.unwrap_or_else(|error| Err(error.to_string())),
))
}),
None => Task::none(),
};
(app, Task::batch([Task::batch(tasks), wallpaper_task]))
} }
fn subscription(&self) -> Subscription<Self::Message> { fn subscription(&self) -> Subscription<Self::Message> {
@@ -241,6 +271,13 @@ impl cosmic::Application for FereseShell {
fn update(&mut self, message: Self::Message) -> Task<Self::Message> { fn update(&mut self, message: Self::Message) -> Task<Self::Message> {
match message { match message {
Message::WallpaperLoaded(result) => {
match result {
Ok(handle) => self.wallpaper = Some(handle),
Err(error) => eprintln!("ferese-shell: wallpaper unavailable: {error}"),
}
Task::none()
}
Message::NativeSurface(id, result) => { Message::NativeSurface(id, result) => {
match result { match result {
Ok((_connection, surface)) if id == self.bar_surface_id => { Ok((_connection, surface)) if id == self.bar_surface_id => {
@@ -610,7 +647,7 @@ impl FereseShell {
} }
fn view_wallpaper(&self) -> Element<'_, cosmic::Action<Message>> { fn view_wallpaper(&self) -> Element<'_, cosmic::Action<Message>> {
let Some(path) = self.config.wallpaper.path.as_ref() else { let Some(handle) = self.wallpaper.as_ref() else {
return container(text("")) return container(text(""))
.width(Length::Fill) .width(Length::Fill)
.height(Length::Fill) .height(Length::Fill)
@@ -623,7 +660,7 @@ impl FereseShell {
}; };
container( container(
image(path.clone()) image(handle.clone())
.width(Length::Fill) .width(Length::Fill)
.height(Length::Fill) .height(Length::Fill)
.content_fit(content_fit), .content_fit(content_fit),