use std::{fmt, io, mem, path::Path, ptr, sync::Arc}; use windows_sys::{ core::PCWSTR, Win32::{ Foundation::HWND, System::LibraryLoader::GetModuleHandleW, UI::WindowsAndMessaging::{ CreateIcon, DestroyIcon, LoadImageW, SendMessageW, HICON, ICON_BIG, ICON_SMALL, IMAGE_ICON, LR_DEFAULTSIZE, LR_LOADFROMFILE, WM_SETICON, }, }, }; use crate::dpi::PhysicalSize; use crate::icon::*; use super::util; impl Pixel { fn convert_to_bgra(&mut self) { mem::swap(&mut self.r, &mut self.b); } } impl RgbaIcon { fn into_windows_icon(self) -> Result { let rgba = self.rgba; let pixel_count = rgba.len() / PIXEL_SIZE; let mut and_mask = Vec::with_capacity(pixel_count); let pixels = unsafe { std::slice::from_raw_parts_mut(rgba.as_ptr() as *mut Pixel, pixel_count) }; for pixel in pixels { and_mask.push(pixel.a.wrapping_sub(std::u8::MAX)); // invert alpha channel pixel.convert_to_bgra(); } assert_eq!(and_mask.len(), pixel_count); let handle = unsafe { CreateIcon( 0, self.width as i32, self.height as i32, 1, (PIXEL_SIZE * 8) as u8, and_mask.as_ptr(), rgba.as_ptr(), ) }; if handle != 0 { Ok(WinIcon::from_handle(handle)) } else { Err(BadIcon::OsError(io::Error::last_os_error())) } } } #[derive(Debug)] pub enum IconType { Small = ICON_SMALL as isize, Big = ICON_BIG as isize, } #[derive(Debug)] struct RaiiIcon { handle: HICON, } #[derive(Clone)] pub struct WinIcon { inner: Arc, } unsafe impl Send for WinIcon {} impl WinIcon { pub fn as_raw_handle(&self) -> HICON { self.inner.handle } pub fn from_path>( path: P, size: Option>, ) -> Result { // width / height of 0 along with LR_DEFAULTSIZE tells windows to load the default icon size let (width, height) = size.map(Into::into).unwrap_or((0, 0)); let wide_path = util::encode_wide(path.as_ref()); let handle = unsafe { LoadImageW( 0, wide_path.as_ptr(), IMAGE_ICON, width as i32, height as i32, LR_DEFAULTSIZE | LR_LOADFROMFILE, ) }; if handle != 0 { Ok(WinIcon::from_handle(handle as HICON)) } else { Err(BadIcon::OsError(io::Error::last_os_error())) } } pub fn from_resource( resource_id: u16, size: Option>, ) -> Result { // width / height of 0 along with LR_DEFAULTSIZE tells windows to load the default icon size let (width, height) = size.map(Into::into).unwrap_or((0, 0)); let handle = unsafe { LoadImageW( GetModuleHandleW(ptr::null()), resource_id as PCWSTR, IMAGE_ICON, width as i32, height as i32, LR_DEFAULTSIZE, ) }; if handle != 0 { Ok(WinIcon::from_handle(handle as HICON)) } else { Err(BadIcon::OsError(io::Error::last_os_error())) } } pub fn from_rgba(rgba: Vec, width: u32, height: u32) -> Result { let rgba_icon = RgbaIcon::from_rgba(rgba, width, height)?; rgba_icon.into_windows_icon() } pub fn set_for_window(&self, hwnd: HWND, icon_type: IconType) { unsafe { SendMessageW(hwnd, WM_SETICON, icon_type as usize, self.as_raw_handle()); } } fn from_handle(handle: HICON) -> Self { Self { inner: Arc::new(RaiiIcon { handle }), } } } impl Drop for RaiiIcon { fn drop(&mut self) { unsafe { DestroyIcon(self.handle) }; } } impl fmt::Debug for WinIcon { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { (*self.inner).fmt(formatter) } } pub fn unset_for_window(hwnd: HWND, icon_type: IconType) { unsafe { SendMessageW(hwnd, WM_SETICON, icon_type as usize, 0); } }