icon: refactor Icon to be dyn

Same as for `CustomCursor`. However, the API uses `dyn` stuff only
because of `Windows` backend at the time of writing, generally, platforms
should just have a separate method that deals with all of that, and e.g.
top-level winit only guarantees `Rgba`.
This commit is contained in:
Kirill Chibisov 2025-03-23 20:02:07 +03:00
parent cdbdd974fb
commit 07c25b9703
16 changed files with 230 additions and 211 deletions

View file

@ -20,7 +20,6 @@ use crate::event_loop::{
EventLoopProxy as CoreEventLoopProxy, EventLoopProxyProvider,
OwnedDisplayHandle as CoreOwnedDisplayHandle,
};
pub(crate) use crate::icon::NoIcon as PlatformIcon;
use crate::monitor::{Fullscreen, MonitorHandle as CoreMonitorHandle};
use crate::platform::pump_events::PumpStatus;
use crate::window::{

View file

@ -21,4 +21,3 @@ pub(crate) use self::event_loop::{
pub(crate) use self::monitor::MonitorHandle;
pub(crate) use self::window::Window;
pub(crate) use self::window_delegate::PlatformSpecificWindowAttributes;
pub(crate) use crate::icon::NoIcon as PlatformIcon;

View file

@ -14,7 +14,6 @@ pub(crate) use self::event_loop::{
};
pub(crate) use self::monitor::MonitorHandle;
pub(crate) use self::window::{PlatformSpecificWindowAttributes, Window};
pub(crate) use crate::icon::NoIcon as PlatformIcon;
#[derive(Debug)]
pub enum OsError {}

View file

@ -13,7 +13,6 @@ use crate::application::ApplicationHandler;
use crate::dpi::Size;
use crate::error::{EventLoopError, NotSupportedError};
use crate::event_loop::ActiveEventLoop;
pub(crate) use crate::icon::RgbaIcon as PlatformIcon;
use crate::platform::pump_events::PumpStatus;
#[cfg(x11_platform)]
use crate::platform::x11::WindowType as XWindowType;

View file

@ -28,15 +28,16 @@ use crate::dpi::{PhysicalInsets, PhysicalPosition, PhysicalSize, Position, Size}
use crate::error::{NotSupportedError, RequestError};
use crate::event::{SurfaceSizeWriter, WindowEvent};
use crate::event_loop::AsyncRequestSerial;
use crate::icon::RgbaIcon;
use crate::monitor::{
Fullscreen, MonitorHandle as CoreMonitorHandle, MonitorHandleProvider, VideoMode,
};
use crate::platform::x11::WindowType;
use crate::platform_impl::common;
use crate::platform_impl::x11::atoms::*;
use crate::platform_impl::x11::{
xinput_fp1616_to_float, MonitorHandle as X11MonitorHandle, WakeSender, X11Error,
};
use crate::platform_impl::{common, PlatformIcon};
use crate::window::{
CursorGrabMode, ImePurpose, ResizeDirection, Theme, UserAttentionType, Window as CoreWindow,
WindowAttributes, WindowButtons, WindowId, WindowLevel,
@ -203,7 +204,11 @@ impl CoreWindow for Window {
}
fn set_window_icon(&self, window_icon: Option<crate::window::Icon>) {
self.0.set_window_icon(window_icon.map(|inner| inner.inner))
let icon = match window_icon.as_ref() {
Some(icon) => icon.0.cast_ref::<RgbaIcon>(),
None => None,
};
self.0.set_window_icon(icon)
}
fn set_ime_cursor_area(&self, position: Position, size: Size) {
@ -765,8 +770,10 @@ impl UnownedWindow {
.check());
// Set window icons
if let Some(icon) = window_attrs.window_icon {
leap!(window.set_icon_inner(icon.inner)).ignore_error();
if let Some(icon) =
window_attrs.window_icon.as_ref().and_then(|icon| icon.0.cast_ref::<RgbaIcon>())
{
leap!(window.set_icon_inner(icon)).ignore_error();
}
// Opt into handling window close and resize synchronization
@ -1404,7 +1411,7 @@ impl UnownedWindow {
self.xconn.flush_requests().expect("Failed to set window-level state");
}
fn set_icon_inner(&self, icon: PlatformIcon) -> Result<VoidCookie<'_>, X11Error> {
fn set_icon_inner(&self, icon: &RgbaIcon) -> Result<VoidCookie<'_>, X11Error> {
let atoms = self.xconn.atoms();
let icon_atom = atoms[_NET_WM_ICON];
let data = icon.to_cardinals();
@ -1431,7 +1438,7 @@ impl UnownedWindow {
}
#[inline]
pub(crate) fn set_window_icon(&self, icon: Option<PlatformIcon>) {
pub(crate) fn set_window_icon(&self, icon: Option<&RgbaIcon>) {
match icon {
Some(icon) => self.set_icon_inner(icon),
None => self.unset_icon_inner(),

View file

@ -4,7 +4,6 @@ use std::{fmt, str};
pub(crate) use self::event_loop::{ActiveEventLoop, EventLoop};
pub use self::window::Window;
pub(crate) use crate::icon::NoIcon as PlatformIcon;
mod event_loop;
mod window;

View file

@ -42,4 +42,3 @@ pub(crate) use self::monitor::{
};
use self::web_sys as backend;
pub use self::window::{PlatformSpecificWindowAttributes, Window};
pub(crate) use crate::icon::NoIcon as PlatformIcon;

View file

@ -5,13 +5,12 @@ use std::{fmt, io, mem, ptr};
use cursor_icon::CursorIcon;
use windows_sys::core::PCWSTR;
use windows_sys::Win32::Foundation::HWND;
use windows_sys::Win32::Graphics::Gdi::{
CreateBitmap, CreateCompatibleBitmap, DeleteObject, GetDC, ReleaseDC, SetBitmapBits,
};
use windows_sys::Win32::UI::WindowsAndMessaging::{
CreateIcon, CreateIconIndirect, DestroyCursor, DestroyIcon, LoadImageW, SendMessageW, HCURSOR,
HICON, ICONINFO, ICON_BIG, ICON_SMALL, IMAGE_ICON, LR_DEFAULTSIZE, LR_LOADFROMFILE, WM_SETICON,
CreateIcon, CreateIconIndirect, DestroyCursor, DestroyIcon, LoadImageW, HCURSOR, HICON,
ICONINFO, ICON_BIG, ICON_SMALL, IMAGE_ICON, LR_DEFAULTSIZE, LR_LOADFROMFILE,
};
use super::util;
@ -19,71 +18,12 @@ use crate::cursor::{CursorImage, CustomCursorProvider};
use crate::dpi::PhysicalSize;
use crate::error::RequestError;
use crate::icon::*;
impl Pixel {
fn convert_to_bgra(&mut self) {
mem::swap(&mut self.r, &mut self.b);
}
}
impl RgbaIcon {
fn into_windows_icon(self) -> Result<WinIcon, BadIcon> {
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(u8::MAX)); // invert alpha channel
pixel.convert_to_bgra();
}
assert_eq!(and_mask.len(), pixel_count);
let handle = unsafe {
CreateIcon(
ptr::null_mut(),
self.width as i32,
self.height as i32,
1,
(PIXEL_SIZE * 8) as u8,
and_mask.as_ptr(),
rgba.as_ptr(),
)
};
if !handle.is_null() {
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, PartialEq, Eq, Hash)]
struct RaiiIcon {
handle: HICON,
}
unsafe impl Send for RaiiIcon {}
unsafe impl Sync for RaiiIcon {}
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct WinIcon {
inner: Arc<RaiiIcon>,
}
use crate::platform::windows::WinIcon;
unsafe impl Send for WinIcon {}
impl WinIcon {
pub fn as_raw_handle(&self) -> HICON {
self.inner.handle
}
pub fn from_path<P: AsRef<Path>>(
pub(crate) fn from_path_impl<P: AsRef<Path>>(
path: P,
size: Option<PhysicalSize<u32>>,
) -> Result<Self, BadIcon> {
@ -109,14 +49,14 @@ impl WinIcon {
}
}
pub fn from_resource(
pub(crate) fn from_resource_impl(
resource_id: u16,
size: Option<PhysicalSize<u32>>,
) -> Result<Self, BadIcon> {
Self::from_resource_ptr(resource_id as PCWSTR, size)
}
pub fn from_resource_name(
pub(crate) fn from_resource_name_impl(
resource_name: &str,
size: Option<PhysicalSize<u32>>,
) -> Result<Self, BadIcon> {
@ -147,14 +87,36 @@ impl WinIcon {
}
}
pub fn from_rgba(rgba: Vec<u8>, width: u32, height: u32) -> Result<Self, BadIcon> {
let rgba_icon = RgbaIcon::from_rgba(rgba, width, height)?;
rgba_icon.into_windows_icon()
pub(crate) fn as_raw_handle(&self) -> HICON {
self.inner.handle
}
pub fn set_for_window(&self, hwnd: HWND, icon_type: IconType) {
unsafe {
SendMessageW(hwnd, WM_SETICON, icon_type as usize, self.as_raw_handle() as isize);
pub(crate) fn from_rgba(rgba: &RgbaIcon) -> Result<Self, BadIcon> {
let pixel_count = rgba.rgba.len() / PIXEL_SIZE;
let mut and_mask = Vec::with_capacity(pixel_count);
let pixels = unsafe {
std::slice::from_raw_parts_mut(rgba.rgba.as_ptr() as *mut Pixel, pixel_count)
};
for pixel in pixels {
and_mask.push(pixel.a.wrapping_sub(u8::MAX)); // invert alpha channel
pixel.convert_to_bgra();
}
assert_eq!(and_mask.len(), pixel_count);
let handle = unsafe {
CreateIcon(
ptr::null_mut(),
rgba.width as i32,
rgba.height as i32,
1,
(PIXEL_SIZE * 8) as u8,
and_mask.as_ptr(),
rgba.rgba.as_ptr(),
)
};
if !handle.is_null() {
Ok(WinIcon::from_handle(handle))
} else {
Err(BadIcon::OsError(io::Error::last_os_error()))
}
}
@ -163,11 +125,7 @@ impl WinIcon {
}
}
impl Drop for RaiiIcon {
fn drop(&mut self) {
unsafe { DestroyIcon(self.handle) };
}
}
impl IconProvider for WinIcon {}
impl fmt::Debug for WinIcon {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
@ -175,9 +133,29 @@ impl fmt::Debug for WinIcon {
}
}
pub fn unset_for_window(hwnd: HWND, icon_type: IconType) {
unsafe {
SendMessageW(hwnd, WM_SETICON, icon_type as usize, 0);
impl Pixel {
fn convert_to_bgra(&mut self) {
mem::swap(&mut self.r, &mut self.b);
}
}
#[derive(Debug, Clone, Copy)]
pub enum IconType {
Small = ICON_SMALL as isize,
Big = ICON_BIG as isize,
}
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct RaiiIcon {
handle: HICON,
}
unsafe impl Send for RaiiIcon {}
unsafe impl Sync for RaiiIcon {}
impl Drop for RaiiIcon {
fn drop(&mut self) {
unsafe { DestroyIcon(self.handle) };
}
}

View file

@ -2,7 +2,7 @@ use windows_sys::Win32::Foundation::HWND;
use windows_sys::Win32::UI::WindowsAndMessaging::{HMENU, WINDOW_LONG_PTR_INDEX};
pub(crate) use self::event_loop::{EventLoop, PlatformSpecificEventLoopAttributes};
pub(crate) use self::icon::{SelectedCursor, WinIcon as PlatformIcon, WinIcon};
pub(crate) use self::icon::{RaiiIcon, SelectedCursor};
pub(crate) use self::keyboard::{physicalkey_to_scancode, scancode_to_physicalkey};
pub(crate) use self::monitor::MonitorHandle;
pub(crate) use self::window::Window;
@ -10,7 +10,7 @@ use crate::event::DeviceId;
use crate::icon::Icon;
use crate::platform::windows::{BackdropType, Color, CornerPreference};
#[derive(Clone, Debug, PartialEq)]
#[derive(Clone, Debug)]
pub struct PlatformSpecificWindowAttributes {
pub owner: Option<HWND>,
pub menu: Option<HMENU>,

View file

@ -37,14 +37,15 @@ use windows_sys::Win32::UI::WindowsAndMessaging::{
CreateWindowExW, EnableMenuItem, FlashWindowEx, GetClientRect, GetCursorPos,
GetForegroundWindow, GetSystemMenu, GetSystemMetrics, GetWindowPlacement, GetWindowTextLengthW,
GetWindowTextW, IsWindowVisible, LoadCursorW, PeekMessageW, PostMessageW, RegisterClassExW,
SetCursor, SetCursorPos, SetForegroundWindow, SetMenuDefaultItem, SetWindowDisplayAffinity,
SetWindowPlacement, SetWindowPos, SetWindowTextW, TrackPopupMenu, CS_HREDRAW, CS_VREDRAW,
CW_USEDEFAULT, FLASHWINFO, FLASHW_ALL, FLASHW_STOP, FLASHW_TIMERNOFG, FLASHW_TRAY,
GWLP_HINSTANCE, HTBOTTOM, HTBOTTOMLEFT, HTBOTTOMRIGHT, HTCAPTION, HTLEFT, HTRIGHT, HTTOP,
HTTOPLEFT, HTTOPRIGHT, MENU_ITEM_STATE, MFS_DISABLED, MFS_ENABLED, MF_BYCOMMAND, NID_READY,
PM_NOREMOVE, SC_CLOSE, SC_MAXIMIZE, SC_MINIMIZE, SC_MOVE, SC_RESTORE, SC_SIZE, SM_DIGITIZER,
SWP_ASYNCWINDOWPOS, SWP_NOACTIVATE, SWP_NOSIZE, SWP_NOZORDER, TPM_LEFTALIGN, TPM_RETURNCMD,
WDA_EXCLUDEFROMCAPTURE, WDA_NONE, WM_NCLBUTTONDOWN, WM_SYSCOMMAND, WNDCLASSEXW,
SendMessageW, SetCursor, SetCursorPos, SetForegroundWindow, SetMenuDefaultItem,
SetWindowDisplayAffinity, SetWindowPlacement, SetWindowPos, SetWindowTextW, TrackPopupMenu,
CS_HREDRAW, CS_VREDRAW, CW_USEDEFAULT, FLASHWINFO, FLASHW_ALL, FLASHW_STOP, FLASHW_TIMERNOFG,
FLASHW_TRAY, GWLP_HINSTANCE, HTBOTTOM, HTBOTTOMLEFT, HTBOTTOMRIGHT, HTCAPTION, HTLEFT, HTRIGHT,
HTTOP, HTTOPLEFT, HTTOPRIGHT, MENU_ITEM_STATE, MFS_DISABLED, MFS_ENABLED, MF_BYCOMMAND,
NID_READY, PM_NOREMOVE, SC_CLOSE, SC_MAXIMIZE, SC_MINIMIZE, SC_MOVE, SC_RESTORE, SC_SIZE,
SM_DIGITIZER, SWP_ASYNCWINDOWPOS, SWP_NOACTIVATE, SWP_NOSIZE, SWP_NOZORDER, TPM_LEFTALIGN,
TPM_RETURNCMD, WDA_EXCLUDEFROMCAPTURE, WDA_NONE, WM_NCLBUTTONDOWN, WM_SETICON, WM_SYSCOMMAND,
WNDCLASSEXW,
};
use super::icon::WinCursor;
@ -52,9 +53,9 @@ use super::MonitorHandle;
use crate::cursor::Cursor;
use crate::dpi::{PhysicalInsets, PhysicalPosition, PhysicalSize, Position, Size};
use crate::error::{NotSupportedError, RequestError};
use crate::icon::Icon;
use crate::icon::{Icon, RgbaIcon};
use crate::monitor::{Fullscreen, MonitorHandle as CoreMonitorHandle, MonitorHandleProvider};
use crate::platform::windows::{BackdropType, Color, CornerPreference};
use crate::platform::windows::{BackdropType, Color, CornerPreference, WinIcon};
use crate::platform_impl::platform::dark_mode::try_theme;
use crate::platform_impl::platform::definitions::{
CLSID_TaskbarList, IID_ITaskbarList, IID_ITaskbarList2, ITaskbarList, ITaskbarList2,
@ -66,7 +67,7 @@ use crate::platform_impl::platform::drop_handler::FileDropHandler;
use crate::platform_impl::platform::event_loop::{
self, ActiveEventLoop, Event, EventLoopRunner, DESTROY_MSG_ID,
};
use crate::platform_impl::platform::icon::{self, IconType};
use crate::platform_impl::platform::icon::IconType;
use crate::platform_impl::platform::ime::ImeContext;
use crate::platform_impl::platform::keyboard::KeyEventBuilder;
use crate::platform_impl::platform::window_state::{
@ -189,12 +190,11 @@ impl Window {
}
pub fn set_taskbar_icon(&self, taskbar_icon: Option<Icon>) {
if let Some(ref taskbar_icon) = taskbar_icon {
taskbar_icon.inner.set_for_window(self.hwnd(), IconType::Big);
if let Some(taskbar_icon) = taskbar_icon {
self.set_icon(taskbar_icon, IconType::Big);
} else {
icon::unset_for_window(self.hwnd(), IconType::Big);
self.unset_icon(IconType::Big);
}
self.window_state_lock().taskbar_icon = taskbar_icon;
}
unsafe fn handle_os_dragging(&self, wparam: WPARAM) {
@ -349,6 +349,45 @@ impl Window {
);
}
}
fn set_icon(&self, mut new_icon: Icon, icon_type: IconType) {
if let Some(icon) = new_icon.0.cast_ref::<RgbaIcon>() {
let icon = match WinIcon::from_rgba(icon) {
Ok(icon) => icon,
Err(err) => {
warn!("{}", err);
return;
},
};
new_icon = Icon(Arc::new(icon));
}
if let Some(icon) = new_icon.0.cast_ref::<WinIcon>() {
unsafe {
SendMessageW(
self.hwnd(),
WM_SETICON,
icon_type as usize,
icon.as_raw_handle() as isize,
);
}
match icon_type {
IconType::Small => self.window_state_lock().window_icon = Some(new_icon),
IconType::Big => self.window_state_lock().taskbar_icon = Some(new_icon),
}
}
}
fn unset_icon(&self, icon_type: IconType) {
unsafe {
SendMessageW(self.hwnd(), WM_SETICON, icon_type as usize, 0);
}
match icon_type {
IconType::Small => self.window_state_lock().window_icon = None,
IconType::Big => self.window_state_lock().taskbar_icon = None,
}
}
}
impl Drop for Window {
@ -974,12 +1013,11 @@ impl CoreWindow for Window {
}
fn set_window_icon(&self, window_icon: Option<Icon>) {
if let Some(ref window_icon) = window_icon {
window_icon.inner.set_for_window(self.hwnd(), IconType::Small);
if let Some(window_icon) = window_icon {
self.set_icon(window_icon, IconType::Small);
} else {
icon::unset_for_window(self.hwnd(), IconType::Small);
self.unset_icon(IconType::Small);
}
self.window_state_lock().window_icon = window_icon;
}
fn set_ime_cursor_area(&self, spot: Position, size: Size) {