diff --git a/app/src/lib.rs b/app/src/lib.rs index 1aad5397bb..f4d3b8d2ed 100644 --- a/app/src/lib.rs +++ b/app/src/lib.rs @@ -258,6 +258,8 @@ use crate::workspaces::user_profiles::UserProfiles; use anyhow::Context; use anyhow::{anyhow, Result}; use appearance::{Appearance, AppearanceManager}; +#[cfg(any(target_os = "linux", target_os = "freebsd"))] +use channel::Channel; use channel::ChannelState; use interval_timer::IntervalTimer; use itertools::Itertools; @@ -985,6 +987,10 @@ fn run_internal(mut launch_mode: LaunchMode) -> Result<()> { use warpui::platform::linux::{self, AppBuilderExt}; app_builder.set_window_class(ChannelState::app_id().to_string()); + match linux_window_icon() { + Ok(icon) => app_builder.set_window_icon(icon), + Err(err) => log::warn!("Failed to load Linux window icon: {err:#}"), + } let force_x11 = ForceX11::read_from_preferences(prefs_for_public_settings) .unwrap_or(ForceX11::default_value()); @@ -1079,6 +1085,32 @@ fn run_internal(mut launch_mode: LaunchMode) -> Result<()> { }) } +#[cfg(any(target_os = "linux", target_os = "freebsd"))] +fn linux_window_icon() -> Result { + use anyhow::Context as _; + + let image = image::load_from_memory(linux_window_icon_png()) + .context("Failed to decode Linux window icon asset")? + .into_rgba8(); + let (width, height) = image.dimensions(); + + winit::window::Icon::from_rgba(image.into_raw(), width, height) + .context("Failed to create Linux window icon") +} + +#[cfg(any(target_os = "linux", target_os = "freebsd"))] +fn linux_window_icon_png() -> &'static [u8] { + match ChannelState::channel() { + Channel::Stable => include_bytes!("../channels/stable/icon/no-padding/512x512.png"), + Channel::Preview => include_bytes!("../channels/preview/icon/no-padding/512x512.png"), + Channel::Dev => include_bytes!("../channels/dev/icon/no-padding/512x512.png"), + Channel::Local | Channel::Integration => { + include_bytes!("../channels/local/icon/no-padding/512x512.png") + } + Channel::Oss => include_bytes!("../channels/oss/icon/no-padding/512x512.png"), + } +} + pub struct UpdateQuakeModeEventArg { active_window_id: Option, } diff --git a/crates/warpui/src/platform/linux/mod.rs b/crates/warpui/src/platform/linux/mod.rs index e55c9c3109..6682909e42 100644 --- a/crates/warpui/src/platform/linux/mod.rs +++ b/crates/warpui/src/platform/linux/mod.rs @@ -18,6 +18,8 @@ pub trait AppBuilderExt { /// This is used to identify the application and link it properly to its /// .desktop file and associated resources (like app icons). fn set_window_class(&mut self, window_class: String); + /// Sets the icon to apply to newly-created native windows. + fn set_window_icon(&mut self, icon: winit::window::Icon); /// Whether or not to force the use of XWayland for users running Wayland. fn force_x11(&mut self, force_x11: bool); @@ -30,6 +32,12 @@ impl AppBuilderExt for super::AppBuilder { AppBackend::Headless(_) => (), } } + fn set_window_icon(&mut self, icon: winit::window::Icon) { + match self.as_inner_mut() { + AppBackend::CurrentPlatform(app) => app.set_window_icon(icon), + AppBackend::Headless(_) => (), + } + } fn force_x11(&mut self, force_x11: bool) { match self.as_inner_mut() { diff --git a/crates/warpui/src/windowing/winit/app.rs b/crates/warpui/src/windowing/winit/app.rs index 2bfef44749..94a1ea39be 100644 --- a/crates/warpui/src/windowing/winit/app.rs +++ b/crates/warpui/src/windowing/winit/app.rs @@ -128,6 +128,8 @@ pub struct App { is_integration_test: bool, window_class: Option, #[cfg(any(target_os = "linux", target_os = "freebsd"))] + window_icon: Option, + #[cfg(any(target_os = "linux", target_os = "freebsd"))] force_x11: bool, } @@ -143,6 +145,8 @@ impl App { is_integration_test: test_driver.is_some(), window_class: None, #[cfg(any(target_os = "linux", target_os = "freebsd"))] + window_icon: None, + #[cfg(any(target_os = "linux", target_os = "freebsd"))] force_x11: false, } } @@ -153,6 +157,10 @@ impl App { pub(crate) fn set_window_class(&mut self, window_class: String) { self.window_class = Some(window_class); } + #[cfg(any(target_os = "linux", target_os = "freebsd"))] + pub(crate) fn set_window_icon(&mut self, icon: winit::window::Icon) { + self.window_icon = Some(icon); + } #[cfg(any(target_os = "linux", target_os = "freebsd"))] pub(crate) fn force_x11(&mut self, force_x11: bool) { @@ -169,6 +177,8 @@ impl App { is_integration_test, window_class, #[cfg(any(target_os = "linux", target_os = "freebsd"))] + window_icon, + #[cfg(any(target_os = "linux", target_os = "freebsd"))] force_x11, } = self; @@ -211,6 +221,8 @@ impl App { callbacks, init_fn, window_class, + #[cfg(any(target_os = "linux", target_os = "freebsd"))] + window_icon, event_loop.create_proxy(), ); diff --git a/crates/warpui/src/windowing/winit/event_loop/mod.rs b/crates/warpui/src/windowing/winit/event_loop/mod.rs index 692a3e9c6f..38f8912195 100644 --- a/crates/warpui/src/windowing/winit/event_loop/mod.rs +++ b/crates/warpui/src/windowing/winit/event_loop/mod.rs @@ -500,6 +500,8 @@ pub(super) struct EventLoop { callbacks: AppCallbackDispatcher, init_fn: Option, window_class: Option, + #[cfg(any(target_os = "linux", target_os = "freebsd"))] + window_icon: Option, state: State, proxy: EventLoopProxy, ime_enabled: bool, @@ -522,6 +524,9 @@ impl EventLoop { callbacks: platform::AppCallbacks, init_fn: impl FnOnce(&mut AppContext, LocalBoxFuture<'static, crate::App>) + 'static, window_class: Option, + #[cfg(any(target_os = "linux", target_os = "freebsd"))] window_icon: Option< + winit::window::Icon, + >, proxy: EventLoopProxy, ) -> Self { Self { @@ -529,6 +534,8 @@ impl EventLoop { callbacks: AppCallbackDispatcher::new(callbacks, ui_app), init_fn: Some(Box::new(init_fn)), window_class, + #[cfg(any(target_os = "linux", target_os = "freebsd"))] + window_icon, state: Default::default(), proxy, ime_enabled: false, @@ -615,6 +622,8 @@ impl EventLoop { window_target, window_options, &self.window_class, + #[cfg(any(target_os = "linux", target_os = "freebsd"))] + self.window_icon.as_ref(), is_tiling_window_manager, self.downrank_non_nvidia_vulkan_adapters, ) { diff --git a/crates/warpui/src/windowing/winit/window.rs b/crates/warpui/src/windowing/winit/window.rs index d3320d1c90..7b2a7ebdba 100644 --- a/crates/warpui/src/windowing/winit/window.rs +++ b/crates/warpui/src/windowing/winit/window.rs @@ -756,6 +756,9 @@ impl Window { window_target: &ActiveEventLoop, window_options: WindowOptions, window_class: &Option, + #[cfg(any(target_os = "linux", target_os = "freebsd"))] window_icon: Option< + &winit::window::Icon, + >, tiling_window_manager: bool, downrank_non_nvidia_vulkan_adapters: bool, ) -> Result { @@ -763,6 +766,8 @@ impl Window { window_target, &window_options, window_class, + #[cfg(any(target_os = "linux", target_os = "freebsd"))] + window_icon, tiling_window_manager, )?; @@ -1296,6 +1301,9 @@ fn create_window( window_target: &ActiveEventLoop, window_options: &WindowOptions, _window_class: &Option, + #[cfg(any(target_os = "linux", target_os = "freebsd"))] window_icon: Option< + &winit::window::Icon, + >, tiling_window_manager: bool, ) -> Result { let decorations = !window_options.hide_title_bar; @@ -1415,6 +1423,11 @@ fn create_window( FullscreenState::Normal => {} } + #[cfg(any(target_os = "linux", target_os = "freebsd"))] + { + window_attributes.window_icon = window_icon.cloned(); + } + #[cfg(any(target_os = "linux", target_os = "freebsd"))] if let Some(window_class) = _window_class.as_deref() { use winit::platform::x11::{WindowAttributesExtX11, WindowType};