mod settings;
#[cfg(target_os = "linux")]
mod linux;
#[cfg(target_os = "macos")]
mod macos;
#[cfg(target_os = "windows")]
mod windows;
use iced::{Subscription, Task, window};
use std::fmt;
pub use settings::{
CaptionButtons, ChromeSettings, LinuxChromeSettings, MacosChromeSettings,
MacosTitlebarSeparatorStyle, WindowCornerPreference, WindowsBackdrop, WindowsChromeSettings,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct WindowsVersion {
pub major: u32,
pub minor: u32,
pub build: u32,
}
impl WindowsVersion {
pub fn is_windows_11_or_newer(self) -> bool {
self.major > 10 || (self.major == 10 && self.build >= 22_000)
}
}
impl fmt::Display for WindowsVersion {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}.{}.{}", self.major, self.minor, self.build)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct WindowsCapabilities {
pub version: WindowsVersion,
pub corner_preference: bool,
pub border_color: bool,
pub title_background_color: bool,
pub title_text_color: bool,
pub system_backdrop: bool,
}
impl WindowsCapabilities {
pub fn supports_dwm_visuals(self) -> bool {
self.corner_preference
&& self.border_color
&& self.title_background_color
&& self.title_text_color
}
pub fn supports_system_backdrop(self) -> bool {
self.system_backdrop
}
}
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug)]
pub enum Error {
NoWindowAvailable,
WindowHandle(raw_window_handle::HandleError),
UnsupportedWindowHandle(&'static str),
Windows(&'static str),
Macos(&'static str),
Linux(&'static str),
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::NoWindowAvailable => {
write!(f, "no live iced window was available to patch")
}
Self::WindowHandle(error) => {
write!(f, "failed to access the native window handle: {error}")
}
Self::UnsupportedWindowHandle(kind) => {
write!(f, "unsupported native window handle: {kind}")
}
Self::Windows(message) => write!(f, "Windows API error: {message}"),
Self::Macos(message) => write!(f, "macOS AppKit error: {message}"),
Self::Linux(message) => write!(f, "Linux/X11 error: {message}"),
}
}
}
impl std::error::Error for Error {}
#[derive(Debug, Clone, PartialEq, Hash)]
pub enum Event {
ApplyToWindow {
id: window::Id,
settings: ChromeSettings,
},
}
pub fn apply<Message>(id: window::Id, settings: ChromeSettings) -> Task<Message>
where
Message: Send + 'static,
{
apply_result(id, settings).discard()
}
pub fn apply_result(id: window::Id, settings: ChromeSettings) -> Task<Result<()>> {
window::run(id, move |native| apply_native(native, &settings))
}
pub fn apply_to_latest<Message>(settings: ChromeSettings) -> Task<Message>
where
Message: Send + 'static,
{
apply_to_latest_result(settings).discard()
}
pub fn apply_to_latest_result(settings: ChromeSettings) -> Task<Result<()>> {
window::latest().then(move |id| match id {
Some(id) => apply_result(id, settings.clone()),
None => Task::done(Err(Error::NoWindowAvailable)),
})
}
pub fn subscription(settings: ChromeSettings) -> Subscription<Event> {
window::open_events().with(settings).map(map_open_event)
}
pub fn handle<Message>(event: Event) -> Task<Message>
where
Message: Send + 'static,
{
handle_result(event).discard()
}
pub fn handle_result(event: Event) -> Task<Result<()>> {
match event {
Event::ApplyToWindow { id, settings } => apply_result(id, settings),
}
}
#[cfg(target_os = "windows")]
pub fn current_windows_capabilities() -> Option<WindowsCapabilities> {
windows::current_capabilities()
}
#[cfg(not(target_os = "windows"))]
pub fn current_windows_capabilities() -> Option<WindowsCapabilities> {
None
}
#[cfg(target_os = "windows")]
fn apply_native(window: &dyn iced::window::Window, settings: &ChromeSettings) -> Result<()> {
use raw_window_handle::RawWindowHandle;
let handle = window.window_handle().map_err(Error::WindowHandle)?;
match handle.as_raw() {
RawWindowHandle::Win32(handle) => windows::apply(handle, settings),
_ => Err(Error::UnsupportedWindowHandle("non-Win32")),
}
}
#[cfg(target_os = "macos")]
fn apply_native(window: &dyn iced::window::Window, settings: &ChromeSettings) -> Result<()> {
use raw_window_handle::RawWindowHandle;
let handle = window.window_handle().map_err(Error::WindowHandle)?;
match handle.as_raw() {
RawWindowHandle::AppKit(handle) => macos::apply(handle, settings),
_ => Err(Error::UnsupportedWindowHandle("non-AppKit")),
}
}
#[cfg(target_os = "linux")]
fn apply_native(window: &dyn iced::window::Window, settings: &ChromeSettings) -> Result<()> {
use raw_window_handle::{RawDisplayHandle, RawWindowHandle};
let window_handle = window.window_handle().map_err(Error::WindowHandle)?;
let display_handle = window.display_handle().map_err(Error::WindowHandle)?;
match (display_handle.as_raw(), window_handle.as_raw()) {
(RawDisplayHandle::Xlib(display), RawWindowHandle::Xlib(handle)) => {
linux::apply_xlib(display, handle, settings)
}
(RawDisplayHandle::Wayland(_), RawWindowHandle::Wayland(_)) => {
linux::apply_wayland(settings)
}
_ => Err(Error::UnsupportedWindowHandle("non-Xlib Linux")),
}
}
#[cfg(not(any(target_os = "windows", target_os = "macos", target_os = "linux")))]
fn apply_native(_window: &dyn iced::window::Window, _settings: &ChromeSettings) -> Result<()> {
Ok(())
}
fn map_open_event((settings, id): (ChromeSettings, window::Id)) -> Event {
Event::ApplyToWindow { id, settings }
}