1mod settings;
13
14#[cfg(target_os = "linux")]
15mod linux;
16#[cfg(target_os = "macos")]
17mod macos;
18#[cfg(target_os = "windows")]
19mod windows;
20
21use iced::{Subscription, Task, window};
22use std::fmt;
23
24pub use settings::{
25 CaptionButtons, ChromeSettings, LinuxChromeSettings, MacosChromeSettings,
26 MacosTitlebarSeparatorStyle, WindowCornerPreference, WindowsBackdrop, WindowsChromeSettings,
27};
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
31pub struct WindowsVersion {
32 pub major: u32,
33 pub minor: u32,
34 pub build: u32,
35}
36
37impl WindowsVersion {
38 pub fn is_windows_11_or_newer(self) -> bool {
40 self.major > 10 || (self.major == 10 && self.build >= 22_000)
41 }
42}
43
44impl fmt::Display for WindowsVersion {
45 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46 write!(f, "{}.{}.{}", self.major, self.minor, self.build)
47 }
48}
49
50#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
52pub struct WindowsCapabilities {
53 pub version: WindowsVersion,
54 pub corner_preference: bool,
55 pub border_color: bool,
56 pub title_background_color: bool,
57 pub title_text_color: bool,
58 pub system_backdrop: bool,
59}
60
61impl WindowsCapabilities {
62 pub fn supports_dwm_visuals(self) -> bool {
64 self.corner_preference
65 && self.border_color
66 && self.title_background_color
67 && self.title_text_color
68 }
69
70 pub fn supports_system_backdrop(self) -> bool {
72 self.system_backdrop
73 }
74}
75
76pub type Result<T> = std::result::Result<T, Error>;
78
79#[derive(Debug)]
81pub enum Error {
82 NoWindowAvailable,
83 WindowHandle(raw_window_handle::HandleError),
84 UnsupportedWindowHandle(&'static str),
85 Windows(&'static str),
86 Macos(&'static str),
87 Linux(&'static str),
88}
89
90impl fmt::Display for Error {
91 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92 match self {
93 Self::NoWindowAvailable => {
94 write!(f, "no live iced window was available to patch")
95 }
96 Self::WindowHandle(error) => {
97 write!(f, "failed to access the native window handle: {error}")
98 }
99 Self::UnsupportedWindowHandle(kind) => {
100 write!(f, "unsupported native window handle: {kind}")
101 }
102 Self::Windows(message) => write!(f, "Windows API error: {message}"),
103 Self::Macos(message) => write!(f, "macOS AppKit error: {message}"),
104 Self::Linux(message) => write!(f, "Linux/X11 error: {message}"),
105 }
106 }
107}
108
109impl std::error::Error for Error {}
110
111#[derive(Debug, Clone, PartialEq, Hash)]
113pub enum Event {
114 ApplyToWindow {
115 id: window::Id,
116 settings: ChromeSettings,
117 },
118}
119
120pub fn apply<Message>(id: window::Id, settings: ChromeSettings) -> Task<Message>
122where
123 Message: Send + 'static,
124{
125 apply_result(id, settings).discard()
126}
127
128pub fn apply_result(id: window::Id, settings: ChromeSettings) -> Task<Result<()>> {
130 window::run(id, move |native| apply_native(native, &settings))
131}
132
133pub fn apply_to_latest<Message>(settings: ChromeSettings) -> Task<Message>
135where
136 Message: Send + 'static,
137{
138 apply_to_latest_result(settings).discard()
139}
140
141pub fn apply_to_latest_result(settings: ChromeSettings) -> Task<Result<()>> {
143 window::latest().then(move |id| match id {
144 Some(id) => apply_result(id, settings.clone()),
145 None => Task::done(Err(Error::NoWindowAvailable)),
146 })
147}
148
149pub fn subscription(settings: ChromeSettings) -> Subscription<Event> {
151 window::open_events().with(settings).map(map_open_event)
152}
153
154pub fn handle<Message>(event: Event) -> Task<Message>
156where
157 Message: Send + 'static,
158{
159 handle_result(event).discard()
160}
161
162pub fn handle_result(event: Event) -> Task<Result<()>> {
164 match event {
165 Event::ApplyToWindow { id, settings } => apply_result(id, settings),
166 }
167}
168
169#[cfg(target_os = "windows")]
171pub fn current_windows_capabilities() -> Option<WindowsCapabilities> {
172 windows::current_capabilities()
173}
174
175#[cfg(not(target_os = "windows"))]
177pub fn current_windows_capabilities() -> Option<WindowsCapabilities> {
178 None
179}
180
181#[cfg(target_os = "windows")]
182fn apply_native(window: &dyn iced::window::Window, settings: &ChromeSettings) -> Result<()> {
183 use raw_window_handle::RawWindowHandle;
184
185 let handle = window.window_handle().map_err(Error::WindowHandle)?;
186
187 match handle.as_raw() {
188 RawWindowHandle::Win32(handle) => windows::apply(handle, settings),
189 _ => Err(Error::UnsupportedWindowHandle("non-Win32")),
190 }
191}
192
193#[cfg(target_os = "macos")]
194fn apply_native(window: &dyn iced::window::Window, settings: &ChromeSettings) -> Result<()> {
195 use raw_window_handle::RawWindowHandle;
196
197 let handle = window.window_handle().map_err(Error::WindowHandle)?;
198
199 match handle.as_raw() {
200 RawWindowHandle::AppKit(handle) => macos::apply(handle, settings),
201 _ => Err(Error::UnsupportedWindowHandle("non-AppKit")),
202 }
203}
204
205#[cfg(target_os = "linux")]
206fn apply_native(window: &dyn iced::window::Window, settings: &ChromeSettings) -> Result<()> {
207 use raw_window_handle::{RawDisplayHandle, RawWindowHandle};
208
209 let window_handle = window.window_handle().map_err(Error::WindowHandle)?;
210 let display_handle = window.display_handle().map_err(Error::WindowHandle)?;
211
212 match (display_handle.as_raw(), window_handle.as_raw()) {
213 (RawDisplayHandle::Xlib(display), RawWindowHandle::Xlib(handle)) => {
214 linux::apply_xlib(display, handle, settings)
215 }
216 (RawDisplayHandle::Wayland(_), RawWindowHandle::Wayland(_)) => {
217 linux::apply_wayland(settings)
218 }
219 _ => Err(Error::UnsupportedWindowHandle("non-Xlib Linux")),
220 }
221}
222
223#[cfg(not(any(target_os = "windows", target_os = "macos", target_os = "linux")))]
224fn apply_native(_window: &dyn iced::window::Window, _settings: &ChromeSettings) -> Result<()> {
225 Ok(())
226}
227
228fn map_open_event((settings, id): (ChromeSettings, window::Id)) -> Event {
229 Event::ApplyToWindow { id, settings }
230}