From 7f2de426d277a81201d206e2ec6e165e75f1f963 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Thu, 20 Apr 2023 15:47:04 +0200 Subject: [PATCH] eframe: Set app icon on Mac and Windows (#2940) * eframe: Set app icon on Mac and Windows Also: correctly set window title on Mac when launching from another process, e.g. python. Co-authored-by: Wumpf * lint fixes * Fix web build * fix typo * Try fix windows build --------- Co-authored-by: Wumpf --- Cargo.lock | 19 ++ crates/eframe/Cargo.toml | 17 +- crates/eframe/src/epi/icon_data.rs | 64 ++++++ crates/eframe/src/{epi.rs => epi/mod.rs} | 19 +- crates/eframe/src/native/app_icon.rs | 239 ++++++++++++++++++++ crates/eframe/src/native/epi_integration.rs | 14 +- crates/eframe/src/native/mod.rs | 1 + crates/eframe/src/native/run.rs | 6 +- crates/egui_demo_app/src/main.rs | 5 + media/icon.png | Bin 0 -> 26923 bytes 10 files changed, 363 insertions(+), 21 deletions(-) create mode 100644 crates/eframe/src/epi/icon_data.rs rename crates/eframe/src/{epi.rs => epi/mod.rs} (99%) create mode 100644 crates/eframe/src/native/app_icon.rs create mode 100644 media/icon.png diff --git a/Cargo.lock b/Cargo.lock index f3dc1a9c..303a7a29 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -697,6 +697,22 @@ dependencies = [ "winapi", ] +[[package]] +name = "cocoa" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f425db7937052c684daec3bd6375c8abe2d146dca4b8b143d6db777c39138f3a" +dependencies = [ + "bitflags", + "block", + "cocoa-foundation", + "core-foundation", + "core-graphics", + "foreign-types", + "libc", + "objc", +] + [[package]] name = "cocoa-foundation" version = "0.1.0" @@ -1182,6 +1198,7 @@ name = "eframe" version = "0.21.3" dependencies = [ "bytemuck", + "cocoa", "directories-next", "document-features", "egui", @@ -1194,6 +1211,7 @@ dependencies = [ "image", "js-sys", "log", + "objc", "percent-encoding", "pollster", "puffin", @@ -1206,6 +1224,7 @@ dependencies = [ "wasm-bindgen-futures", "web-sys", "wgpu", + "winapi", "winit", ] diff --git a/crates/eframe/Cargo.toml b/crates/eframe/Cargo.toml index 275c0565..c8b7da9e 100644 --- a/crates/eframe/Cargo.toml +++ b/crates/eframe/Cargo.toml @@ -58,7 +58,7 @@ web_screen_reader = ["tts"] ## If set, eframe will look for the env-var `EFRAME_SCREENSHOT_TO` and write a screenshot to that location, and then quit. ## This is used to generate images for the examples. -__screenshot = ["dep:image"] +__screenshot = [] ## Use [`wgpu`](https://docs.rs/wgpu) for painting (via [`egui-wgpu`](https://github.com/emilk/egui/tree/master/crates/egui-wgpu)). ## This overrides the `glow` feature. @@ -98,6 +98,9 @@ egui-winit = { version = "0.21.1", path = "../egui-winit", default-features = fa "clipboard", "links", ] } +image = { version = "0.24", default-features = false, features = [ + "png", +] } # Needed for app icon raw-window-handle = { version = "0.5.0" } winit = "0.28.1" @@ -112,12 +115,18 @@ pollster = { version = "0.3", optional = true } # needed for wgpu # this can be done at the same time we expose x11/wayland features of winit crate. glutin = { version = "0.30", optional = true } glutin-winit = { version = "0.3.0", optional = true } -image = { version = "0.24", optional = true, default-features = false, features = [ - "png", -] } puffin = { version = "0.14", optional = true } wgpu = { version = "0.15.0", optional = true } +# mac: +[target.'cfg(any(target_os = "macos"))'.dependencies] +cocoa = "0.24.1" +objc = "0.2.7" + +# windows: +[target.'cfg(any(target_os = "windows"))'.dependencies] +winapi = "0.3.9" + # ------------------------------------------- # web: [target.'cfg(target_arch = "wasm32")'.dependencies] diff --git a/crates/eframe/src/epi/icon_data.rs b/crates/eframe/src/epi/icon_data.rs new file mode 100644 index 00000000..2bb6b138 --- /dev/null +++ b/crates/eframe/src/epi/icon_data.rs @@ -0,0 +1,64 @@ +/// Image data for an application icon. +/// +/// Use a square image, e.g. 256x256 pixels. +/// You can use a transparent background. +#[derive(Clone)] +pub struct IconData { + /// RGBA pixels, with separate/unmultiplied alpha. + pub rgba: Vec, + + /// Image width. This should be a multiple of 4. + pub width: u32, + + /// Image height. This should be a multiple of 4. + pub height: u32, +} + +impl IconData { + /// Convert into [`image::RgbaImage`] + /// + /// # Errors + /// If this is not a valid png. + pub fn try_from_png_bytes(png_bytes: &[u8]) -> Result { + let image = image::load_from_memory(png_bytes)?; + Ok(Self::from_image(image)) + } + + fn from_image(image: image::DynamicImage) -> Self { + let image = image.into_rgba8(); + Self { + width: image.width(), + height: image.height(), + rgba: image.into_raw(), + } + } + + /// Convert into [`image::RgbaImage`] + /// + /// # Errors + /// If `width*height != 4 * rgba.len()`, or if the image is too big. + pub fn to_image(&self) -> Result { + let Self { + rgba, + width, + height, + } = self.clone(); + image::RgbaImage::from_raw(width, height, rgba).ok_or_else(|| "Invalid IconData".to_owned()) + } + + /// Encode as PNG. + /// + /// # Errors + /// The image is invalid, or the PNG encoder failed. + pub fn to_png_bytes(&self) -> Result, String> { + let image = self.to_image()?; + let mut png_bytes: Vec = Vec::new(); + image + .write_to( + &mut std::io::Cursor::new(&mut png_bytes), + image::ImageOutputFormat::Png, + ) + .map_err(|err| err.to_string())?; + Ok(png_bytes) + } +} diff --git a/crates/eframe/src/epi.rs b/crates/eframe/src/epi/mod.rs similarity index 99% rename from crates/eframe/src/epi.rs rename to crates/eframe/src/epi/mod.rs index 33166362..33ef0f75 100644 --- a/crates/eframe/src/epi.rs +++ b/crates/eframe/src/epi/mod.rs @@ -6,6 +6,12 @@ #![warn(missing_docs)] // Let's keep `epi` well-documented. +#[cfg(not(target_arch = "wasm32"))] +mod icon_data; + +#[cfg(not(target_arch = "wasm32"))] +pub use icon_data::IconData; + #[cfg(target_arch = "wasm32")] use std::any::Any; @@ -621,19 +627,6 @@ impl std::str::FromStr for Renderer { // ---------------------------------------------------------------------------- -/// Image data for an application icon. -#[derive(Clone)] -pub struct IconData { - /// RGBA pixels, unmultiplied. - pub rgba: Vec, - - /// Image width. This should be a multiple of 4. - pub width: u32, - - /// Image height. This should be a multiple of 4. - pub height: u32, -} - /// Represents the surroundings of your app. /// /// It provides methods to inspect the surroundings (are we on the web?), diff --git a/crates/eframe/src/native/app_icon.rs b/crates/eframe/src/native/app_icon.rs new file mode 100644 index 00000000..20a49a6e --- /dev/null +++ b/crates/eframe/src/native/app_icon.rs @@ -0,0 +1,239 @@ +//! Set the native app icon at runtime. +//! +//! TODO(emilk): port this to [`winit`]. + +use crate::IconData; + +pub struct AppTitleIconSetter { + title: String, + icon_data: Option, + status: AppIconStatus, +} + +impl AppTitleIconSetter { + pub fn new(title: String, icon_data: Option) -> Self { + Self { + title, + icon_data, + status: AppIconStatus::NotSetTryAgain, + } + } + + /// Call once per frame; we will set the icon when we can. + pub fn update(&mut self) { + if self.status == AppIconStatus::NotSetTryAgain { + self.status = set_title_and_icon(&self.title, self.icon_data.as_ref()); + } + } +} + +/// In which state the app icon is (as far as we know). +#[derive(PartialEq, Eq)] +enum AppIconStatus { + /// We did not set it or failed to do it. In any case we won't try again. + NotSetIgnored, + + /// We haven't set the icon yet, we should try again next frame. + /// + /// This can happen repeatedly due to lazy window creation on some platforms. + NotSetTryAgain, + + /// We successfully set the icon and it should be visible now. + #[allow(dead_code)] // Not used on Linux + Set, +} + +/// Sets app icon at runtime. +/// +/// By setting the icon at runtime and not via resource files etc. we ensure that we'll get the chance +/// to set the same icon when the process/window is started from python (which sets its own icon ahead of us!). +/// +/// Since window creation can be lazy, call this every frame until it's either successfully or gave up. +/// (See [`AppIconStatus`]) +fn set_title_and_icon(_title: &str, _icon_data: Option<&IconData>) -> AppIconStatus { + crate::profile_function!(); + + #[cfg(target_os = "windows")] + { + if let Some(icon_data) = _icon_data { + return set_app_icon_windows(icon_data); + } + } + + #[cfg(target_os = "macos")] + return set_title_and_icon_mac(_title, _icon_data); + + #[allow(unreachable_code)] + AppIconStatus::NotSetIgnored +} + +/// Set icon for Windows applications. +#[cfg(target_os = "windows")] +#[allow(unsafe_code)] +fn set_app_icon_windows(icon_data: &IconData) -> AppIconStatus { + use winapi::um::winuser; + + // We would get fairly far already with winit's `set_window_icon` (which is exposed to eframe) actually! + // However, it only sets ICON_SMALL, i.e. doesn't allow us to set a higher resolution icon for the task bar. + // Also, there is scaling issues, detailed below. + + // TODO(andreas): This does not set the task bar icon for when our application is started from python. + // Things tried so far: + // * Querying for an owning window and setting icon there (there doesn't seem to be an owning window) + // * using undocumented SetConsoleIcon method (successfully queried via GetProcAddress) + + // SAFETY: WinApi function without side-effects. + let window_handle = unsafe { winuser::GetActiveWindow() }; + if window_handle.is_null() { + // The Window isn't available yet. Try again later! + return AppIconStatus::NotSetTryAgain; + } + + fn create_hicon_with_scale( + unscaled_image: &image::RgbaImage, + target_size: i32, + ) -> winapi::shared::windef::HICON { + let image_scaled = image::imageops::resize( + unscaled_image, + target_size as _, + target_size as _, + image::imageops::Lanczos3, + ); + + // Creating transparent icons with WinApi is a huge mess. + // We'd need to go through CreateIconIndirect's ICONINFO struct which then + // takes a mask HBITMAP and a color HBITMAP and creating each of these is pain. + // Instead we workaround this by creating a png which CreateIconFromResourceEx magically understands. + // This is a pretty horrible hack as we spend a lot of time encoding, but at least the code is a lot shorter. + let mut image_scaled_bytes: Vec = Vec::new(); + if image_scaled + .write_to( + &mut std::io::Cursor::new(&mut image_scaled_bytes), + image::ImageOutputFormat::Png, + ) + .is_err() + { + return std::ptr::null_mut(); + } + + // SAFETY: Creating an HICON which should be readonly on our data. + unsafe { + winuser::CreateIconFromResourceEx( + image_scaled_bytes.as_mut_ptr(), + image_scaled_bytes.len() as u32, + 1, // Means this is an icon, not a cursor. + 0x00030000, // Version number of the HICON + target_size, // Note that this method can scale, but it does so *very* poorly. So let's avoid that! + target_size, + winuser::LR_DEFAULTCOLOR, + ) + } + } + + let unscaled_image = match icon_data.to_image() { + Ok(unscaled_image) => unscaled_image, + Err(err) => { + log::warn!("Invalid icon: {err}"); + return AppIconStatus::NotSetIgnored; + } + }; + + // Only setting ICON_BIG with the icon size for big icons (SM_CXICON) works fine + // but the scaling it does then for the small icon is pretty bad. + // Instead we set the correct sizes manually and take over the scaling ourselves. + // For this to work we first need to set the big icon and then the small one. + // + // Note that ICON_SMALL may be used even if we don't render a title bar as it may be used in alt+tab! + { + // SAFETY: WinAPI getter function with no known side effects. + let icon_size_big = unsafe { winuser::GetSystemMetrics(winuser::SM_CXICON) }; + let icon_big = create_hicon_with_scale(&unscaled_image, icon_size_big); + if icon_big.is_null() { + log::warn!("Failed to create HICON (for big icon) from embedded png data."); + return AppIconStatus::NotSetIgnored; // We could try independently with the small icon but what's the point, it would look bad! + } else { + // SAFETY: Unsafe WinApi function, takes objects previously created with WinAPI, all checked for null prior. + unsafe { + winuser::SendMessageW( + window_handle, + winuser::WM_SETICON, + winuser::ICON_BIG as usize, + icon_big as isize, + ); + } + } + } + { + // SAFETY: WinAPI getter function with no known side effects. + let icon_size_small = unsafe { winuser::GetSystemMetrics(winuser::SM_CXSMICON) }; + let icon_small = create_hicon_with_scale(&unscaled_image, icon_size_small); + if icon_small.is_null() { + log::warn!("Failed to create HICON (for small icon) from embedded png data."); + return AppIconStatus::NotSetIgnored; + } else { + // SAFETY: Unsafe WinApi function, takes objects previously created with WinAPI, all checked for null prior. + unsafe { + winuser::SendMessageW( + window_handle, + winuser::WM_SETICON, + winuser::ICON_SMALL as usize, + icon_small as isize, + ); + } + } + } + + // It _probably_ worked out. + AppIconStatus::Set +} + +/// Set icon & app title for `MacOS` applications. +#[cfg(target_os = "macos")] +#[allow(unsafe_code)] +fn set_title_and_icon_mac(title: &str, icon_data: Option<&IconData>) -> AppIconStatus { + use cocoa::{ + appkit::{NSApp, NSApplication, NSImage, NSMenu, NSWindow}, + base::{id, nil}, + foundation::{NSData, NSString}, + }; + use objc::{msg_send, sel, sel_impl}; + + let png_bytes = if let Some(icon_data) = icon_data { + match icon_data.to_png_bytes() { + Ok(png_bytes) => Some(png_bytes), + Err(err) => { + log::warn!("Failed to convert IconData to png: {err}"); + return AppIconStatus::NotSetIgnored; + } + } + } else { + None + }; + + // SAFETY: Accessing raw data from icon in a read-only manner. Icon data is static! + unsafe { + let app = NSApp(); + + if let Some(png_bytes) = png_bytes { + let data = NSData::dataWithBytes_length_( + nil, + png_bytes.as_ptr().cast::(), + png_bytes.len() as u64, + ); + let app_icon = NSImage::initWithData_(NSImage::alloc(nil), data); + app.setApplicationIconImage_(app_icon); + } + + // Change the title in the top bar - for python processes this would be again "python" otherwise. + let main_menu = app.mainMenu(); + let app_menu: id = msg_send![main_menu.itemAtIndex_(0), submenu]; + app_menu.setTitle_(NSString::alloc(nil).init_str(title)); + + // The title in the Dock apparently can't be changed. + // At least these people didn't figure it out either: + // https://stackoverflow.com/questions/69831167/qt-change-application-title-dynamically-on-macos + // https://stackoverflow.com/questions/28808226/changing-cocoa-app-icon-title-and-menu-labels-at-runtime + } + + AppIconStatus::Set +} diff --git a/crates/eframe/src/native/epi_integration.rs b/crates/eframe/src/native/epi_integration.rs index f7e79a9b..cd2d83fe 100644 --- a/crates/eframe/src/native/epi_integration.rs +++ b/crates/eframe/src/native/epi_integration.rs @@ -329,6 +329,7 @@ pub struct EpiIntegration { can_drag_window: bool, window_state: WindowState, follow_system_theme: bool, + app_icon_setter: super::app_icon::AppTitleIconSetter, } impl EpiIntegration { @@ -338,7 +339,8 @@ impl EpiIntegration { max_texture_side: usize, window: &winit::window::Window, system_theme: Option, - follow_system_theme: bool, + app_name: &str, + native_options: &crate::NativeOptions, storage: Option>, #[cfg(feature = "glow")] gl: Option>, #[cfg(feature = "wgpu")] wgpu_render_state: Option, @@ -378,6 +380,11 @@ impl EpiIntegration { egui_winit.set_max_texture_side(max_texture_side); egui_winit.set_pixels_per_point(native_pixels_per_point); + let app_icon_setter = super::app_icon::AppTitleIconSetter::new( + app_name.to_owned(), + native_options.icon_data.clone(), + ); + Self { frame, last_auto_save: std::time::Instant::now(), @@ -387,7 +394,8 @@ impl EpiIntegration { close: false, can_drag_window: false, window_state, - follow_system_theme, + follow_system_theme: native_options.follow_system_theme, + app_icon_setter, } } @@ -474,6 +482,8 @@ impl EpiIntegration { ) -> egui::FullOutput { let frame_start = std::time::Instant::now(); + self.app_icon_setter.update(); + self.frame.info.window_info = read_window_info(window, self.egui_ctx.pixels_per_point(), &self.window_state); let raw_input = self.egui_winit.take_egui_input(window); diff --git a/crates/eframe/src/native/mod.rs b/crates/eframe/src/native/mod.rs index 3718d60c..8b606155 100644 --- a/crates/eframe/src/native/mod.rs +++ b/crates/eframe/src/native/mod.rs @@ -1,3 +1,4 @@ +mod app_icon; mod epi_integration; pub mod run; diff --git a/crates/eframe/src/native/run.rs b/crates/eframe/src/native/run.rs index 1d3e461d..15a89e3e 100644 --- a/crates/eframe/src/native/run.rs +++ b/crates/eframe/src/native/run.rs @@ -702,7 +702,8 @@ mod glow_integration { painter.max_texture_side(), gl_window.window(), system_theme, - self.native_options.follow_system_theme, + &self.app_name, + &self.native_options, storage, Some(gl.clone()), #[cfg(feature = "wgpu")] @@ -1166,7 +1167,8 @@ mod wgpu_integration { painter.max_texture_side().unwrap_or(2048), &window, system_theme, - self.native_options.follow_system_theme, + &self.app_name, + &self.native_options, storage, #[cfg(feature = "glow")] None, diff --git a/crates/egui_demo_app/src/main.rs b/crates/egui_demo_app/src/main.rs index 177e24a7..9ab02770 100644 --- a/crates/egui_demo_app/src/main.rs +++ b/crates/egui_demo_app/src/main.rs @@ -22,6 +22,11 @@ fn main() -> Result<(), eframe::Error> { initial_window_size: Some([1280.0, 1024.0].into()), + icon_data: Some( + eframe::IconData::try_from_png_bytes(&include_bytes!("../../../media/icon.png")[..]) + .unwrap(), + ), + #[cfg(feature = "wgpu")] renderer: eframe::Renderer::Wgpu, diff --git a/media/icon.png b/media/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ee93346058d09aa546890bbc2e7dfb1ed6cafb6c GIT binary patch literal 26923 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 literal 0 HcmV?d00001